summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.gitmodules3
-rw-r--r--base/Xresources33
-rw-r--r--base/bash_aliases2
-rw-r--r--base/bash_logout6
-rw-r--r--base/bashrc132
-rw-r--r--base/config/.nolink0
-rw-r--r--base/config/i3/.nolink0
-rw-r--r--base/config/i3/autostart.d/.nolink0
-rw-r--r--base/config/i3/autostart.d/10_xautolock2
-rw-r--r--base/config/i3/autostart.d/20_gpg-agent2
-rw-r--r--base/config/i3/config.d/.nolink0
-rw-r--r--base/config/i3/config.d/00_base_config194
-rw-r--r--base/config/nvim/init.vim49
-rw-r--r--base/config/nvim/rust.vim102
-rw-r--r--base/conkyrc19
-rw-r--r--base/conkyrc-json37
-rw-r--r--base/fonts.config34
-rw-r--r--base/gnupg/.nolink0
-rw-r--r--base/gnupg/gpg.conf10
-rw-r--r--base/local/.nolink0
-rw-r--r--base/local/share/.nolink0
-rw-r--r--base/local/share/applications/.nolink0
-rw-r--r--base/mailcap10
-rw-r--r--base/muttrc84
-rw-r--r--base/notmuch-config88
-rw-r--r--base/screenrc1
-rw-r--r--base/urlview1
-rw-r--r--base/vim/after/syntax/html.vim23
-rw-r--r--base/vim/autoload/pathogen.vim347
-rw-r--r--base/vim/bundle/.gitignore4
-rwxr-xr-xbase/vim/bundle/fetch_bundles.sh11
-rw-r--r--base/vim/bundle/javacomplete/autoload/Reflection.java670
-rw-r--r--base/vim/bundle/javacomplete/autoload/java_parser.vim3500
-rw-r--r--base/vim/bundle/javacomplete/autoload/javacomplete.vim2932
-rw-r--r--base/vim/bundle/javacomplete/doc/javacomplete.txt568
m---------base/vim/bundle/syntastic0
-rw-r--r--base/vim/doc/.gitignore1
-rw-r--r--base/vim/doc/git-vim.txt95
-rw-r--r--base/vim/ftdetect/markdown.vim6
-rw-r--r--base/vim/ftdetect/mediawiki.vim6
-rw-r--r--base/vim/ftplugin/html.vim1
-rw-r--r--base/vim/ftplugin/javascript.vim1
-rw-r--r--base/vim/ftplugin/mail.vim1
-rw-r--r--base/vim/ftplugin/markdown.vim21
-rw-r--r--base/vim/ftplugin/php.vim2
-rw-r--r--base/vim/ftplugin/xml.vim1
-rw-r--r--base/vim/plugin/git.vim372
-rw-r--r--base/vim/plugin/gnupg.vim1457
-rw-r--r--base/vim/plugin/scratch.vim134
-rw-r--r--base/vim/syntax/git-diff.vim8
-rw-r--r--base/vim/syntax/git-log.vim3
-rw-r--r--base/vim/syntax/git-status.vim18
-rw-r--r--base/vim/syntax/markdown.vim130
-rw-r--r--base/vim/syntax/mediawiki.vim293
-rw-r--r--base/vim/syntax/php.vim1
-rw-r--r--base/vimrc151
-rw-r--r--base/weatherrc6
-rw-r--r--base/xsession55
-rw-r--r--base/zshrc53
-rw-r--r--host-overrides/bismuth.jesterpm.net/config/.nolink0
-rw-r--r--host-overrides/bismuth.jesterpm.net/config/i3/.nolink0
-rw-r--r--host-overrides/bismuth.jesterpm.net/config/i3/autostart.d/.nolink0
-rw-r--r--host-overrides/bismuth.jesterpm.net/config/i3/autostart.d/10_xautolock2
-rw-r--r--host-overrides/bismuth.jesterpm.net/config/i3/config.d/.nolink0
-rw-r--r--host-overrides/bismuth.jesterpm.net/config/i3/config.d/50_bismuth.jesterpm.net7
-rw-r--r--host-overrides/cadmium.jesterpm.net/config/.nolink0
-rw-r--r--host-overrides/cadmium.jesterpm.net/config/grobi.conf11
-rw-r--r--host-overrides/cadmium.jesterpm.net/config/i3/.nolink0
-rw-r--r--host-overrides/cadmium.jesterpm.net/config/i3/autostart.d/.nolink0
-rw-r--r--host-overrides/cadmium.jesterpm.net/config/i3/config.d/.nolink0
-rw-r--r--host-overrides/cadmium.jesterpm.net/config/systemd/.nolink0
-rw-r--r--host-overrides/cadmium.jesterpm.net/config/systemd/user/.nolink0
-rw-r--r--host-overrides/cadmium.jesterpm.net/config/systemd/user/grobi.service11
-rw-r--r--host-overrides/jesterpm.net/config/.nolink0
-rw-r--r--host-overrides/jesterpm.net/config/i3/.nolink0
-rw-r--r--host-overrides/jesterpm.net/config/i3/autostart.d/.nolink0
-rw-r--r--host-overrides/jesterpm.net/config/i3/config.d/.nolink0
-rw-r--r--host-overrides/jesterpm.net/config/systemd/.nolink0
-rw-r--r--host-overrides/jesterpm.net/config/systemd/user/.nolink0
-rw-r--r--host-overrides/jesterpm.net/config/systemd/user/dyndns.service7
-rw-r--r--host-overrides/jesterpm.net/config/systemd/user/dyndns.timer10
-rw-r--r--host-overrides/jesterpm.net/config/systemd/user/mbsync.service7
-rw-r--r--host-overrides/jesterpm.net/config/systemd/user/mbsync.timer10
-rw-r--r--host-overrides/jesterpm.net/gitconfig14
-rw-r--r--host-overrides/jesterpm.net/host-specific/bash_aliases1
-rw-r--r--host-overrides/jesterpm.net/host-specific/bashrc10
-rw-r--r--host-overrides/jesterpm.net/host-specific/muttrc11
-rw-r--r--host-overrides/jesterpm.net/host-specific/shell_pathes2
-rw-r--r--host-overrides/jesterpm.net/host-specific/vim/ftplugin/java.vim3
-rw-r--r--host-overrides/jesterpm.net/host-specific/vimrc23
-rw-r--r--host-overrides/jesterpm.net/local/.nolink0
-rw-r--r--host-overrides/jesterpm.net/local/share/.nolink0
-rw-r--r--host-overrides/jesterpm.net/local/share/applications/.nolink0
-rwxr-xr-xhost-overrides/jesterpm.net/local/share/applications/gnome-terminal.desktop28
-rw-r--r--host-overrides/jesterpm.net/mbsyncrc58
-rw-r--r--host-overrides/jesterpm.net/msmtprc9
-rw-r--r--host-overrides/jesterpm.net/ssh/.nolink0
-rw-r--r--host-overrides/jesterpm.net/ssh/authorized_keys22
-rw-r--r--host-overrides/jesterpm.net/ssh/config3
100 files changed, 11910 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a0e76af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.netrwhist
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..34fa261
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "base/vim/bundle/syntastic"]
+ path = base/vim/bundle/syntastic
+ url = https://github.com/scrooloose/syntastic.git
diff --git a/base/Xresources b/base/Xresources
new file mode 100644
index 0000000..2a80b98
--- /dev/null
+++ b/base/Xresources
@@ -0,0 +1,33 @@
+utohint: 0
+Xft.antialias: 1
+Xft.hinting: true
+Xft.hintstyle: hintslight
+Xft.dpi: 96
+Xft.rgba: rgb
+Xft.lcdfilter: lcddefault
+
+*color4: royalblue
+*color12: #5895cf
+
+Rxvt.background: #1a1a1a
+Rxvt.foreground: grey
+Rxvt.scrollBar: false
+Rxvt.font: xft:Inconsolata:size=10:autohint=true
+Rxvt.fade: 5
+Rxvt.termName: xterm
+
+URxvt*urgentOnBell: true
+URxvt*transparent: true
+URxvt*shading: 20
+URxvt.iso14755: false
+URxvt.iso14755_52: false
+
+XTerm*background: rgb:1a/1a/1a
+XTerm*foreground: grey
+XTerm*scrollBar: false
+XTerm*faceName: xft:Inconsolata:size=7:autohint=true
+XTerm*bellIsUrgent: true
+XTerm*eightBitInput: false
+XTerm*metaSendsEscape: true
+xterm*termName: xterm-256color
+XTerm*locale: true
diff --git a/base/bash_aliases b/base/bash_aliases
new file mode 100644
index 0000000..a5c77f3
--- /dev/null
+++ b/base/bash_aliases
@@ -0,0 +1,2 @@
+alias g="gvim --remote-silent"
+alias vi=vim
diff --git a/base/bash_logout b/base/bash_logout
new file mode 100644
index 0000000..09df5b1
--- /dev/null
+++ b/base/bash_logout
@@ -0,0 +1,6 @@
+# ~/.bash_logout: executed by bash(1) when login shell exits.
+
+# when leaving the console clear the screen to increase privacy
+if [ "$SHLVL" = 1 ]; then
+ [ -x /usr/bin/clear_console ] && /usr/bin/clear_console -q
+fi
diff --git a/base/bashrc b/base/bashrc
new file mode 100644
index 0000000..36bc1c6
--- /dev/null
+++ b/base/bashrc
@@ -0,0 +1,132 @@
+# ~/.bashrc: executed by bash(1) for non-login shells.
+# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
+# for examples
+
+# If not running interactively, don't do anything
+[ -z "$PS1" ] && return
+
+# don't put duplicate lines in the history. See bash(1) for more options
+# ... or force ignoredups and ignorespace
+HISTCONTROL=ignoredups
+
+# append to the history file, don't overwrite it
+shopt -s histappend
+
+# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
+HISTSIZE=1000
+HISTFILESIZE=2000
+
+# check the window size after each command and, if necessary,
+# update the values of LINES and COLUMNS.
+shopt -s checkwinsize
+
+# make less more friendly for non-text input files, see lesspipe(1)
+[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"
+
+# set variable identifying the chroot you work in (used in the prompt below)
+if [ -z "$debian_chroot" ] && [ -r /etc/debian_chroot ]; then
+ debian_chroot=$(cat /etc/debian_chroot)
+fi
+
+# set a fancy prompt (non-color, unless we know we "want" color)
+case "$TERM" in
+ xterm-color) color_prompt=yes;;
+esac
+
+# uncomment for a colored prompt, if the terminal has the capability; turned
+# off by default to not distract the user: the focus in a terminal window
+# should be on the output of commands, not on the prompt
+#force_color_prompt=yes
+
+if [ -n "$force_color_prompt" ]; then
+ if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
+ # We have color support; assume it's compliant with Ecma-48
+ # (ISO/IEC-6429). (Lack of such support is extremely rare, and such
+ # a case would tend to support setf rather than setaf.)
+ color_prompt=yes
+ else
+ color_prompt=
+ fi
+fi
+
+GIT_PS1_SHOWDIRTYSTATE=1
+
+if [ "$color_prompt" = yes ]; then
+ PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
+else
+ PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w$(__git_ps1 " (%s)")\$ '
+fi
+unset color_prompt force_color_prompt
+
+# If this is an xterm set the title to user@host:dir
+case "$TERM" in
+xterm*|rxvt*)
+ PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
+ ;;
+*)
+ ;;
+esac
+
+# enable color support of ls and also add handy aliases
+if [ -x /usr/bin/dircolors ]; then
+ test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
+ alias ls='ls --color=auto'
+ #alias dir='dir --color=auto'
+ #alias vdir='vdir --color=auto'
+
+ alias grep='grep --color=auto'
+ alias fgrep='fgrep --color=auto'
+ alias egrep='egrep --color=auto'
+fi
+
+# some more ls aliases
+alias ll='ls -alF'
+alias la='ls -A'
+alias l='ls -CF'
+
+# Add an "alert" alias for long running commands. Use like so:
+# sleep 10; alert
+alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'
+
+# Alias definitions.
+# You may want to put all your additions into a separate file like
+# ~/.bash_aliases, instead of adding them here directly.
+# See /usr/share/doc/bash-doc/examples in the bash-doc package.
+
+if [ -f ~/.bash_aliases ]; then
+ . ~/.bash_aliases
+fi
+
+# enable programmable completion features (you don't need to enable
+# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
+# sources /etc/bash.bashrc).
+if [ -f /etc/bash_completion ] && ! shopt -oq posix; then
+ . /etc/bash_completion
+fi
+
+# Personal Settings
+export EDITOR=vim
+export MAILDIR=$HOME/.maildir/INBOX
+
+#export SSH_AUTH_SOCK=$HOME/.gnupg/S.gpg-agent.ssh
+export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
+
+#source /usr/local/etc/bash_completion.d/*
+
+if [ -f ~/.shell_pathes ]; then
+ source ~/.shell_pathes
+fi
+
+# Host Specific Settings
+if [ -f ~/.host-specific/bashrc ]; then
+ source ~/.host-specific/bashrc
+fi
+
+if [ -f ~/.host-specific/shell_pathes ]; then
+ source ~/.host-specific/shell_pathes
+fi
+
+if [ -f ~/.host-specific/bash_aliases ]; then
+ source ~/.host-specific/bash_aliases
+fi
+
diff --git a/base/config/.nolink b/base/config/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/base/config/.nolink
diff --git a/base/config/i3/.nolink b/base/config/i3/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/base/config/i3/.nolink
diff --git a/base/config/i3/autostart.d/.nolink b/base/config/i3/autostart.d/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/base/config/i3/autostart.d/.nolink
diff --git a/base/config/i3/autostart.d/10_xautolock b/base/config/i3/autostart.d/10_xautolock
new file mode 100644
index 0000000..b72d673
--- /dev/null
+++ b/base/config/i3/autostart.d/10_xautolock
@@ -0,0 +1,2 @@
+exec --no-startup-id xset s 180 120
+exec --no-startup-id xss-lock -n dim-screen.sh -l lock
diff --git a/base/config/i3/autostart.d/20_gpg-agent b/base/config/i3/autostart.d/20_gpg-agent
new file mode 100644
index 0000000..cec15e2
--- /dev/null
+++ b/base/config/i3/autostart.d/20_gpg-agent
@@ -0,0 +1,2 @@
+exec --no-startup-id gpg-agent --daemon --enable-ssh-support --write-env-file "${HOME}/.gpg-agent-info"
+
diff --git a/base/config/i3/config.d/.nolink b/base/config/i3/config.d/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/base/config/i3/config.d/.nolink
diff --git a/base/config/i3/config.d/00_base_config b/base/config/i3/config.d/00_base_config
new file mode 100644
index 0000000..c373851
--- /dev/null
+++ b/base/config/i3/config.d/00_base_config
@@ -0,0 +1,194 @@
+#
+# This is my base i3 config which is shared between all hosts.
+#
+
+set $mod Mod4
+floating_modifier $mod
+
+## Visual Settings
+font xft:Inconsolata 8
+hide_edge_borders both
+
+## Bindings to start programs
+bindsym $mod+space exec dmenu_run -l 20
+bindsym $mod+Return exec i3-sensible-terminal
+
+# kill focused window
+bindsym $mod+Shift+Q kill
+
+
+## Bindings to control windows
+
+### Focus Commands
+bindsym $mod+h focus left
+bindsym $mod+j focus down
+bindsym $mod+k focus up
+bindsym $mod+l focus right
+bindsym Mod1+Tab focus right
+bindsym Mod1+Shift+Tab focus left
+bindsym $mod+a focus parent
+bindsym $mod+d focus child
+
+### Window Moving Commands
+bindsym $mod+Shift+H move left
+bindsym $mod+Shift+J move down
+bindsym $mod+Shift+K move up
+bindsym $mod+Shift+L move right
+
+### Floating Windows
+bindsym $mod+Shift+space floating toggle
+bindsym $mod+Tab focus mode_toggle
+
+## Layout Commands
+bindsym $mod+s layout stacking
+bindsym $mod+w layout tabbed
+bindsym $mod+e layout default
+bindsym $mod+v split v
+bindsym $mod+Shift+V split h
+bindsym $mod+f fullscreen
+
+
+## Bindings to control workspaces
+
+### Switch Workspaces
+bindsym $mod+1 workspace number "1: mail"
+bindsym $mod+2 workspace number "2: web"
+bindsym $mod+3 workspace number "3: dev"
+bindsym $mod+4 workspace number 4
+bindsym $mod+5 workspace number 5
+bindsym $mod+6 workspace number 6
+bindsym $mod+7 workspace number 7
+bindsym $mod+8 workspace number 8
+bindsym $mod+9 workspace number 9
+bindsym $mod+0 workspace number 10
+bindsym $mod+Left workspace prev_on_output
+bindsym $mod+Right workspace next_on_output
+
+### Move container to workspace
+bindsym $mod+Shift+exclam move workspace number "1: mail"
+bindsym $mod+Shift+at move workspace number "2: web"
+bindsym $mod+Shift+numbersign move workspace number "3: dev"
+bindsym $mod+Shift+dollar move workspace number 4
+bindsym $mod+Shift+percent move workspace number 5
+bindsym $mod+Shift+asciicircum move workspace number 6
+bindsym $mod+Shift+ampersand move workspace number 7
+bindsym $mod+Shift+asterisk move workspace number 8
+bindsym $mod+Shift+parenleft move workspace number 9
+bindsym $mod+Shift+parenright move workspace number 10
+bindsym $mod+Shift+Left move workspace prev
+bindsym $mod+Shift+Right move workspace next
+
+### Move workspace to different monitor
+bindsym $mod+Mod1+Left move workspace to output left
+bindsym $mod+Mod1+Right move workspace to output right
+bindsym $mod+Mod1+Up move workspace to output up
+bindsym $mod+Mod1+Down move workspace to output down
+
+workspace_auto_back_and_forth yes
+workspace_layout default
+
+### Workspace monitor assignments
+
+workspace "1: mail" output eDP-1
+workspace "2: web" output DP-1
+workspace "3: dev" output DP-1
+workspace 4 output DP-1
+workspace 5 output DP-1
+workspace 6 output DP-1
+workspace 7 output DP-1
+workspace 8 output DP-1
+workspace 9 output DP-1
+workspace 10 output DP-1
+
+### Scratchpad
+bindsym $mod+Shift+minus move scratchpad
+bindsym $mod+minus scratchpad show
+
+
+## Bindings to contorl the session
+bindsym $mod+Shift+C exec "make_i3_config > $HOME/.config/i3/config"; reload
+bindsym $mod+Shift+R restart
+bindsym $mod+Shift+E exit
+bindsym Control+mod1+l exec $HOME/bin/lock
+
+
+## Alternative modes
+
+### Resize Mode
+bindsym $mod+r mode "resize"
+mode "resize" {
+ bindsym h resize shrink left 10 px or 10 ppt
+ bindsym Shift+H resize grow left 10 px or 10 ppt
+ bindsym j resize shrink down 10 px or 10 ppt
+ bindsym Shift+j resize grow down 10 px or 10 ppt
+ bindsym k resize shrink up 10 px or 10 ppt
+ bindsym Shift+K resize grow up 10 px or 10 ppt
+ bindsym l resize shrink right 10 px or 10 ppt
+ bindsym Shift+L resize grow right 10 px or 10 ppt
+
+ bindsym Return mode "default"
+ bindsym Escape mode "default"
+}
+
+### Display Settings
+set $displayMode "Choose [l]aptop or [d]esktop, [e]xtend, [m]irror"
+bindsym $mod+p mode $displayMode
+mode $displayMode {
+ bindsym l exec "\
+ xrandr --output eDP-1 --mode 1920x1080 --primary && \
+ xrandr --output DP-1 --off"; mode "default"
+ bindsym d exec "\
+ xrandr --output DP-1 --auto --primary && \
+ xrandr --output eDP-1 --off"; mode "default"
+ bindsym e exec "\
+ xrandr --output eDP-1 --mode 1920x1080 --primary && \
+ xrandr --output DP-1 --above eDP-1 --mode 1920x1080 \
+ "; mode "default"
+ bindsym m exec "\
+ xrandr --output eDP-1 --mode 1920x1080 --primary && \
+ xrandr --output DP-1 --same-as eDP-1 --auto \
+ "; mode "default"
+ bindsym Return mode "default"
+ bindsym Escape mode "default"
+}
+
+## i3bar Settings
+bar {
+ status_command conky-i3bar
+ position bottom
+ tray_output primary
+ font xft:Inconsolata 9
+}
+
+
+## Misc. Bindings
+
+## sshclip
+bindsym $mod+g exec sshclip
+
+bindsym $mod+c exec clipmv
+
+### Music
+bindsym XF86AudioPlay exec xmms2 toggle
+bindsym $mod+End exec xmms2 toggle
+bindsym XF86AudioNext exec xmms2 next
+bindsym $mod+Home exec xmms2 next
+bindsym XF86AudioPrev exec xmms2 prev
+
+### Volume
+bindsym XF86AudioRaiseVolume exec "amixer -D pulse -q set Master 5%+ unmute; amixer -D pulse -q set Speaker unmute"
+bindsym $mod+Prior exec "amixer -D pulse -q set Master 5%+ unmute; amixer -D pulse -q set Speaker unmute"
+bindsym XF86AudioLowerVolume exec "amixer -D pulse -q set Master 5%- unmute; amixer -D pulse -q set Speaker unmute"
+bindsym $mod+Next exec "amixer -D pulse -q set Master 5%- unmute; amixer -D pulse -q set Speaker unmute"
+bindsym XF86AudioMute exec amixer -D pulse -q set Master mute
+
+### Brightness
+bindsym XF86MonBrightnessUp exec "brightnessctl set +5%"
+bindsym XF86MonBrightnessDown exec "brightnessctl set 5%-"
+
+## Specific Window Settings
+for_window [class="Gcalctool"] floating enable
+for_window [class="Gnome-calculator"] floating enable
+for_window [class="file_progress"] floating enable
+for_window [title="Unlock Database - KeePassXC"] floating enable
+
diff --git a/base/config/nvim/init.vim b/base/config/nvim/init.vim
new file mode 100644
index 0000000..18324af
--- /dev/null
+++ b/base/config/nvim/init.vim
@@ -0,0 +1,49 @@
+set runtimepath^=~/.vim runtimepath+=~/.vim/after
+let &packpath = &runtimepath
+source ~/.vimrc
+
+call plug#begin('~/.vim/plugged')
+
+" Collection of common configurations for the Nvim LSP client
+Plug 'neovim/nvim-lspconfig'
+
+" Completion framework
+Plug 'hrsh7th/nvim-cmp'
+
+" LSP completion source for nvim-cmp
+Plug 'hrsh7th/cmp-nvim-lsp'
+
+" Snippet completion source for nvim-cmp
+Plug 'hrsh7th/cmp-vsnip'
+
+" Other usefull completion sources
+Plug 'hrsh7th/cmp-path'
+Plug 'hrsh7th/cmp-buffer'
+
+" See hrsh7th's other plugins for more completion sources!
+
+" To enable more of the features of rust-analyzer, such as inlay hints and more!
+Plug 'simrat39/rust-tools.nvim'
+
+" Snippet engine
+Plug 'hrsh7th/vim-vsnip'
+
+" Fuzzy finder
+" Optional
+Plug 'nvim-lua/popup.nvim'
+Plug 'nvim-lua/plenary.nvim'
+Plug 'nvim-telescope/telescope.nvim'
+
+" Color Scheme
+Plug 'arcticicestudio/nord-vim'
+
+Plug 'vim-airline/vim-airline'
+Plug 'airblade/vim-gitgutter'
+Plug 'tpope/vim-fugitive'
+
+call plug#end()
+
+colorscheme nord
+
+source ~/.config/nvim/rust.vim
+
diff --git a/base/config/nvim/rust.vim b/base/config/nvim/rust.vim
new file mode 100644
index 0000000..81ce516
--- /dev/null
+++ b/base/config/nvim/rust.vim
@@ -0,0 +1,102 @@
+" Set completeopt to have a better completion experience
+" :help completeopt
+" menuone: popup even when there's only one match
+" noinsert: Do not insert text until a selection is made
+" noselect: Do not select, force user to select one from the menu
+set completeopt=menuone,noinsert,noselect
+
+" Avoid showing extra messages when using completion
+set shortmess+=c
+
+" Configure LSP through rust-tools.nvim plugin.
+" rust-tools will configure and enable certain LSP features for us.
+" See https://github.com/simrat39/rust-tools.nvim#configuration
+lua <<EOF
+local nvim_lsp = require'lspconfig'
+
+local opts = {
+ tools = { -- rust-tools options
+ autoSetHints = true,
+ hover_with_actions = true,
+ inlay_hints = {
+ show_parameter_hints = false,
+ parameter_hints_prefix = "",
+ other_hints_prefix = "",
+ },
+ },
+
+ -- all the opts to send to nvim-lspconfig
+ -- these override the defaults set by rust-tools.nvim
+ -- see https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#rust_analyzer
+ server = {
+ -- on_attach is a callback called when the language server attachs to the buffer
+ -- on_attach = on_attach,
+ settings = {
+ -- to enable rust-analyzer settings visit:
+ -- https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/generated_config.adoc
+ ["rust-analyzer"] = {
+ -- enable clippy on save
+ checkOnSave = {
+ command = "clippy"
+ },
+ }
+ }
+ },
+}
+
+require('rust-tools').setup(opts)
+EOF
+
+" Setup Completion
+" See https://github.com/hrsh7th/nvim-cmp#basic-configuration
+lua <<EOF
+local cmp = require'cmp'
+cmp.setup({
+ -- Enable LSP snippets
+ snippet = {
+ expand = function(args)
+ vim.fn["vsnip#anonymous"](args.body)
+ end,
+ },
+ mapping = {
+ ['<C-p>'] = cmp.mapping.select_prev_item(),
+ ['<C-n>'] = cmp.mapping.select_next_item(),
+ -- Add tab support
+ ['<S-Tab>'] = cmp.mapping.select_prev_item(),
+ ['<Tab>'] = cmp.mapping.select_next_item(),
+ ['<C-d>'] = cmp.mapping.scroll_docs(-4),
+ ['<C-f>'] = cmp.mapping.scroll_docs(4),
+ ['<C-Space>'] = cmp.mapping.complete(),
+ ['<C-e>'] = cmp.mapping.close(),
+ ['<CR>'] = cmp.mapping.confirm({
+ behavior = cmp.ConfirmBehavior.Insert,
+ select = true,
+ })
+ },
+
+ -- Installed sources
+ sources = {
+ { name = 'nvim_lsp' },
+ { name = 'vsnip' },
+ { name = 'path' },
+ { name = 'buffer' },
+ },
+})
+EOF
+
+" Code navigation shortcuts
+nnoremap <silent> <c-]> <cmd>lua vim.lsp.buf.definition()<CR>
+nnoremap <silent> K <cmd>lua vim.lsp.buf.hover()<CR>
+nnoremap <silent> gD <cmd>lua vim.lsp.buf.implementation()<CR>
+nnoremap <silent> <c-k> <cmd>lua vim.lsp.buf.signature_help()<CR>
+nnoremap <silent> 1gD <cmd>lua vim.lsp.buf.type_definition()<CR>
+nnoremap <silent> gr <cmd>lua vim.lsp.buf.references()<CR>
+nnoremap <silent> g0 <cmd>lua vim.lsp.buf.document_symbol()<CR>
+nnoremap <silent> gW <cmd>lua vim.lsp.buf.workspace_symbol()<CR>
+nnoremap <silent> gd <cmd>lua vim.lsp.buf.definition()<CR>
+
+nnoremap <silent> ga <cmd>lua vim.lsp.buf.code_action()<CR>
+
+" Goto previous/next diagnostic warning/error
+nnoremap <silent> g[ <cmd>lua vim.diagnostic.goto_prev()<CR>
+nnoremap <silent> g] <cmd>lua vim.diagnostic.goto_next()<CR>
diff --git a/base/conkyrc b/base/conkyrc
new file mode 100644
index 0000000..035e4ab
--- /dev/null
+++ b/base/conkyrc
@@ -0,0 +1,19 @@
+out_to_x no
+own_window no
+out_to_console yes
+background no
+max_text_width 0
+use_spacer left
+
+update_interval 1.0
+cpu_avg_samples 2
+net_avg_samples 2
+
+TEXT
+✉ ${new_mails $HOME/.maildir/INBOX/}/${mails $HOME/.maildir/INBOX} | \
+/home ${fs_free /home} | \
+RAM ${memperc}% | \
+BATT ${battery BAT0} | \
+${loadavg 1} | \
+♪ ${exec pactl list sinks | awk '/Volume: 0:/ {print substr($3, 1, index($3, "%") - 2)}'|head -n 1} | \
+${time %a %d %b %Y} ${time %H:%M:%S}
diff --git a/base/conkyrc-json b/base/conkyrc-json
new file mode 100644
index 0000000..3a143ba
--- /dev/null
+++ b/base/conkyrc-json
@@ -0,0 +1,37 @@
+out_to_x no
+own_window no
+out_to_console yes
+background no
+max_text_width 0
+use_spacer left
+if_up_strictness address
+
+update_interval 2.0
+cpu_avg_samples 2
+net_avg_samples 2
+
+TEXT
+[
+
+{ "full_text": "${addr wlp58s0}",
+ "color": ${if_up (wlp58s0)}"\#ff0000"${else}"\#00ff00"${endif} },
+
+{ "full_text": "✉ ${new_mails $HOME/.maildir/INBOX/}/${mails $HOME/.maildir/INBOX}",
+ "color": ${if_match ${new_mails $HOME/.maildir/INBOX/}>0}"\#00ff00"${else}"\#ffffff"${endif} },
+
+{ "full_text":"/home ${fs_free /home}",
+ "color": ${if_match ${fs_free_perc /home}<5}"\#ff0000"${else}"\#ffffff"${endif} },
+
+{ "full_text":"RAM ${memperc}%",
+ "color":${if_match ${memperc}<90}"\#ffffff"${else}"\#ff0000"${endif} },
+
+{ "full_text":"🔋 ${battery BAT0} (${exec ~/bin/discharge_rate.sh})" },
+
+{ "full_text":"${loadavg 1}" },
+
+{ "full_text":"♪ ${exec amixer get Master | awk '/Mono/ {print substr($4, 2, index($4, "%") - 1)}'|tail -n 1}" },
+
+{ "full_text":"${time %a %d %b %Y} ${time %H:%M:%S} ${utime %H:%M}Z",
+ "short_text":"${time %H:%M:%S}" }
+
+],
diff --git a/base/fonts.config b/base/fonts.config
new file mode 100644
index 0000000..3e39a3a
--- /dev/null
+++ b/base/fonts.config
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
+<fontconfig>
+ <match target="font">
+ <edit mode="assign" name="hinting" >
+ <bool>true</bool>
+ </edit>
+ </match>
+ <match target="font" >
+ <edit mode="assign" name="autohint" >
+ <bool>true</bool>
+ </edit>
+ </match>
+ <match target="font">
+ <edit mode="assign" name="hintstyle" >
+ <const>hintslight</const>
+ </edit>
+ </match>
+ <match target="font">
+ <edit mode="assign" name="rgba" >
+ <const>rgb</const>
+ </edit>
+ </match>
+ <match target="font">
+ <edit mode="assign" name="antialias" >
+ <bool>true</bool>
+ </edit>
+ </match>
+ <match target="font">
+ <edit mode="assign" name="lcdfilter">
+ <const>lcddefault</const>
+ </edit>
+ </match>
+</fontconfig>
diff --git a/base/gnupg/.nolink b/base/gnupg/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/base/gnupg/.nolink
diff --git a/base/gnupg/gpg.conf b/base/gnupg/gpg.conf
new file mode 100644
index 0000000..e8c1cdd
--- /dev/null
+++ b/base/gnupg/gpg.conf
@@ -0,0 +1,10 @@
+default-key 08311F3C
+
+keyserver pool.sks-keyservers.net
+keyserver-options auto-key-retrieve
+
+personal-digest-preferences SHA256
+cert-digest-algo SHA256
+default-preference-list SHA512 SHA384 SHA256 SHA224 AES256 AES192 AES CAST5 ZLIB BZIP2 ZIP Uncompressed
+
+use-agent
diff --git a/base/local/.nolink b/base/local/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/base/local/.nolink
diff --git a/base/local/share/.nolink b/base/local/share/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/base/local/share/.nolink
diff --git a/base/local/share/applications/.nolink b/base/local/share/applications/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/base/local/share/applications/.nolink
diff --git a/base/mailcap b/base/mailcap
new file mode 100644
index 0000000..f95fa25
--- /dev/null
+++ b/base/mailcap
@@ -0,0 +1,10 @@
+text/calendar; mutt-ical.py -i -e "jesse@jesterpm.net" %s
+application/ics; mutt-ical.py -i -e "jesse@jesterpm.net" %s
+
+text/html; w3m -I %{charset} -T text/html -dump; copiousoutput; description=HTML; compose=vim %s
+
+application/pdf; evince %s; test=test -n "$DISPLAY"
+application/x-bzpdf; evince %s; test=test -n "$DISPLAY"
+application/x-ext-pdf; evince %s; test=test -n "$DISPLAY"
+application/x-gzpdf; evince %s; test=test -n "$DISPLAY"
+application/x-xzpdf; evince %s; test=test -n "$DISPLAY"
diff --git a/base/muttrc b/base/muttrc
new file mode 100644
index 0000000..ba0d8d6
--- /dev/null
+++ b/base/muttrc
@@ -0,0 +1,84 @@
+#
+# Mutt settings common everywhere
+#
+
+# Mailbox Settings
+set mbox_type="Maildir"
+set folder="~/.maildir"
+
+# Mutt settings
+set header_cache=~/.maildir/mutt_cache/
+set delete=yes
+unset confirmappend
+set sort=threads
+set sort_aux = last-date-received
+set pager_stop=yes
+set mark_old = no
+unset markers
+set text_flowed=yes
+set display_filter="~/bin/mutt-display-filter.sh"
+set edit_headers=yes
+auto_view text/html
+alternative_order text/plain text/enriched text/html
+
+# Headers
+ignore *
+unignore date from: to: cc subject X-Spam-Status
+set forward_format="Fwd: %s"
+
+# Macros
+macro pager <Up> "<previous-line>" "Previous line"
+macro pager <Down> "<next-line>" "Next line"
+
+bind index,pager g noop
+bind index gg first-entry
+bind pager gg top
+bind index G last-entry
+bind pager G bottom
+bind index,pager R group-reply
+macro index,pager c "<change-folder>?<toggle-mailboxes>" "open a different folder"
+macro index C "<copy-message>?<toggle-mailboxes>" "copy a message to a mailbox"
+macro index M "<save-message>?<toggle-mailboxes>" "move a message to a mailbox"
+# For gmail, don't move the message to archive, just remove it from the inbox.
+#macro index e "<save-message>+archive<enter>" "Archive a message."
+#macro pager e "<save-message>+archive<enter>" "Archive a message."
+macro index,pager e "<delete-message>" "Archive a message."
+macro index,pager S "<save-message>+spam<enter>" "Report message as spam."
+macro index,pager d "<save-message>+trash<enter>" "Move to trash"
+macro pager \Cu "|urlview<enter>" "view links in urlview"
+
+macro index \es "<enter-command>unset wait_key<enter><shell-escape>notmuch-mutt --prompt search<enter><change-folder-readonly>~/.cache/notmuch/mutt/results/<enter>" "Search All Mail"
+ macro index <F9> \
+ "<enter-command>set my_old_pipe_decode=\$pipe_decode my_old_wait_key=\$wait_key nopipe_decode nowait_key<enter>\
+ <pipe-message>notmuch-mutt -r thread<enter>\
+ <change-folder-readonly>`echo ${XDG_CACHE_HOME:-$HOME/.cache}/notmuch/mutt/results`<enter>\
+ <enter-command>set pipe_decode=\$my_old_pipe_decode wait_key=\$my_old_wait_key<enter>" \
+ "notmuch: reconstruct thread"
+
+# Contacts
+set query_command= "khard email --parsable '%s'"
+bind editor <Tab> complete-query
+bind editor ^T complete
+#add email addresses to khard's address book
+macro index,pager A "<pipe-message>khard add-email<return>" "add the sender email address to khard"
+
+# Colors
+#color index brightwhite default ~N
+#color index brightwhite default ~O
+#
+## colors
+#color normal white black
+#color attachment brightyellow black
+#color hdrdefault white black
+#color indicator black white
+#color markers brightred black
+#color quoted green black
+#color signature cyan black
+#color status black white
+#color tilde blue black
+#color tree red black
+
+# Grab any host-specific settings
+source ~/.host-specific/muttrc
+
+source ~/.mutt/aliases
diff --git a/base/notmuch-config b/base/notmuch-config
new file mode 100644
index 0000000..b61b264
--- /dev/null
+++ b/base/notmuch-config
@@ -0,0 +1,88 @@
+# .notmuch-config - Configuration file for the notmuch mail system
+#
+# For more information about notmuch, see https://notmuchmail.org
+
+# Database configuration
+#
+# The only value supported here is 'path' which should be the top-level
+# directory where your mail currently exists and to where mail will be
+# delivered in the future. Files should be individual email messages.
+# Notmuch will store its database within a sub-directory of the path
+# configured here named ".notmuch".
+#
+[database]
+path=/home/jesterpm/.maildir
+
+# User configuration
+#
+# Here is where you can let notmuch know how you would like to be
+# addressed. Valid settings are
+#
+# name Your full name.
+# primary_email Your primary email address.
+# other_email A list (separated by ';') of other email addresses
+# at which you receive email.
+#
+# Notmuch will use the various email addresses configured here when
+# formatting replies. It will avoid including your own addresses in the
+# recipient list of replies, and will set the From address based on the
+# address to which the original email was addressed.
+#
+[user]
+name=Jesse Morgan
+primary_email=jesse@jesterpm.net
+other_email=jesterpm@u.washington.edu;
+
+# Configuration for "notmuch new"
+#
+# The following options are supported here:
+#
+# tags A list (separated by ';') of the tags that will be
+# added to all messages incorporated by "notmuch new".
+#
+# ignore A list (separated by ';') of file and directory names
+# that will not be searched for messages by "notmuch new".
+#
+# NOTE: *Every* file/directory that goes by one of those
+# names will be ignored, independent of its depth/location
+# in the mail store.
+#
+[new]
+tags=unread;inbox;
+ignore=
+
+# Search configuration
+#
+# The following option is supported here:
+#
+# exclude_tags
+# A ;-separated list of tags that will be excluded from
+# search results by default. Using an excluded tag in a
+# query will override that exclusion.
+#
+[search]
+exclude_tags=deleted;spam;sent
+
+# Maildir compatibility configuration
+#
+# The following option is supported here:
+#
+# synchronize_flags Valid values are true and false.
+#
+# If true, then the following maildir flags (in message filenames)
+# will be synchronized with the corresponding notmuch tags:
+#
+# Flag Tag
+# ---- -------
+# D draft
+# F flagged
+# P passed
+# R replied
+# S unread (added when 'S' flag is not present)
+#
+# The "notmuch new" command will notice flag changes in filenames
+# and update tags, while the "notmuch tag" and "notmuch restore"
+# commands will notice tag changes and update flags in filenames
+#
+[maildir]
+synchronize_flags=true
diff --git a/base/screenrc b/base/screenrc
new file mode 100644
index 0000000..c958003
--- /dev/null
+++ b/base/screenrc
@@ -0,0 +1 @@
+vbell off
diff --git a/base/urlview b/base/urlview
new file mode 100644
index 0000000..14ee137
--- /dev/null
+++ b/base/urlview
@@ -0,0 +1 @@
+COMMAND firefox
diff --git a/base/vim/after/syntax/html.vim b/base/vim/after/syntax/html.vim
new file mode 100644
index 0000000..20740b2
--- /dev/null
+++ b/base/vim/after/syntax/html.vim
@@ -0,0 +1,23 @@
+" Vim syntax file
+" Language: HTML (version 5)
+" Maintainer: Rodrigo Machado <rcmachado@gmail.com>
+" URL: http://rm.blog.br/vim/syntax/html.vim
+" Last Change: 2009 Aug 19
+" License: Public domain
+" (but let me know if you liked it :) )
+"
+" Note: This file just adds the new tags from HTML 5
+" and don't replace default html.vim syntax file
+
+" HTML 5 tags
+syn keyword htmlTagName contained article aside audio bb canvas command datagrid
+syn keyword htmlTagName contained datalist details dialog embed figure footer
+syn keyword htmlTagName contained header hgroup keygen mark meter nav output
+syn keyword htmlTagName contained progress time ruby rt rp section time video
+
+" HTML 5 arguments
+syn keyword htmlArg contained autofocus placeholder min max step
+syn keyword htmlArg contained contenteditable contextmenu draggable hidden item
+syn keyword htmlArg contained itemprop list subject spellcheck
+" this doesn't work because default syntax file alredy define a 'data' attribute
+syn match htmlArg "\<\(data-[\-a-zA-Z0-9_]\+\)=" contained
diff --git a/base/vim/autoload/pathogen.vim b/base/vim/autoload/pathogen.vim
new file mode 100644
index 0000000..a13ae08
--- /dev/null
+++ b/base/vim/autoload/pathogen.vim
@@ -0,0 +1,347 @@
+" pathogen.vim - path option manipulation
+" Maintainer: Tim Pope <http://tpo.pe/>
+" Version: 2.3
+
+" Install in ~/.vim/autoload (or ~\vimfiles\autoload).
+"
+" For management of individually installed plugins in ~/.vim/bundle (or
+" ~\vimfiles\bundle), adding `execute pathogen#infect()` to the top of your
+" .vimrc is the only other setup necessary.
+"
+" The API is documented inline below.
+
+if exists("g:loaded_pathogen") || &cp
+ finish
+endif
+let g:loaded_pathogen = 1
+
+" Point of entry for basic default usage. Give a relative path to invoke
+" pathogen#interpose() (defaults to "bundle/{}"), or an absolute path to invoke
+" pathogen#surround(). Curly braces are expanded with pathogen#expand():
+" "bundle/{}" finds all subdirectories inside "bundle" inside all directories
+" in the runtime path.
+function! pathogen#infect(...) abort
+ for path in a:0 ? filter(reverse(copy(a:000)), 'type(v:val) == type("")') : ['bundle/{}']
+ if path =~# '^\%({\=[$~\\/]\|{\=\w:[\\/]\).*[{}*]'
+ call pathogen#surround(path)
+ elseif path =~# '^\%([$~\\/]\|\w:[\\/]\)'
+ call s:warn('Change pathogen#infect('.string(path).') to pathogen#infect('.string(path.'/{}').')')
+ call pathogen#surround(path . '/{}')
+ elseif path =~# '[{}*]'
+ call pathogen#interpose(path)
+ else
+ call s:warn('Change pathogen#infect('.string(path).') to pathogen#infect('.string(path.'/{}').')')
+ call pathogen#interpose(path . '/{}')
+ endif
+ endfor
+ call pathogen#cycle_filetype()
+ if pathogen#is_disabled($MYVIMRC)
+ return 'finish'
+ endif
+ return ''
+endfunction
+
+" Split a path into a list.
+function! pathogen#split(path) abort
+ if type(a:path) == type([]) | return a:path | endif
+ if empty(a:path) | return [] | endif
+ let split = split(a:path,'\\\@<!\%(\\\\\)*\zs,')
+ return map(split,'substitute(v:val,''\\\([\\,]\)'',''\1'',"g")')
+endfunction
+
+" Convert a list to a path.
+function! pathogen#join(...) abort
+ if type(a:1) == type(1) && a:1
+ let i = 1
+ let space = ' '
+ else
+ let i = 0
+ let space = ''
+ endif
+ let path = ""
+ while i < a:0
+ if type(a:000[i]) == type([])
+ let list = a:000[i]
+ let j = 0
+ while j < len(list)
+ let escaped = substitute(list[j],'[,'.space.']\|\\[\,'.space.']\@=','\\&','g')
+ let path .= ',' . escaped
+ let j += 1
+ endwhile
+ else
+ let path .= "," . a:000[i]
+ endif
+ let i += 1
+ endwhile
+ return substitute(path,'^,','','')
+endfunction
+
+" Convert a list to a path with escaped spaces for 'path', 'tag', etc.
+function! pathogen#legacyjoin(...) abort
+ return call('pathogen#join',[1] + a:000)
+endfunction
+
+" Turn filetype detection off and back on again if it was already enabled.
+function! pathogen#cycle_filetype() abort
+ if exists('g:did_load_filetypes')
+ filetype off
+ filetype on
+ endif
+endfunction
+
+" Check if a bundle is disabled. A bundle is considered disabled if its
+" basename or full name is included in the list g:pathogen_disabled.
+function! pathogen#is_disabled(path) abort
+ if a:path =~# '\~$'
+ return 1
+ endif
+ let sep = pathogen#slash()
+ let blacklist = map(
+ \ get(g:, 'pathogen_blacklist', get(g:, 'pathogen_disabled', [])) +
+ \ pathogen#split($VIMBLACKLIST),
+ \ 'substitute(v:val, "[\\/]$", "", "")')
+ return index(blacklist, fnamemodify(a:path, ':t')) != -1 || index(blacklist, a:path) != -1
+endfunction "}}}1
+
+" Prepend the given directory to the runtime path and append its corresponding
+" after directory. Curly braces are expanded with pathogen#expand().
+function! pathogen#surround(path) abort
+ let sep = pathogen#slash()
+ let rtp = pathogen#split(&rtp)
+ let path = fnamemodify(a:path, ':p:?[\\/]\=$??')
+ let before = filter(pathogen#expand(path), '!pathogen#is_disabled(v:val)')
+ let after = filter(reverse(pathogen#expand(path.sep.'after')), '!pathogen#is_disabled(v:val[0:-7])')
+ call filter(rtp, 'index(before + after, v:val) == -1')
+ let &rtp = pathogen#join(before, rtp, after)
+ return &rtp
+endfunction
+
+" For each directory in the runtime path, add a second entry with the given
+" argument appended. Curly braces are expanded with pathogen#expand().
+function! pathogen#interpose(name) abort
+ let sep = pathogen#slash()
+ let name = a:name
+ if has_key(s:done_bundles, name)
+ return ""
+ endif
+ let s:done_bundles[name] = 1
+ let list = []
+ for dir in pathogen#split(&rtp)
+ if dir =~# '\<after$'
+ let list += reverse(filter(pathogen#expand(dir[0:-6].name.sep.'after'), '!pathogen#is_disabled(v:val[0:-7])')) + [dir]
+ else
+ let list += [dir] + filter(pathogen#expand(dir.sep.name), '!pathogen#is_disabled(v:val)')
+ endif
+ endfor
+ let &rtp = pathogen#join(pathogen#uniq(list))
+ return 1
+endfunction
+
+let s:done_bundles = {}
+
+" Invoke :helptags on all non-$VIM doc directories in runtimepath.
+function! pathogen#helptags() abort
+ let sep = pathogen#slash()
+ for glob in pathogen#split(&rtp)
+ for dir in map(split(glob(glob), "\n"), 'v:val.sep."/doc/".sep')
+ if (dir)[0 : strlen($VIMRUNTIME)] !=# $VIMRUNTIME.sep && filewritable(dir) == 2 && !empty(split(glob(dir.'*.txt'))) && (!filereadable(dir.'tags') || filewritable(dir.'tags'))
+ silent! execute 'helptags' pathogen#fnameescape(dir)
+ endif
+ endfor
+ endfor
+endfunction
+
+command! -bar Helptags :call pathogen#helptags()
+
+" Execute the given command. This is basically a backdoor for --remote-expr.
+function! pathogen#execute(...) abort
+ for command in a:000
+ execute command
+ endfor
+ return ''
+endfunction
+
+" Section: Unofficial
+
+function! pathogen#is_absolute(path) abort
+ return a:path =~# (has('win32') ? '^\%([\\/]\|\w:\)[\\/]\|^[~$]' : '^[/~$]')
+endfunction
+
+" Given a string, returns all possible permutations of comma delimited braced
+" alternatives of that string. pathogen#expand('/{a,b}/{c,d}') yields
+" ['/a/c', '/a/d', '/b/c', '/b/d']. Empty braces are treated as a wildcard
+" and globbed. Actual globs are preserved.
+function! pathogen#expand(pattern) abort
+ if a:pattern =~# '{[^{}]\+}'
+ let [pre, pat, post] = split(substitute(a:pattern, '\(.\{-\}\){\([^{}]\+\)}\(.*\)', "\\1\001\\2\001\\3", ''), "\001", 1)
+ let found = map(split(pat, ',', 1), 'pre.v:val.post')
+ let results = []
+ for pattern in found
+ call extend(results, pathogen#expand(pattern))
+ endfor
+ return results
+ elseif a:pattern =~# '{}'
+ let pat = matchstr(a:pattern, '^.*{}[^*]*\%($\|[\\/]\)')
+ let post = a:pattern[strlen(pat) : -1]
+ return map(split(glob(substitute(pat, '{}', '*', 'g')), "\n"), 'v:val.post')
+ else
+ return [a:pattern]
+ endif
+endfunction
+
+" \ on Windows unless shellslash is set, / everywhere else.
+function! pathogen#slash() abort
+ return !exists("+shellslash") || &shellslash ? '/' : '\'
+endfunction
+
+function! pathogen#separator() abort
+ return pathogen#slash()
+endfunction
+
+" Convenience wrapper around glob() which returns a list.
+function! pathogen#glob(pattern) abort
+ let files = split(glob(a:pattern),"\n")
+ return map(files,'substitute(v:val,"[".pathogen#slash()."/]$","","")')
+endfunction "}}}1
+
+" Like pathogen#glob(), only limit the results to directories.
+function! pathogen#glob_directories(pattern) abort
+ return filter(pathogen#glob(a:pattern),'isdirectory(v:val)')
+endfunction "}}}1
+
+" Remove duplicates from a list.
+function! pathogen#uniq(list) abort
+ let i = 0
+ let seen = {}
+ while i < len(a:list)
+ if (a:list[i] ==# '' && exists('empty')) || has_key(seen,a:list[i])
+ call remove(a:list,i)
+ elseif a:list[i] ==# ''
+ let i += 1
+ let empty = 1
+ else
+ let seen[a:list[i]] = 1
+ let i += 1
+ endif
+ endwhile
+ return a:list
+endfunction
+
+" Backport of fnameescape().
+function! pathogen#fnameescape(string) abort
+ if exists('*fnameescape')
+ return fnameescape(a:string)
+ elseif a:string ==# '-'
+ return '\-'
+ else
+ return substitute(escape(a:string," \t\n*?[{`$\\%#'\"|!<"),'^[+>]','\\&','')
+ endif
+endfunction
+
+" Like findfile(), but hardcoded to use the runtimepath.
+function! pathogen#runtime_findfile(file,count) abort "{{{1
+ let rtp = pathogen#join(1,pathogen#split(&rtp))
+ let file = findfile(a:file,rtp,a:count)
+ if file ==# ''
+ return ''
+ else
+ return fnamemodify(file,':p')
+ endif
+endfunction
+
+" Section: Deprecated
+
+function! s:warn(msg) abort
+ echohl WarningMsg
+ echomsg a:msg
+ echohl NONE
+endfunction
+
+" Prepend all subdirectories of path to the rtp, and append all 'after'
+" directories in those subdirectories. Deprecated.
+function! pathogen#runtime_prepend_subdirectories(path) abort
+ call s:warn('Change pathogen#runtime_prepend_subdirectories('.string(a:path).') to pathogen#infect('.string(a:path.'/{}').')')
+ return pathogen#surround(a:path . pathogen#slash() . '{}')
+endfunction
+
+function! pathogen#incubate(...) abort
+ let name = a:0 ? a:1 : 'bundle/{}'
+ call s:warn('Change pathogen#incubate('.(a:0 ? string(a:1) : '').') to pathogen#infect('.string(name).')')
+ return pathogen#interpose(name)
+endfunction
+
+" Deprecated alias for pathogen#interpose().
+function! pathogen#runtime_append_all_bundles(...) abort
+ if a:0
+ call s:warn('Change pathogen#runtime_append_all_bundles('.string(a:1).') to pathogen#infect('.string(a:1.'/{}').')')
+ else
+ call s:warn('Change pathogen#runtime_append_all_bundles() to pathogen#infect()')
+ endif
+ return pathogen#interpose(a:0 ? a:1 . '/{}' : 'bundle/{}')
+endfunction
+
+if exists(':Vedit')
+ finish
+endif
+
+let s:vopen_warning = 0
+
+function! s:find(count,cmd,file,lcd)
+ let rtp = pathogen#join(1,pathogen#split(&runtimepath))
+ let file = pathogen#runtime_findfile(a:file,a:count)
+ if file ==# ''
+ return "echoerr 'E345: Can''t find file \"".a:file."\" in runtimepath'"
+ endif
+ if !s:vopen_warning
+ let s:vopen_warning = 1
+ let warning = '|echohl WarningMsg|echo "Install scriptease.vim to continue using :V'.a:cmd.'"|echohl NONE'
+ else
+ let warning = ''
+ endif
+ if a:lcd
+ let path = file[0:-strlen(a:file)-2]
+ execute 'lcd `=path`'
+ return a:cmd.' '.pathogen#fnameescape(a:file) . warning
+ else
+ return a:cmd.' '.pathogen#fnameescape(file) . warning
+ endif
+endfunction
+
+function! s:Findcomplete(A,L,P)
+ let sep = pathogen#slash()
+ let cheats = {
+ \'a': 'autoload',
+ \'d': 'doc',
+ \'f': 'ftplugin',
+ \'i': 'indent',
+ \'p': 'plugin',
+ \'s': 'syntax'}
+ if a:A =~# '^\w[\\/]' && has_key(cheats,a:A[0])
+ let request = cheats[a:A[0]].a:A[1:-1]
+ else
+ let request = a:A
+ endif
+ let pattern = substitute(request,'/\|\'.sep,'*'.sep,'g').'*'
+ let found = {}
+ for path in pathogen#split(&runtimepath)
+ let path = expand(path, ':p')
+ let matches = split(glob(path.sep.pattern),"\n")
+ call map(matches,'isdirectory(v:val) ? v:val.sep : v:val')
+ call map(matches,'expand(v:val, ":p")[strlen(path)+1:-1]')
+ for match in matches
+ let found[match] = 1
+ endfor
+ endfor
+ return sort(keys(found))
+endfunction
+
+command! -bar -bang -range=1 -nargs=1 -complete=customlist,s:Findcomplete Ve :execute s:find(<count>,'edit<bang>',<q-args>,0)
+command! -bar -bang -range=1 -nargs=1 -complete=customlist,s:Findcomplete Vedit :execute s:find(<count>,'edit<bang>',<q-args>,0)
+command! -bar -bang -range=1 -nargs=1 -complete=customlist,s:Findcomplete Vopen :execute s:find(<count>,'edit<bang>',<q-args>,1)
+command! -bar -bang -range=1 -nargs=1 -complete=customlist,s:Findcomplete Vsplit :execute s:find(<count>,'split',<q-args>,<bang>1)
+command! -bar -bang -range=1 -nargs=1 -complete=customlist,s:Findcomplete Vvsplit :execute s:find(<count>,'vsplit',<q-args>,<bang>1)
+command! -bar -bang -range=1 -nargs=1 -complete=customlist,s:Findcomplete Vtabedit :execute s:find(<count>,'tabedit',<q-args>,<bang>1)
+command! -bar -bang -range=1 -nargs=1 -complete=customlist,s:Findcomplete Vpedit :execute s:find(<count>,'pedit',<q-args>,<bang>1)
+command! -bar -bang -range=1 -nargs=1 -complete=customlist,s:Findcomplete Vread :execute s:find(<count>,'read',<q-args>,<bang>1)
+
+" vim:set et sw=2 foldmethod=expr foldexpr=getline(v\:lnum)=~'^\"\ Section\:'?'>1'\:getline(v\:lnum)=~#'^fu'?'a1'\:getline(v\:lnum)=~#'^endf'?'s1'\:'=':
diff --git a/base/vim/bundle/.gitignore b/base/vim/bundle/.gitignore
new file mode 100644
index 0000000..f988cbe
--- /dev/null
+++ b/base/vim/bundle/.gitignore
@@ -0,0 +1,4 @@
+vim-abolish/
+ctrlp.vim/
+tcomment/
+typescript-vim/
diff --git a/base/vim/bundle/fetch_bundles.sh b/base/vim/bundle/fetch_bundles.sh
new file mode 100755
index 0000000..fabe0a9
--- /dev/null
+++ b/base/vim/bundle/fetch_bundles.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+##
+## I don't want to include the plugin repos in my dotfiles,
+## so here are the comments to fetch the plugins I care about.
+##
+
+git clone git://github.com/tpope/vim-abolish.git
+git clone https://github.com/ctrlpvim/ctrlp.vim.git
+git clone https://github.com/tomtom/tcomment_vim.git
+git clone https://github.com/leafgarland/typescript-vim.git
diff --git a/base/vim/bundle/javacomplete/autoload/Reflection.java b/base/vim/bundle/javacomplete/autoload/Reflection.java
new file mode 100644
index 0000000..5452b0f
--- /dev/null
+++ b/base/vim/bundle/javacomplete/autoload/Reflection.java
@@ -0,0 +1,670 @@
+/**
+ * Reflection.java
+ *
+ * A utility class for javacomplete mainly for reading class or package information.
+ * Version: 0.77
+ * Maintainer: cheng fang <fangread@yahoo.com.cn>
+ * Last Change: 2007-09-16
+ * Copyright: Copyright (C) 2007 cheng fang. All rights reserved.
+ * License: Vim License (see vim's :help license)
+ *
+ */
+
+import java.lang.reflect.*;
+import java.io.*;
+import java.util.*;
+import java.util.zip.*;
+
+class Reflection {
+ static final String VERSION = "0.77";
+
+ static final int OPTION_FIELD = 1;
+ static final int OPTION_METHOD = 2;
+ static final int OPTION_STATIC_FIELD = 4;
+ static final int OPTION_STATIC_METHOD = 8;
+ static final int OPTION_CONSTRUCTOR = 16;
+ static final int OPTION_STATIC = 12; // compound static
+ static final int OPTION_INSTANCE = 15; // compound instance
+ static final int OPTION_ALL = 31; // compound all
+ static final int OPTION_SUPER = 32;
+ static final int OPTION_SAME_PACKAGE = 64;
+
+ static final int STRATEGY_ALPHABETIC = 128;
+ static final int STRATEGY_HIERARCHY = 256;
+ static final int STRATEGY_DEFAULT = 512;
+
+ static final int RETURN_ALL_PACKAGE_INFO = 0x1000;
+
+ static final String KEY_NAME = "'n':"; // "'name':";
+ static final String KEY_TYPE = "'t':"; // "'type':";
+ static final String KEY_MODIFIER = "'m':"; // "'modifier':";
+ static final String KEY_PARAMETERTYPES = "'p':"; // "'parameterTypes':";
+ static final String KEY_RETURNTYPE = "'r':"; // "'returnType':";
+ static final String KEY_DESCRIPTION = "'d':"; // "'description':";
+ static final String KEY_DECLARING_CLASS = "'c':"; // "'declaringclass':";
+
+ static final String NEWLINE = ""; // "\r\n"
+
+ static boolean debug_mode = false;
+
+ static Hashtable htClasspath = new Hashtable();
+
+ public static boolean existed(String fqn) {
+ boolean result = false;
+ try {
+ Class.forName(fqn);
+ result = true;
+ }
+ catch (Exception ex) {
+ }
+ return result;
+ }
+
+ public static String existedAndRead(String fqns) {
+ Hashtable mapPackages = new Hashtable(); // qualified name --> StringBuffer
+ Hashtable mapClasses = new Hashtable(); // qualified name --> StringBuffer
+
+ for (StringTokenizer st = new StringTokenizer(fqns, ","); st.hasMoreTokens(); ) {
+ String fqn = st.nextToken();
+ try {
+ Class clazz = Class.forName(fqn);
+ putClassInfo(mapClasses, clazz);
+ }
+ catch (Exception ex) {
+ String binaryName = fqn;
+ boolean found = false;
+ while (true) {
+ try {
+ int lastDotPos = binaryName.lastIndexOf('.');
+ if (lastDotPos == -1)
+ break;
+ binaryName = binaryName.substring(0, lastDotPos) + '$' + binaryName.substring(lastDotPos+1, binaryName.length());
+ Class clazz = Class.forName(binaryName);
+ putClassInfo(mapClasses, clazz);
+ found = true;
+ break;
+ }
+ catch (Exception e) {
+ }
+ }
+ if (!found)
+ putPackageInfo(mapPackages, fqn);
+ }
+ }
+
+ if (mapPackages.size() > 0 || mapClasses.size() > 0) {
+ StringBuffer sb = new StringBuffer(4096);
+ sb.append("{");
+ for (Enumeration e = mapPackages.keys(); e.hasMoreElements(); ) {
+ String s = (String)e.nextElement();
+ sb.append("'").append( s.replace('$', '.') ).append("':").append(mapPackages.get(s)).append(",");
+ }
+ for (Enumeration e = mapClasses.keys(); e.hasMoreElements(); ) {
+ String s = (String)e.nextElement();
+ sb.append("'").append( s.replace('$', '.') ).append("':").append(mapClasses.get(s)).append(",");
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+ else
+ return "";
+ }
+
+ private static String getPackageList(String fqn) {
+ Hashtable mapPackages = new Hashtable();
+ putPackageInfo(mapPackages, fqn);
+ return mapPackages.size() > 0 ? mapPackages.get(fqn).toString() : "";
+ }
+
+ private static Hashtable collectClassPath() {
+ if (!htClasspath.isEmpty())
+ return htClasspath;
+
+ // runtime classes
+ if ("Kaffe".equals(System.getProperty("java.vm.name"))) {
+ addClasspathesFromDir(System.getProperty("java.home") + File.separator + "share" + File.separator + "kaffe" + File.separator);
+ }
+ else if ("GNU libgcj".equals(System.getProperty("java.vm.name"))) {
+ if (new File(System.getProperty("sun.boot.class.path")).exists())
+ htClasspath.put(System.getProperty("sun.boot.class.path"), "");
+ }
+
+ if (System.getProperty("java.vendor").toLowerCase(Locale.US).indexOf("microsoft") >= 0) {
+ // `*.ZIP` files in `Packages` directory
+ addClasspathesFromDir(System.getProperty("java.home") + File.separator + "Packages" + File.separator);
+ }
+ else {
+ // the following code works for several kinds of JDK
+ // - JDK1.1: classes.zip
+ // - JDK1.2+: rt.jar
+ // - JDK1.4+ of Sun and Apple: rt.jar + jce.jar + jsse.jar
+ // - JDK1.4 of IBM split rt.jar into core.jar, graphics.jar, server.jar
+ // combined jce.jar and jsse.jar into security.jar
+ // - JDK for MacOS X split rt.jar into classes.jar, ui.jar in Classes directory
+ addClasspathesFromDir(System.getProperty("java.home") + File.separator + "lib" + File.separator);
+ addClasspathesFromDir(System.getProperty("java.home") + File.separator + "jre" + File.separator + "lib" + File.separator);
+ addClasspathesFromDir(System.getProperty("java.home") + File.separator + ".." + File.separator + "Classes" + File.separator);
+ }
+
+ // ext
+ String extdirs = System.getProperty("java.ext.dirs");
+ for (StringTokenizer st = new StringTokenizer(extdirs, File.pathSeparator); st.hasMoreTokens(); ) {
+ addClasspathesFromDir(st.nextToken() + File.separator);
+ }
+
+ // user classpath
+ String classPath = System.getProperty("java.class.path");
+ StringTokenizer st = new StringTokenizer(classPath, File.pathSeparator);
+ while (st.hasMoreTokens()) {
+ String path = st.nextToken();
+ File f = new File(path);
+ if (!f.exists())
+ continue;
+
+ if (path.endsWith(".jar") || path.endsWith(".zip"))
+ htClasspath.put(f.toString(), "");
+ else {
+ if (f.isDirectory())
+ htClasspath.put(f.toString(), "");
+ }
+ }
+
+ return htClasspath;
+ }
+
+ private static void addClasspathesFromDir(String dirpath) {
+ File dir = new File(dirpath);
+ if (dir.isDirectory()) {
+ String[] items = dir.list(); // use list() instead of listFiles() since the latter are introduced in 1.2
+ for (int i = 0; i < items.length; i++) {
+ File f = new File(dirpath + items[i]);
+ if (!f.exists())
+ continue;
+
+ if (items[i].endsWith(".jar") || items[i].endsWith(".zip") || items[i].endsWith(".ZIP")) {
+ htClasspath.put(f.toString(), "");
+ }
+ else if (items.equals("classes")) {
+ if (f.isDirectory())
+ htClasspath.put(f.toString(), "");
+ }
+ }
+ }
+ }
+
+
+ /**
+ * If name is empty, put all loadable package info into map once.
+ */
+ private static void putPackageInfo(Hashtable map, String name) {
+ String prefix = name.replace('.', '/') + "/";
+ Hashtable subpackages = new Hashtable();
+ Hashtable classes = new Hashtable();
+ for (Enumeration e = collectClassPath().keys(); e.hasMoreElements(); ) {
+ String path = (String)e.nextElement();
+ if (path.endsWith(".jar") || path.endsWith(".zip"))
+ appendListFromJar(subpackages, classes, path, prefix);
+ else
+ appendListFromFolder(subpackages, classes, path, prefix);
+ }
+
+ if (subpackages.size() > 0 || classes.size() > 0) {
+ StringBuffer sb = new StringBuffer(1024);
+ sb.append("{'tag':'PACKAGE','subpackages':[");
+ for (Enumeration e = subpackages.keys(); e.hasMoreElements(); ) {
+ sb.append("'").append(e.nextElement()).append("',");
+ }
+ sb.append("],'classes':[");
+ for (Enumeration e = classes.keys(); e.hasMoreElements(); ) {
+ sb.append("'").append(e.nextElement()).append("',");
+ }
+ sb.append("]}");
+ map.put(name, sb.toString());
+ }
+ }
+
+ public static void appendListFromJar(Hashtable subpackages, Hashtable classes, String path, String prefix) {
+ try {
+ for (Enumeration entries = new ZipFile(path).entries(); entries.hasMoreElements(); ) {
+ String entry = entries.nextElement().toString();
+ int len = entry.length();
+ if (entry.endsWith(".class") && entry.indexOf('$') == -1
+ && entry.startsWith(prefix)) {
+ int splitPos = entry.indexOf('/', prefix.length());
+ String shortname = entry.substring(prefix.length(), splitPos == -1 ? entry.length()-6 : splitPos);
+ if (splitPos == -1) {
+ if (!classes.containsKey(shortname))
+ classes.put(shortname, ""); //classes.put(shortname, "{'tag':'CLASSDEF','name':'"+shortname+"'}");
+ }
+ else {
+ if (!subpackages.containsKey(shortname))
+ subpackages.put(shortname, ""); //subpackages.put(shortname, "{'tag':'PACKAGE','name':'" +shortname+"'}");
+ }
+ }
+ }
+ }
+ catch (Throwable e) {
+ //e.printStackTrace();
+ }
+ }
+
+ public static void appendListFromFolder(Hashtable subpackages, Hashtable classes, String path, String prefix) {
+ try {
+ String fullPath = path + "/" + prefix;
+ File file = new File(fullPath);
+ if (file.isDirectory()) {
+ String[] descents = file.list();
+ for (int i = 0; i < descents.length; i++) {
+ if (descents[i].indexOf('$') == -1) {
+ if (descents[i].endsWith(".class")) {
+ String shortname = descents[i].substring(0, descents[i].length()-6);
+ if (!classes.containsKey(shortname))
+ classes.put(shortname, "");
+ }
+ else if ((new File(fullPath + "/" + descents[i])).isDirectory()) {
+ if (!subpackages.containsKey(descents[i]))
+ subpackages.put(descents[i], "");
+ }
+ }
+ }
+ }
+ }
+ catch (Throwable e) {
+ }
+ }
+
+ private static int INDEX_PACKAGE = 0;
+ private static int INDEX_CLASS = 1;
+
+ // generate information of all packages in jar files.
+ public static String getPackageList() {
+ Hashtable map = new Hashtable();
+
+ for (Enumeration e = collectClassPath().keys(); e.hasMoreElements(); ) {
+ String path = (String)e.nextElement();
+ if (path.endsWith(".jar") || path.endsWith(".zip"))
+ appendListFromJar(path, map);
+ }
+
+ StringBuffer sb = new StringBuffer(4096);
+ sb.append("{");
+ //sb.append("'*':'").append( map.remove("") ).append("',"); // default package
+ for (Enumeration e = map.keys(); e.hasMoreElements(); ) {
+ String s = (String)e.nextElement();
+ StringBuffer[] sbs = (StringBuffer[])map.get(s);
+ sb.append("'").append( s.replace('/', '.') ).append("':")
+ .append("{'tag':'PACKAGE'");
+ if (sbs[INDEX_PACKAGE].length() > 0)
+ sb.append(",'subpackages':[").append(sbs[INDEX_PACKAGE]).append("]");
+ if (sbs[INDEX_CLASS].length() > 0)
+ sb.append(",'classes':[").append(sbs[INDEX_CLASS]).append("]");
+ sb.append("},");
+ }
+ sb.append("}");
+ return sb.toString();
+
+ }
+
+ public static void appendListFromJar(String path, Hashtable map) {
+ try {
+ for (Enumeration entries = new ZipFile(path).entries(); entries.hasMoreElements(); ) {
+ String entry = entries.nextElement().toString();
+ int len = entry.length();
+ if (entry.endsWith(".class") && entry.indexOf('$') == -1) {
+ int slashpos = entry.lastIndexOf('/');
+ String parent = entry.substring(0, slashpos);
+ String child = entry.substring(slashpos+1, len-6);
+ putItem(map, parent, child, INDEX_CLASS);
+
+ slashpos = parent.lastIndexOf('/');
+ if (slashpos != -1) {
+ AddToParent(map, parent.substring(0, slashpos), parent.substring(slashpos+1));
+ }
+ }
+ }
+ }
+ catch (Throwable e) {
+ //e.printStackTrace();
+ }
+ }
+
+ public static void putItem(Hashtable map, String parent, String child, int index) {
+ StringBuffer[] sbs = (StringBuffer[])map.get(parent);
+ if (sbs == null) {
+ sbs = new StringBuffer[] { new StringBuffer(256), // packages
+ new StringBuffer(256) // classes
+ };
+ }
+ if (sbs[index].toString().indexOf("'" + child + "',") == -1)
+ sbs[index].append("'").append(child).append("',");
+ map.put(parent, sbs);
+ }
+
+ public static void AddToParent(Hashtable map, String parent, String child) {
+ putItem(map, parent, child, INDEX_PACKAGE);
+
+ int slashpos = parent.lastIndexOf('/');
+ if (slashpos != -1) {
+ AddToParent(map, parent.substring(0, slashpos), parent.substring(slashpos+1));
+ }
+ }
+
+
+ public static String getClassInfo(String className) {
+ Hashtable mapClasses = new Hashtable();
+ try {
+ Class clazz = Class.forName(className);
+ putClassInfo(mapClasses, clazz);
+ }
+ catch (Exception ex) {
+ }
+
+ if (mapClasses.size() == 1) {
+ return mapClasses.get(className).toString(); // return {...}
+ }
+ else if (mapClasses.size() > 1) {
+ StringBuffer sb = new StringBuffer(4096);
+ sb.append("[");
+ for (Enumeration e = mapClasses.keys(); e.hasMoreElements(); ) {
+ String s = (String)e.nextElement();
+ sb.append(mapClasses.get(s)).append(",");
+ }
+ sb.append("]");
+ return sb.toString(); // return [...]
+ }
+ else
+ return "";
+ }
+
+ private static void putClassInfo(Hashtable map, Class clazz) {
+ if (map.containsKey(clazz.getName()))
+ return ;
+
+ try {
+ StringBuffer sb = new StringBuffer(1024);
+ sb.append("{")
+ .append("'tag':'CLASSDEF',").append(NEWLINE)
+ .append("'flags':'").append(Integer.toString(clazz.getModifiers(), 2)).append("',").append(NEWLINE)
+ .append("'name':'").append(clazz.getName().replace('$', '.')).append("',").append(NEWLINE)
+ //.append("'package':'").append(clazz.getPackage().getName()).append("',").append(NEWLINE) // no getPackage() in JDK1.1
+ .append("'classpath':'1',").append(NEWLINE)
+ .append("'fqn':'").append(clazz.getName().replace('$', '.')).append("',").append(NEWLINE);
+
+ Class[] interfaces = clazz.getInterfaces();
+ if (clazz.isInterface()) {
+ sb.append("'extends':[");
+ } else {
+ Class superclass = clazz.getSuperclass();
+ if (superclass != null && !"java.lang.Object".equals(superclass.getName())) {
+ sb.append("'extends':['").append(superclass.getName().replace('$', '.')).append("'],").append(NEWLINE);
+ putClassInfo(map, superclass); // !!
+ }
+ sb.append("'implements':[");
+ }
+ for (int i = 0, n = interfaces.length; i < n; i++) {
+ sb.append("'").append(interfaces[i].getName().replace('$', '.')).append("',");
+ putClassInfo(map, interfaces[i]); // !!
+ }
+ sb.append("],").append(NEWLINE);;
+
+ Constructor[] ctors = clazz.getConstructors();
+ sb.append("'ctors':[");
+ for (int i = 0, n = ctors.length; i < n; i++) {
+ Constructor ctor = ctors[i];
+ sb.append("{");
+ appendModifier(sb, ctor.getModifiers());
+ appendParameterTypes(sb, ctor.getParameterTypes());
+ sb.append(KEY_DESCRIPTION).append("'").append(ctors[i].toString()).append("'");
+ sb.append("},").append(NEWLINE);
+ }
+ sb.append("], ").append(NEWLINE);
+
+ Field[] fields = clazz.getFields();
+ //java.util.Arrays.sort(fields, comparator);
+ sb.append("'fields':[");
+ for (int i = 0, n = fields.length; i < n; i++) {
+ Field f = fields[i];
+ int modifier = f.getModifiers();
+ sb.append("{");
+ sb.append(KEY_NAME).append("'").append(f.getName()).append("',");
+ if (!f.getDeclaringClass().getName().equals(clazz.getName()))
+ sb.append(KEY_DECLARING_CLASS).append("'").append(f.getDeclaringClass().getName()).append("',");
+ appendModifier(sb, modifier);
+ sb.append(KEY_TYPE).append("'").append(f.getType().getName()).append("'");
+ sb.append("},").append(NEWLINE);
+ }
+ sb.append("], ").append(NEWLINE);
+
+ Method[] methods = clazz.getMethods();
+ //java.util.Arrays.sort(methods, comparator);
+ sb.append("'methods':[");
+ for (int i = 0, n = methods.length; i < n; i++) {
+ Method m = methods[i];
+ int modifier = m.getModifiers();
+ sb.append("{");
+ sb.append(KEY_NAME).append("'").append(m.getName()).append("',");
+ if (!m.getDeclaringClass().getName().equals(clazz.getName()))
+ sb.append(KEY_DECLARING_CLASS).append("'").append(m.getDeclaringClass().getName()).append("',");
+ appendModifier(sb, modifier);
+ sb.append(KEY_RETURNTYPE).append("'").append(m.getReturnType().getName()).append("',");
+ appendParameterTypes(sb, m.getParameterTypes());
+ sb.append(KEY_DESCRIPTION).append("'").append(m.toString()).append("'");
+ sb.append("},").append(NEWLINE);
+ }
+ sb.append("], ").append(NEWLINE);
+
+ Class[] classes = clazz.getClasses();
+ sb.append("'classes': [");
+ for (int i = 0, n = classes.length; i < n; i++) {
+ Class c = classes[i];
+ sb.append("'").append(c.getName().replace('$', '.')).append("',");
+ putClassInfo(map, c); // !!
+ }
+ sb.append("], ").append(NEWLINE);
+
+ appendDeclaredMembers(map, clazz, sb);
+
+ sb.append("}");
+ map.put(clazz.getName(), sb);
+ }
+ catch (Exception ex) {
+ //ex.printStackTrace();
+ }
+ }
+
+ private static void appendDeclaredMembers(Hashtable map, Class clazz, StringBuffer sb) {
+ Constructor[] ctors = clazz.getDeclaredConstructors();
+ sb.append("'declared_ctors':[");
+ for (int i = 0, n = ctors.length; i < n; i++) {
+ Constructor ctor = ctors[i];
+ if (!Modifier.isPublic(ctor.getModifiers())) {
+ sb.append("{");
+ appendModifier(sb, ctor.getModifiers());
+ appendParameterTypes(sb, ctor.getParameterTypes());
+ sb.append(KEY_DESCRIPTION).append("'").append(ctors[i].toString()).append("'");
+ sb.append("},").append(NEWLINE);
+ }
+ }
+ sb.append("], ").append(NEWLINE);
+
+ Field[] fields = clazz.getDeclaredFields();
+ sb.append("'declared_fields':[");
+ for (int i = 0, n = fields.length; i < n; i++) {
+ Field f = fields[i];
+ int modifier = f.getModifiers();
+ if (!Modifier.isPublic(modifier)) {
+ sb.append("{");
+ sb.append(KEY_NAME).append("'").append(f.getName()).append("',");
+ if (!f.getDeclaringClass().getName().equals(clazz.getName()))
+ sb.append(KEY_DECLARING_CLASS).append("'").append(f.getDeclaringClass().getName()).append("',");
+ appendModifier(sb, modifier);
+ sb.append(KEY_TYPE).append("'").append(f.getType().getName()).append("'");
+ sb.append("},").append(NEWLINE);
+ }
+ }
+ sb.append("], ").append(NEWLINE);
+
+ Method[] methods = clazz.getDeclaredMethods();
+ sb.append("'declared_methods':[");
+ for (int i = 0, n = methods.length; i < n; i++) {
+ Method m = methods[i];
+ int modifier = m.getModifiers();
+ if (!Modifier.isPublic(modifier)) {
+ sb.append("{");
+ sb.append(KEY_NAME).append("'").append(m.getName()).append("',");
+ if (!m.getDeclaringClass().getName().equals(clazz.getName()))
+ sb.append(KEY_DECLARING_CLASS).append("'").append(m.getDeclaringClass().getName()).append("',");
+ appendModifier(sb, modifier);
+ sb.append(KEY_RETURNTYPE).append("'").append(m.getReturnType().getName()).append("',");
+ appendParameterTypes(sb, m.getParameterTypes());
+ sb.append(KEY_DESCRIPTION).append("'").append(m.toString()).append("'");
+ sb.append("},").append(NEWLINE);
+ }
+ }
+ sb.append("], ").append(NEWLINE);
+
+ Class[] classes = clazz.getDeclaredClasses();
+ sb.append("'declared_classes': [");
+ for (int i = 0, n = classes.length; i < n; i++) {
+ Class c = classes[i];
+ if (!Modifier.isPublic(c.getModifiers())) {
+ sb.append("'").append(c.getName().replace('$', '.')).append("',");
+ putClassInfo(map, c); // !!
+ }
+ }
+ sb.append("], ").append(NEWLINE);
+ }
+
+ private static void appendModifier(StringBuffer sb, int modifier) {
+ sb.append(KEY_MODIFIER).append("'").append(Integer.toString(modifier, 2)).append("', ");
+ }
+
+ private static void appendParameterTypes(StringBuffer sb, Class[] paramTypes) {
+ if (paramTypes.length == 0) return ;
+
+ sb.append(KEY_PARAMETERTYPES).append("[");
+ for (int j = 0; j < paramTypes.length; j++) {
+ sb.append("'").append(paramTypes[j].getName()).append("',");
+ }
+ sb.append("],");
+ }
+
+ private static boolean isBlank(String str) {
+ int len;
+ if (str == null || (len = str.length()) == 0)
+ return true;
+ for (int i = 0; i < len; i++)
+ if ((Character.isWhitespace(str.charAt(i)) == false))
+ return false;
+ return true;
+ }
+
+ // test methods
+
+ static void debug(String s) {
+ if (debug_mode)
+ System.out.println(s);
+ }
+ static void output(String s) {
+ if (!debug_mode)
+ System.out.print(s);
+ }
+
+
+ private static void usage() {
+ System.out.println("Reflection for javacomplete (" + VERSION + ")");
+ System.out.println(" java [-classpath] Reflection [-c] [-d] [-e] [-h] [-v] [-p] [-s] name[,comma_separated_name_list]");
+ System.out.println("Options:");
+ System.out.println(" -a list all members in alphabetic order");
+ System.out.println(" -c list constructors");
+ System.out.println(" -C return class info");
+ System.out.println(" -d default strategy, i.e. instance fields, instance methods, static fields, static methods");
+ System.out.println(" -e check class existed");
+ System.out.println(" -E check class existed and read class information");
+ System.out.println(" -D debug mode");
+ System.out.println(" -p list package content");
+ System.out.println(" -P print all package info in the Vim dictionary format");
+ System.out.println(" -s list static fields and methods");
+ System.out.println(" -h help");
+ System.out.println(" -v version");
+ }
+
+ public static void main(String[] args) {
+ String className = null;
+ int option = 0x0;
+ boolean wholeClassInfo = false;
+ boolean onlyStatic = false;
+ boolean onlyConstructor = false;
+ boolean listPackageContent = false;
+ boolean checkExisted = false;
+ boolean checkExistedAndRead = false;
+ boolean allPackageInfo = false;
+
+ for (int i = 0, n = args.length; i < n && !isBlank(args[i]); i++) {
+ //debug(args[i]);
+ if (args[i].charAt(0) == '-') {
+ if (args[i].length() > 1) {
+ switch (args[i].charAt(1)) {
+ case 'a':
+ break;
+ case 'c': // request constructors
+ option = option | OPTION_CONSTRUCTOR;
+ onlyConstructor = true;
+ break;
+ case 'C': // class info
+ wholeClassInfo = true;
+ break;
+ case 'd': // default strategy
+ option = option | STRATEGY_DEFAULT;
+ break;
+ case 'D': // debug mode
+ debug_mode = true;
+ break;
+ case 'e': // class existed
+ checkExisted = true;
+ break;
+ case 'E': // check existed and read class information
+ checkExistedAndRead = true;
+ break;
+ case 'h': // help
+ usage();
+ return ;
+ case 'v': // version
+ System.out.println("Reflection for javacomplete (" + VERSION + ")");
+ break;
+ case 'p':
+ listPackageContent = true;
+ break;
+ case 'P':
+ option = RETURN_ALL_PACKAGE_INFO;
+ break;
+ case 's': // request static members
+ option = option | OPTION_STATIC_METHOD | OPTION_STATIC_FIELD;
+ onlyStatic = true;
+ break;
+ default:
+ }
+ }
+ }
+ else {
+ className = args[i];
+ }
+ }
+ if (className == null && (option & RETURN_ALL_PACKAGE_INFO) != RETURN_ALL_PACKAGE_INFO) {
+ return;
+ }
+ if (option == 0x0)
+ option = OPTION_INSTANCE;
+
+ if (wholeClassInfo)
+ output( getClassInfo(className) );
+ else if ((option & RETURN_ALL_PACKAGE_INFO) == RETURN_ALL_PACKAGE_INFO)
+ output( getPackageList() );
+ else if (checkExistedAndRead)
+ output( existedAndRead(className) );
+ else if (checkExisted)
+ output( String.valueOf(existed(className)) );
+ else if (listPackageContent)
+ output( getPackageList(className) );
+ }
+}
diff --git a/base/vim/bundle/javacomplete/autoload/java_parser.vim b/base/vim/bundle/javacomplete/autoload/java_parser.vim
new file mode 100644
index 0000000..5e1ec38
--- /dev/null
+++ b/base/vim/bundle/javacomplete/autoload/java_parser.vim
@@ -0,0 +1,3500 @@
+" Vim autoload script for a JAVA PARSER and more.
+" Language: Java
+" Maintainer: cheng fang <fangread@yahoo.com.cn>
+" Last Changed: 2007-09-16
+" Version: 0.67
+" Copyright: Copyright (C) 2007 cheng fang. All rights reserved.
+" License: Vim License (see vim's :help license)
+
+
+if exists("g:loaded_javaparser") || version < 700 || &cp
+ finish
+endif
+let g:loaded_javaparser = 'v0.67'
+
+
+" Constants used by scanner and parser {{{1
+let s:EOI = ''
+
+let s:keywords = {'+': 'PLUS', '-': 'SUB', '!': 'BANG', '%': 'PERCENT', '^': 'CARET', '&': 'AMP', '*': 'STAR', '|': 'BAR', '~': 'TILDE', '/': 'SLASH', '>': 'GT', '<': 'LT', '?': 'QUES', ':': 'COLON', '=': 'EQ', '++': 'PLUSPLUS', '--': 'SUBSUB', '==': 'EQEQ', '<=': 'LTEQ', '>=': 'GTEQ', '!=': 'BANGEQ', '<<': 'LTLT', '>>': 'GTGT', '>>>': 'GTGTGT', '+=': 'PLUSEQ', '-=': 'SUBEQ', '*=': 'STAREQ', '/=': 'SLASHEQ', '&=': 'AMPEQ', '|=': 'BAREQ', '^=': 'CARETEQ', '%=': 'PERCENTEQ', '<<=': 'LTLTEQ', '>>=': 'GTGTEQ', '>>>=': 'GTGTGTEQ', '||': 'BARBAR', '&&': 'AMPAMP', 'abstract': 'ABSTRACT', 'assert': 'ASSERT', 'boolean': 'BOOLEAN', 'break': 'BREAK', 'byte': 'BYTE', 'case': 'CASE', 'catch': 'CATCH', 'char': 'CHAR', 'class': 'CLASS', 'const': 'CONST', 'continue': 'CONTINUE', 'default': 'DEFAULT', 'do': 'DO', 'double': 'DOUBLE', 'else': 'ELSE', 'extends': 'EXTENDS', 'final': 'FINAL', 'finally': 'FINALLY', 'float': 'FLOAT', 'for': 'FOR', 'goto': 'GOTO', 'if': 'IF', 'implements': 'IMPLEMENTS', 'import': 'IMPORT', 'instanceof': 'INSTANCEOF', 'int': 'INT', 'interface': 'INTERFACE', 'long': 'LONG', 'native': 'NATIVE', 'new': 'NEW', 'package': 'PACKAGE', 'private': 'PRIVATE', 'protected': 'PROTECTED', 'public': 'PUBLIC', 'return': 'RETURN', 'short': 'SHORT', 'static': 'STATIC', 'strictfp': 'STRICTFP', 'super': 'SUPER', 'switch': 'SWITCH', 'synchronized': 'SYNCHRONIZED', 'this': 'THIS', 'throw': 'THROW', 'throws': 'THROWS', 'transient': 'TRANSIENT', 'try': 'TRY', 'void': 'VOID', 'volatile': 'VOLATILE', 'while': 'WHILE', 'true': 'TRUE', 'false': 'FALSE', 'null': 'NULL', '(': 'LPAREN', ')': 'RPAREN', '{': 'LBRACE', '}': 'RBRACE', '[': 'LBRACKET', ']': 'RBRACKET', ';': 'SEMI', ',': 'COMMA', '.': 'DOT', 'enum': 'ENUM', '...': 'ELLIPSIS', '@': 'MONKEYS_AT'}
+
+let s:Flags = {'PUBLIC': 0x1, 'PRIVATE': 0x2, 'PROTECTED': 0x4, 'STATIC': 0x8, 'FINAL': 0x10, 'SYNCHRONIZED': 0x20, 'VOLATILE': 0x40, 'TRANSIENT': 0x80, 'NATIVE': 0x100, 'INTERFACE': 0x200, 'ABSTRACT': 0x400, 'STRICTFP': 0x800, 'SYNTHETIC': 0x1000, 'ANNOTATION': 0x2000, 'ENUM': 0x4000, 'StandardFlags':0x0fff, 'ACC_SUPER': 0x20, 'ACC_BRIDGE': 0x40, 'ACC_VARARGS': 0x80, 'DEPRECATED': 0x20000, 'HASINIT': 0x40000, 'BLOCK': 0x100000, 'IPROXY': 0x200000, 'NOOUTERTHIS': 0x400000, 'EXISTS': 0x800000, 'COMPOUND': 0x1000000, 'CLASS_SEEN': 0x2000000, 'SOURCE_SEEN': 0x4000000, 'LOCKED': 0x8000000, 'UNATTRIBUTED': 0x10000000, 'ANONCONSTR': 0x20000000, 'ACYCLIC': 0x40000000, 'BRIDGE': 1.repeat('0', 31), 'PARAMETER': 1.repeat('0', 33), 'VARARGS': 1.repeat('0', 34), 'ACYCLIC_ANN': 1.repeat('0', 35), 'GENERATEDCONSTR': 1.repeat('0', 36), 'HYPOTHETICAL': 1.repeat('0', 37), 'PROPRIETARY': 1.repeat('0', 38)}
+
+let s:RE_ANYTHING_AND_NEWLINE = '\(\(.\|\n\)*\)'
+let s:RE_LINE_COMMENT = '//.*$'
+let s:RE_COMMENT_SP = '/\*\*/'
+let s:RE_COMMENT = ''
+let s:RE_BRACKETS = '\(\s*\[\s*\]\)'
+
+let s:RE_IDENTIFIER = '[a-zA-Z_$][a-zA-Z0-9_$]*'
+let s:RE_QUALID = s:RE_IDENTIFIER. '\(\s*\.\s*' .s:RE_IDENTIFIER. '\)*'
+let s:RE_TYPE_NAME = s:RE_QUALID
+let s:RE_REFERENCE_TYPE = s:RE_QUALID . s:RE_BRACKETS . '*' " TypeName || Type[]
+let s:RE_TYPE = s:RE_REFERENCE_TYPE " PrimitiveType || ReferenceType
+let s:RE_TYPE_VARIABLE = s:RE_IDENTIFIER
+let s:RE_VAR_DECL_ID = s:RE_IDENTIFIER . s:RE_BRACKETS . '*'
+
+let s:RE_TYPE_PARAMS = ''
+
+let s:RE_THROWS = 'throws\s\+' . s:RE_TYPE_NAME . '\(\s*,\s*' . s:RE_TYPE_NAME . '\)*'
+let s:RE_FORMAL_PARAM = '\(final\s*\)\='. s:RE_TYPE . '\s\+' . s:RE_VAR_DECL_ID
+let s:RE_FORMAL_PARAM_LIST = s:RE_FORMAL_PARAM . '\(\s*,\s*' . s:RE_FORMAL_PARAM . '\)*'
+let s:RE_FORMAL_PARAM2 = '^\s*\(final\s*\)\=\('. s:RE_TYPE . '\)\s\+\(' . s:RE_IDENTIFIER . '\)' . s:RE_BRACKETS . '*'
+
+let s:RE_MEMBER_MODS = '\%(PUBLIC\|PROTECTED\|PRIVATE\|ABSTRACT\|STATIC\|FINAL\|TRANSIENT\|VOLATILE\|SYNCHRONIZED\|NATIVE\|STRICTFP\)'
+let s:RE_MEMBER_HEADER = '\s*\(\%(' .s:RE_MEMBER_MODS. '\s\+\)\+\)\(' .s:RE_IDENTIFIER. '\%(\s*\.\s*' .s:RE_IDENTIFIER. '\)*\%(\s*\[\s*\]\)*\)\s\+\(' .s:RE_IDENTIFIER. '\)'
+
+" API {{{1
+
+let s:PROTOTYPE = {'s:options': {}, 'b:buf': '', 'b:buflen': 0, 'b:lines': [], 'b:idxes': [0], 'b:bp': -1, 'b:ch': '', 'b:line': 0, 'b:col': 0, 'b:pos': 0, 'b:endPos': 0, 'b:prevEndPos': 0, 'b:errPos': -1, 'b:errorEndPos': -1, 'b:sbuf': '', 'b:name': '', 'b:token': '', 'b:docComment': '', 'b:radix': 0, 'b:unicodeConversionBp': -1, 'b:scanStrategy': 0, 'b:allowGenerics': 1, 'b:allowVarargs': 1, 'b:allowAsserts': 1, 'b:allowEnums': 1, 'b:allowForeach': 1, 'b:allowStaticImport': 1, 'b:allowAnnotations': 1, 'b:keepDocComments': 1, 'b:mode': 0, 'b:lastmode': 0, 'b:log': [], 'b:et_perf': '', 'b:et_nextToken_count': 0}
+
+" Function to initialize the parser
+" parameters:
+" - lines List of code text
+" - options A set of options
+fu! java_parser#InitParser(lines, ...)
+ let s:options = a:0 == 0 ? {} : a:1
+
+ " internal variables for scanning
+" let b:buf = '' " The input buffer
+ let b:buflen = 0 " index of one past last character in buffer. also eofPos
+ let b:lines = a:lines " The input buffer
+ let b:idxes = [0] " Begin index of every lines
+ let b:bp = -1 " index of next character to be read.
+ let b:ch = '' " The current character.
+ let b:line = 0 " The line number position of the current character.
+ let b:col = 0 " The column number position of the current character.
+ let b:pos = 0 " The token's position, 0-based offset from beginning of text.
+ let b:endPos = 0 " Character position just after the last character of the token.
+ let b:prevEndPos = 0 " The last character position of the previous token.
+ let b:errPos = -1 " The position where a lexical error occurred
+ let b:errorEndPos = -1 "
+ let b:sbuf = '' " A character buffer for literals.
+ let b:name = '' " The name of an identifier or token:
+ let b:token = 0 " The token, set by s:nextToken().
+ let b:docComment = ''
+ let b:radix = 0 " The radix of a numeric literal token.
+ let b:unicodeConversionBp =-1 " The buffer index of the last converted unicode character
+
+ let b:scanStrategy = get(s:options, 'scanStrategy', -1) " 0 - only class members when parse full file;
+ " 1 - keep statement as a whole string;
+ " 2 - all
+ " -1 - enable quick recognition of declarations in common form.
+
+ " language feature options.
+ let b:allowGenerics = get(s:options, 'allowGenerics', 1)
+ let b:allowVarargs = get(s:options, 'allowVarargs', 1)
+ let b:allowAsserts = get(s:options, 'allowAsserts', 1)
+ let b:allowEnums = get(s:options, 'allowEnums', 1)
+ let b:allowForeach = get(s:options, 'allowForeach', 1)
+ let b:allowStaticImport = get(s:options, 'allowStaticImport', 1)
+ let b:allowAnnotations = get(s:options, 'allowAnnotations', 1)
+ let b:keepDocComments = get(s:options, 'keepDocComments', 1)
+
+ let b:mode = 0 " The current mode.
+ let b:lastmode = 0 " The mode of the term that was parsed last.
+
+
+ let b:log = []
+
+ let b:et_perf = ''
+ let b:et_nextToken_count = 0
+
+" let b:buf = join(a:lines, "\r")
+" let b:buflen = strlen(b:buf)
+ for line in a:lines
+ let b:buflen += strlen(line) + 1
+ call add(b:idxes, b:buflen)
+ endfor
+ call add(b:lines, s:EOI) " avoid 'list index out of range' error from lines[] in java_scanChar
+ " if b:bp >= b:buflen
+ " return s:EOI
+ " endif
+
+ " initialize scanner
+ call s:scanChar() " be ready for scanning
+ call s:nextToken() " prime the pump
+endfu
+
+fu! java_parser#FreeParser()
+ for varname in keys(s:PROTOTYPE)
+ exe "if exists(" . string(varname) . ") | unlet " . varname . " | endif"
+ endfor
+endfu
+
+fu! java_parser#GetSnapshot()
+ let snapshot = {}
+ for varname in keys(s:PROTOTYPE)
+ exe "let snapshot[" . string(varname) . "] = " . varname
+ endfor
+ return snapshot
+endfu
+
+fu! java_parser#Restore(snapshot)
+ for key in keys(a:snapshot)
+ exe "let " . key . "=" . string(a:snapshot[key])
+ endfor
+endfu
+
+" move b:bp and b:pos to the specified position
+fu! java_parser#GotoPosition(pos)
+ let b:bp = a:pos-1
+ let b:pos = a:pos
+ let p = java_parser#DecodePos(b:bp)
+ let b:line = p.line
+ let b:col = p.col
+ call s:scanChar()
+ call s:nextToken()
+endfu
+
+fu! java_parser#compilationUnit()
+ return s:compilationUnit()
+endfu
+
+fu! java_parser#block()
+ return s:block()
+endfu
+
+fu! java_parser#statement()
+ return s:blockStatements()
+endfu
+
+fu! java_parser#expression()
+ return s:expression()
+endfu
+
+fu! java_parser#nextToken()
+ return s:nextToken()
+endfu
+
+
+" Tree {{{1
+let s:TTree = {'tag': '', 'pos': 0} " Root class for AST nodes.
+
+" Tree maker functions {{{2
+fu! s:ClassDef(pos, mods, ...)
+ return {'tag': 'CLASSDEF', 'pos': a:pos, 'mods': a:mods, 'name': ''}
+endfu
+
+fu! s:VarDef(pos, mods, name, vartype)
+ return {'tag': 'VARDEF', 'pos': a:pos, 'mods': a:mods, 'name': a:name, 'vartype': a:vartype}
+endfu
+
+fu! s:Unary(pos, opcode, arg)
+ return {'tag': a:opcode, 'pos': a:pos, 'arg': a:arg}
+endfu
+
+fu! s:Binary(pos, opcode, lhs, rhs, ...)
+ return {'tag': a:opcode, 'pos': a:pos, 'lhs': a:lhs, 'rhs': a:rhs}
+endfu
+
+fu! s:TypeCast(pos, clazz, expr)
+ return {'tag': 'TYPECAST', 'pos': a:pos, 'clazz': a:clazz, 'expr': a:expr}
+endfu
+
+fu! s:Select(pos, selected, name)
+ return {'tag': 'SELECT', 'pos': a:pos, 'selected': a:selected, 'name': a:name}
+endfu
+
+fu! s:Ident(pos, name)
+ return {'tag': 'IDENT', 'pos': a:pos, 'name': a:name}
+endfu
+
+fu! s:TypeArray(pos, elementtype)
+ return {'tag': 'TYPEARRAY', 'pos': a:pos, 'elementtype': a:elementtype}
+endfu
+
+fu! s:Modifiers(pos, flags, annotations)
+ let noFlags = s:BitAnd(a:flags, s:Flags.StandardFlags) == 0
+ let mods = {'tag': 'MODIFIERS', 'flags': a:flags}
+ let mods.pos = noFlags && empty(a:annotations) ? -1 : a:pos
+ if !empty(a:annotations)
+ let mods.annotations = a:annotations
+ endif
+ return mods
+endfu
+
+
+" {{{2
+fu! java_parser#IsStatement(tree)
+ return has_key(a:tree, 'tag') && a:tree.tag =~# '^\(CLASSDEF\|VARDEF\|BLOCK\|IF\|DOLOOP\|WHILELOOP\|FORLOOP\|FOREACHLOOP\|SWITCH\|TRY\|EXEC\|LABELLED\|SYNCHRONIZED\|CASE\|BREAK\|RETURN\|SKIP\|THROW\|ASSERT\|CONTINUE\)$'
+endfu
+
+" Tree Helper {{{1
+fu! java_parser#type2Str(type)
+ if type(a:type) == type("")
+ return a:type
+ endif
+
+ let t = a:type
+ if t.tag == 'IDENTIFIER'
+ return t.value
+ elseif t.tag == 'IDENT'
+ return t.name
+ elseif t.tag == 'TYPEIDENT'
+ return t.typetag
+ elseif t.tag == 'SELECT'
+ return java_parser#type2Str(t.selected) . '.' . t.name
+ elseif t.tag == 'TYPEARRAY'
+ return java_parser#type2Str(t.elementtype) . '[]'
+ elseif t.tag == 'TYPEAPPLY'
+ return t.clazz.name
+ elseif t.tag == 'TEMPLATE'
+ let s = t.clazz.value . '<'
+ for arg in t.arguments
+ let s .= arg.value
+ endfor
+ let s .= '>'
+ return s
+ endif
+endfu
+
+fu! s:vardef2Str(vardef)
+ return java_parser#type2Str(a:vardef.vartype) . ' ' . a:vardef.name
+endfu
+
+fu! s:method2Str(methoddef)
+ let m = a:methoddef
+ let desc = m.r . ' ' . m.n . '('
+ for item in m.params
+ let desc .= s:vardef2Str(item) . ','
+ endfor
+ let desc = substitute(desc, ',$', '', '')
+ let desc .= ')'
+ return desc
+endfu
+
+" Scanner {{{1
+
+" nextToken() {{{2
+fu! s:nextToken()
+ let b:prevEndPos = b:endPos
+ let b:et_nextToken_count += 1
+ let b:sbuf = ''
+ while 1
+ let b:pos = b:bp
+ if b:ch =~ '[ \t\r\n ]' " - FF
+ " OAO optimized code: skip spaces
+ let b:col = match(b:lines[b:line], '[^ \t\r\n ]\|$', b:col)
+ let b:bp = b:idxes[b:line] + b:col
+ call s:scanChar()
+ let b:endPos = b:bp
+ continue
+
+ elseif b:ch =~ '[a-zA-Z$_]'
+ " read a identifier
+ call s:scanIdent()
+ return
+
+ elseif b:ch == '0'
+ call s:scanChar()
+ " hex
+ if b:ch == 'x' || b:ch == 'X'
+ call s:scanChar()
+ if b:ch == '.'
+ call s:scanHexFractionAndSuffix(0)
+ elseif s:isDigit(16)
+ call s:scanNumber(16)
+ else
+ call s:LexError("invalid.hex.number")
+ endif
+ " oct
+ else
+ let b:sbuf .= '0'
+ call s:scanNumber(8)
+ endif
+ return
+
+ elseif b:ch =~ '[1-9]'
+ " read number
+ call s:scanNumber(10)
+ return
+
+ elseif b:ch == '.'
+ call s:scanChar()
+ if b:ch =~ '[0-9]'
+ let b:sbuf .= '.'
+ call s:scanFractionAndSuffix()
+ " JAVA5 ELLIPSIS
+ elseif b:ch == '.'
+ let b:sbuf .= '..'
+ call s:scanChar()
+ if b:ch == '.'
+ call s:scanChar()
+ let b:sbuf .= '.'
+ let b:token = 'ELLIPSIS'
+ else
+ call s:LexError('malformed.fp.lit')
+ endif
+ else
+ let b:token = 'DOT'
+ endif
+ return
+
+ elseif b:ch =~ '[,;(){}[\]]'
+ let b:token = s:keywords[b:ch]
+ call s:scanChar()
+ return
+
+ elseif b:ch == '/'
+ let status = s:scanComment()
+ if status == 1
+ continue
+ elseif status == 2
+ return
+ elseif b:ch == '='
+ let b:name = '/='
+ let b:token = 'SLASHEQ'
+ call s:scanChar()
+ else
+ let b:name = '/'
+ let b:token = 'SLASH'
+ endif
+ return
+
+
+ elseif b:ch == "'"
+ call s:scanSingleQuote()
+ return
+
+ elseif b:ch == '"'
+ call s:scanDoubleQuote()
+ return
+
+ else
+ if s:IsSpecial(b:ch)
+ call s:scanOperator()
+ elseif b:ch =~ '[a-zA-Z_]'
+ call s:scanIdent()
+ elseif b:bp >= b:buflen
+ let b:token = 'EOF'
+ else
+ call s:LexError("illegal.char '" . b:ch . "'")
+ call s:scanChar()
+ endif
+ return
+ endif
+ endwhile
+endfu
+
+" scanChar() Read next character. {{{2
+" one buf version
+"fu! s:scanChar()
+" let b:bp += 1
+" if b:bp % 256 == 0
+" let b:buf2 = strpart(b:buf, b:bp, 256)
+" endif
+" let b:ch = b:buf2[b:bp % 256]
+"" let b:ch = b:buf[b:bp] " it will be extremely slow when buf is large
+" "call s:Trace( "'" . b:ch . "'" )
+"
+" if b:ch == "\r"
+" let b:line += 1
+" let b:col = 0
+" elseif b:ch == "\n"
+" if b:bp == 0 || b:buf[b:bp-1] == "\r"
+" let b:line += 1
+" let b:col = 0
+" endif
+" else
+" let b:col += 1
+" endif
+"endfu
+
+" Read next character. multiple lines version
+fu! s:scanChar()
+ let b:bp+=1
+ let b:ch=b:lines[b:line][b:col]
+ let b:col+=1
+ if b:ch==''
+ let b:ch="\r"
+ let b:line+=1
+ let b:col=0
+ endif
+
+ if b:ch == '\'
+ call s:convertUnicode()
+ endif
+endfu
+
+fu! java_parser#CharAt(line, col)
+ let ch=b:lines[a:line][a:col]
+ if ch == ''
+ let ch = "\r"
+ endif
+ return ch
+endfu
+
+fu! s:convertUnicode()
+ if b:ch == '\' && b:unicodeConversionBp != b:bp
+ "if java_parser#CharAt(b:bp+1) == 'u'
+ "call s:scanChar()
+ "endif
+ endif
+endfu
+
+" putChar() is substituted with
+" let b:sbuf .= '.'
+
+" scanIdent() {{{2
+" OAO optimized code
+fu! s:scanIdent()
+ let col_old = b:col
+ let b:col = match(b:lines[b:line], '[^a-zA-Z0-9$_]\|$', b:col)
+ let b:name = strpart(b:lines[b:line], col_old-1, b:col-col_old+1)
+ let b:token = get(s:keywords, b:name, 'IDENTIFIER')
+ call s:Debug('name: "' . b:name . '" of token type ' . b:token )
+ let b:bp = b:idxes[b:line] + b:col
+ call s:scanChar()
+endfu
+
+" Standard implementation
+fu! s:scanIdent_old()
+ " do ... while ()
+ let b:sbuf .= b:ch
+ call s:scanChar()
+ if b:ch !~ '[a-zA-Z0-9$_]' || b:bp >= b:buflen
+ let b:name = b:sbuf
+ let b:token = has_key(s:keywords, b:name) ? s:keywords[b:name] : 'IDENTIFIER'
+ call s:Debug('name: "' . b:name . '" of token type ' . b:token )
+ return
+ endif
+
+ while (1)
+ let b:sbuf .= b:ch
+ call s:scanChar()
+ if b:ch !~ '[a-zA-Z0-9$_]' || b:bp >= b:buflen
+ let b:name = b:sbuf
+ let b:token = has_key(s:keywords, b:name) ? s:keywords[b:name] : 'IDENTIFIER'
+ call s:Debug('name: "' . b:name . '" of token type ' . b:token )
+ break
+ endif
+ endwhile
+endfu
+
+" digit() {{{2
+" Convert an ASCII digit from its base (8, 10, or 16) to its value.
+" NOTE: This only implement isdigit() check
+fu! s:digit(base)
+ let c = b:ch
+ "let result =
+endfu
+
+fu! s:isDigit(base)
+ if a:base == 8
+ return b:ch =~ '[0-7]'
+ elseif a:base == 16
+ return b:ch =~ '[0-9a-fA-F]'
+ elseif a:base == 10
+ return b:ch =~ '[0-9]'
+ endif
+endfu
+
+" scanNumber() {{{2
+fu! s:scanNumber(radix)
+ let b:radix = a:radix
+ let digitRadix = a:radix <= 10 ? 10 : 16
+ let seendigit = 0
+ while s:isDigit(a:radix)
+ let seendigit = 1
+ let b:sbuf .= b:ch
+ call s:scanChar()
+ endwhile
+ if a:radix == 16 && b:ch == '.'
+ call s:scanHexFractionAndSuffix(seendigit)
+ elseif seendigit && a:radix == 16 && (b:ch == 'p' || b:ch == 'P')
+ call s:scanHexExponentAndSuffix()
+ elseif a:radix <= 10 && b:ch == '.'
+ let b:sbuf .= b:ch
+ call s:scanChar()
+ call s:scanFractionAndSuffix()
+ elseif a:radix <= 10 && b:ch =~ '[eEfFdD]'
+ call s:scanFractionAndSuffix()
+ else
+ if b:ch == 'l' || b:ch == 'L'
+ call s:scanChar()
+ let b:token = 'LONGLITERAL'
+ else
+ let b:token = 'INTLITERAL'
+ endif
+ endif
+endfu
+
+fu! s:scanHexExponentAndSuffix()
+ if b:ch == 'p' || b:ch == 'P'
+ let b:sbuf .= b:ch
+ call s:scanChar()
+ if b:ch == '+' || b:ch == '-'
+ let b:sbuf .= b:ch
+ call s:scanChar()
+ endif
+
+ if '0' <= b:ch && b:ch <= '9'
+ let b:sbuf .= b:ch
+ call s:scanChar()
+ while '0' <= b:ch && b:ch <= '9'
+ let b:sbuf .= b:ch
+ call s:scanChar()
+ endwhile
+
+ "if !b:allowHexFloats
+ "elseif !b:hexFloatsWork
+ " call s:LexError("unsupported.cross.fp.lit")
+ "endif
+ else
+ call s:LexError("malformed.fp.lit")
+ endif
+ else
+ call s:LexError("malformed.fp.lit")
+ endif
+
+ if b:ch == 'f' || b:ch == 'F'
+ let b:sbuf .= b:ch
+ call s:scanChar()
+ let b:token = 'FLOATLITERAL'
+ else
+ if b:ch == 'f' || b:ch == 'F'
+ let b:sbuf .= b:ch
+ call s:scanChar()
+ endif
+ let b:token = 'DOUBLELITERAL'
+ endif
+endfu
+
+fu! s:scanFraction()
+ " scan fraction
+ while b:ch =~ '[0-9]'
+ let b:sbuf .= b:ch
+ call s:scanChar()
+ endwhile
+
+ " floating point number
+ if b:ch == 'e' || b:ch == 'E'
+ let b:sbuf .= b:ch
+ call s:scanChar()
+
+ if b:ch == '+' || b:ch == '-'
+ let b:sbuf .= b:ch
+ call s:scanChar()
+ endif
+
+ if b:ch =~ '[0-9]'
+ let b:sbuf .= b:ch
+ call s:scanChar()
+ while b:ch =~ '[0-9]'
+ let b:sbuf .= b:ch
+ call s:scanChar()
+ endwhile
+ return
+ endif
+
+ call s:LexError("malformed.fp.lit")
+ endif
+endfu
+
+" Read fractional part and 'd' or 'f' suffix of floating point number.
+fu! s:scanFractionAndSuffix()
+ call s:scanFraction()
+ if b:ch == 'f' || b:ch == 'F'
+ let b:sbuf .= b:ch
+ call s:scanChar()
+ let b:token = 'FLOATLITERAL'
+ else
+ if b:ch == 'd' || b:ch == 'D'
+ let b:sbuf .= b:ch
+ call s:scanChar()
+ endif
+ let b:token = 'DOUBLELITERAL'
+ endif
+endfu
+
+fu! s:scanHexFractionAndSuffix(seendigit)
+ let seendigit = a:seendigit
+ let b:radix = 16
+ if b:ch != '.' | echoerr "b:ch != '.'" | endif
+
+ let b:sbuf .= b:ch
+ call s:scanChar()
+ while s:isDigit(16)
+ let seendigit = 1
+ let b:sbuf .= b:ch
+ call s:scanChar()
+ endwhile
+
+ if !seendigit
+ call s:LexError("invalid.hex.number")
+ else
+ call s:scanHexExponentAndSuffix()
+ endif
+endfu
+
+" scanLitChar() {{{2
+fu! s:scanLitChar()
+ if b:ch == '\'
+ call s:scanChar()
+ if b:ch =~ '[0-7]'
+ let leadch = b:ch
+ let oct = b:ch
+ call s:scanChar()
+ if b:ch =~ '[0-7]'
+ let oct = oct * 8 + b:ch
+ call s:scanChar()
+ if leadch <= '3' && '0' <= b:ch && b:ch <= '7'
+ let oct = oct * 8 + b:ch
+ call s:scanChar()
+ endif
+ endif
+ let b:sbuf .= oct
+
+ elseif b:ch == "'" || b:ch =~ '[btnfr"\\]'
+ let b:sbuf .= b:ch
+ call s:scanChar()
+
+ " unicode escape
+ elseif b:ch == 'u'
+ while b:ch =~ '[a-zA-Z0-9]'
+ call s:scanChar()
+ endwhile
+
+ else
+ call s:LexError("illegal.esc.char")
+ endif
+
+ elseif b:bp < b:buflen
+ let b:sbuf .= b:ch
+ call s:scanChar()
+ endif
+endfu
+
+" scanOperator() {{{2
+fu! s:scanOperator()
+ while 1
+ if !has_key(s:keywords, b:sbuf . b:ch)
+ break
+ endif
+
+ let b:sbuf .= b:ch
+ let b:token = get(s:keywords, b:sbuf, 'IDENTIFIER')
+ call s:Debug('sbuf: "' . b:sbuf . '" of token type ' . b:token )
+ call s:scanChar()
+ if !s:IsSpecial(b:ch)
+ break
+ endif
+ endwhile
+endfu
+
+" NOTE: add @ for JAVA5
+fu! s:IsSpecial(ch)
+ return a:ch =~ '[!%&*?+-:<=>^|~@]'
+endfu
+
+" scan comment {{{2
+" return 0 - not comment, 1 - succeeded to scan comment, 2 - unclosed comment
+fu! s:scanComment()
+ call s:scanChar()
+ " line comment
+ if b:ch == '/'
+ let b:token = 'LINECOMMENT'
+ call s:Info('line comment')
+ call s:SkipLineComment()
+ let b:endPos = b:bp
+ return 1
+
+ " classic comment
+ " test cases: /**/, /***/, /*******/, /*** astatement; /***/, /*/
+ elseif b:ch == '*'
+ let b:token = 'BLOCKCOMMENT'
+ call s:Info('block comment')
+ call s:scanChar()
+ let time = reltime()
+ " javadoc
+ if b:ch == '*'
+ let b:docComment = s:scanDocComment()
+ " normal comment
+ else
+ call s:skipComment()
+ endif
+ let b:et_perf .= "\r" . 'comment ' . reltimestr(reltime(time))
+
+ if b:ch == '/'
+ call s:Info('end block comment')
+ call s:scanChar()
+ let b:endPos = b:bp
+ return 1
+ else
+ call s:LexError('unclosed.comment')
+ return 2
+ endif
+ endif
+ return 0
+endfu
+
+fu! s:SkipLineComment()
+ " OAO optimized code
+ let b:ch = "\r"
+ let b:line += 1
+ let b:col = 0
+ let b:bp = b:idxes[b:line] + b:col
+
+ " OLD
+ "call s:scanChar()
+ "while (b:ch != "\r")
+ " call s:scanChar()
+ "endwhile
+endfu
+
+fu! s:skipComment()
+ if b:ch == '*'
+ call s:scanChar()
+ if b:ch == '/'
+ return
+ else " NOTE: go back
+ let b:ch = '*'
+ let b:bp -= 1
+ let b:col -= 1
+ endif
+ endif
+
+ " OAO optimized code
+ if s:Stridx('*/') > -1
+ call s:scanChar()
+ endif
+
+" " Standard implementation
+" while b:bp < b:buflen
+" if b:ch == '*'
+" call s:scanChar()
+" if b:ch == '/'
+" break
+" endif
+" else
+" call s:scanChar()
+" endif
+" endwhile
+endfu
+
+fu! s:scanDocComment()
+ call s:Info('It is javadoc')
+ return s:skipComment()
+
+ " skip star '*'
+ while (b:bp < b:buflen && b:ch == '*')
+ call s:scanChar()
+ endwhile
+
+ if b:bp < b:buflen && b:ch == '/'
+ return ''
+ endif
+
+ let result = ''
+ while b:bp < b:buflen
+ if b:ch == '*'
+ call s:scanChar()
+ if b:ch == '/'
+ break
+ else
+ let result .= b:ch
+ endif
+ else
+ call s:scanChar()
+ let result .= b:ch
+ endif
+ endwhile
+
+ return result
+endfu
+
+" scan single quote {{{2
+fu! s:scanSingleQuote()
+ call s:scanChar()
+ if (b:ch == "'")
+ call s:LexError("empty.char.lit")
+ else
+ if (b:ch =~ '[\r\n]')
+ call s:LexError("illegal.line.end.in.char.lit")
+ endif
+
+ call s:scanLitChar()
+ if b:ch == "'"
+ call s:scanChar()
+ let b:token = 'CHARLITERAL'
+ else
+ call s:LexError("unclosed.char.lit")
+ endif
+ endif
+endfu
+
+" scan double quote {{{2
+" test cases:
+" 'a"";'
+" 'a"b,c";'
+" 'a"b,\"c";'
+" 'a"b,\\"c";'
+" 'a"b,\\\"c";'
+" 'a"b,\\\\"c";'
+" 'a"b,\\\"c;' " NOTE: cannot handle
+fu! s:scanDoubleQuote()
+ if match(b:lines[b:line], '\\"', b:col) == -1
+ let idx = matchend(b:lines[b:line], '\(\\\(["\\''ntbrf]\)\|[^"]\)*"', b:col)
+ if idx != -1
+ let b:sbuf = strpart(b:lines[b:line], b:col, idx-b:col-1)
+ let b:col = idx-1 " back to the end
+ let b:bp = b:idxes[b:line] + b:col
+ call s:scanChar()
+ let b:token = 'STRINGLITERAL'
+ else
+ call s:LexError("unclosed.str.lit")
+ endif
+ call s:scanChar()
+ return
+ endif
+
+
+ call s:scanChar()
+ while b:ch !~ '["\r\n]' && b:bp < b:buflen
+ call s:scanLitChar()
+ endwhile
+
+ if b:ch == '"'
+ let b:token = 'STRINGLITERAL'
+ call s:scanChar()
+ else
+ call s:LexError("unclosed.str.lit")
+ endif
+endfu
+
+" lex errors {{{2
+fu! s:LexError(key, ...)
+ let pos = a:0 == 0 ? b:pos : a:1
+ let b:token = 'ERROR'
+ let b:errPos = pos
+ call s:Log(4, b:pos, '[lex error]:' . s:Pos2Str(pos) . ': ' . a:key)
+endfu
+
+" Scanner Helper {{{1
+" gotoMatchEnd {{{2
+fu! s:gotoMatchEnd(one, another, ...)
+ while b:bp < b:buflen
+ if b:ch == a:another
+ call s:scanChar()
+ if has_key(s:keywords, a:another)
+ let b:token = s:keywords[a:another]
+ else
+ echoerr '<strange>'
+ endif
+ break
+
+ elseif b:ch == a:one
+ call s:scanChar()
+ call s:gotoMatchEnd(a:one, a:another)
+
+ " skip commment
+ elseif b:ch == '/'
+ call s:scanComment()
+
+ " skip literal character
+ elseif b:ch == "'"
+ call s:scanSingleQuote()
+
+ " skip literal string
+ elseif b:ch == '"'
+ call s:scanDoubleQuote()
+
+ else
+ " OAO
+ call s:Match('[' . a:one . a:another . '/"'']')
+ " OLD
+ "call s:scanChar()
+ endif
+ endwhile
+
+ " For such situation: after accept one token, the next token is just the same.
+ let nextTokenIsLBRACE = a:0 == 0 ? 0 : a:1
+ if nextTokenIsLBRACE
+ call s:gotoMatchEnd(a:one, a:another)
+ endif
+
+ return b:bp
+endfu
+
+" gotoSemi {{{2
+fu! s:gotoSemi()
+ while b:bp < b:buflen
+ if b:ch == ';'
+ let b:pos = b:bp
+ call s:scanChar()
+ let b:token = 'SEMI'
+ return
+
+ " skip commment
+ elseif b:ch == '/'
+ call s:scanComment()
+
+ " skip literal character
+ elseif b:ch == "'"
+ call s:scanSingleQuote()
+
+ " skip literal string
+ elseif b:ch == '"'
+ call s:scanDoubleQuote()
+
+ elseif b:ch == '{'
+ call s:scanChar()
+ call s:gotoMatchEnd('{', '}')
+
+ elseif b:ch == '('
+ call s:scanChar()
+ call s:gotoMatchEnd('(', ')')
+
+ elseif b:ch == '['
+ call s:scanChar()
+ call s:gotoMatchEnd('[', ']')
+
+ else
+ " OAO
+ call s:Match('[;({[/"'']')
+ " OLD
+ "call s:scanChar()
+ endif
+ endwhile
+endfu
+
+" s:Strpart(), s:Stridx(), s:Match() {{{2
+fu! Strpart(start, len)
+ let startline = java_parser#DecodePos(a:start).line
+ let endline = java_parser#DecodePos(a:start + a:len).line
+ let str = join(b:lines[startline:endline-1]) . b:lines[endline]
+ return strpart(str, a:start-b:idxes[startline], a:len)
+endfu
+
+fu! s:Stridx(needle)
+ let found = 0
+ while b:line < len(b:lines)-1
+ let idx = stridx(b:lines[b:line], a:needle, b:col)
+ if idx > -1
+ let found = 1
+ let b:col = idx
+ break
+ endif
+ let b:line += 1
+ let b:col = 0
+ endwhile
+
+ if found
+ let b:bp = b:idxes[b:line] + b:col
+ call s:scanChar()
+ return b:bp
+ else
+ let b:bp = b:buflen
+ let b:ch = s:EOI
+ return -1
+ endif
+endfu
+
+fu! s:Match(pat)
+ let bp_old = b:bp
+ let line_old = b:line
+ let col_old = b:col
+
+ let found = 0
+ while b:line < len(b:lines)-1
+ let idx = match(b:lines[b:line], a:pat, b:col)
+ if idx > -1
+ let found = 1
+ let b:col = idx
+ break
+ endif
+ let b:line += 1
+ let b:col = 0
+ endwhile
+
+ if found
+ let b:bp = b:idxes[b:line] + b:col-1
+ call s:scanChar()
+ return b:bp
+ else
+ let b:bp = bp_old
+ let b:line = line_old
+ let b:col = col_old
+ call s:scanChar()
+ return -1
+ endif
+endfu
+
+
+" conversion between position and (line, col) {{{2
+fu! java_parser#MakePos(line, col)
+ return b:idxes[a:line] + a:col
+endfu
+
+fu! java_parser#DecodePos(pos)
+ let line = -1
+ for idx in b:idxes
+ if idx > a:pos
+ break
+ endif
+ let line += 1
+ endfor
+ let col = a:pos - b:idxes[line]
+ return {'line': line, 'col': col}
+endfu
+
+fu! s:Pos2Str(pos)
+ let o = java_parser#DecodePos(a:pos)
+ return '(' . (o.line+1) . ',' . (o.col+1) . ')'
+endfu
+
+" Bitwise operator emulators {{{1
+" NOTE: For more than 32 bit number, use the string bits form.
+
+" bit operator ~
+fu! s:BitNot(v)
+ return s:Bits2Number( s:BitNot_binary(s:Number2Bits(a:v)) )
+endfu
+
+fu! s:BitNot_binary(v)
+ let v = substitute(a:v, '^0*\([01]\+\)', '\1', 'g')
+ let v = substitute(v, '1', '2', 'g')
+ let v = substitute(v, '0', '1', 'g')
+ let v = substitute(v, '2', '0', 'g')
+ return v
+endfu
+
+" bit operator &
+fu! s:BitAnd(n1, n2)
+ if a:n1 == 0 || a:n2 == 0 | return 0 | endif
+ if a:n1 == a:n2 | return 1 | endif
+ return s:Bits2Number( s:BitOperator_binary(s:Number2Bits(a:n1), s:Number2Bits(a:n2), 'n1[i] == "1" && n2[i] == "1"') )
+endfu
+
+" bit operator |
+fu! s:BitOr(n1, n2, ...)
+ if a:0 == 0
+ if a:n1 == 0
+ return a:n2
+ elseif a:n2 == 0
+ return a:n1
+ endif
+ endif
+ let result = s:BitOperator_binary(s:Number2Bits(a:n1), s:Number2Bits(a:n2), 'n1[i] == "1" || n2[i] == "1"')
+ for a in a:000
+ let result = s:BitOperator_binary(result, s:Number2Bits(a), 'n1[i] == "1" || n2[i] == "1"')
+ endfor
+ return s:Bits2Number( result )
+endfu
+
+" bit operator ^
+fu! s:BitXor(n1, n2)
+ if a:n1 == a:n2 | return 0 | endif
+ return s:Bits2Number( s:BitOperator_binary(s:Number2Bits(a:n1), s:Number2Bits(a:n2), 'n1[i] != n2[i]') )
+endfu
+
+fu! s:BitOperator_binary(n1, n2, comparator)
+ let n1 = a:n1
+ let n2 = a:n2
+
+ let len1 = len(n1)
+ let len2 = len(n2)
+ let len = len1
+ if len1 > len2
+ let n2 = repeat('0', len1-len2) . n2
+ else
+ let len = len2
+ let n1 = repeat('0', len2-len1) . n1
+ endif
+
+ let i = 0
+ let bits = ''
+ while i < len
+ let bits .= eval(a:comparator) ? '1' : '0'
+ let i += 1
+ endwhile
+ return bits
+endfu
+
+" bit operator <<
+fu! s:BitMoveLeft()
+endfu
+
+" bit operator >>
+fu! s:BitMoveRight()
+endfu
+
+" helper function: convert a number to a string consisted of '0' or '1' indicating number
+fu! s:Number2Bits(n, ...)
+ if type(a:n) == type("") | return a:n | endif
+ if a:n == 0 | return '0' | endif
+
+ let n = a:n
+ let bits = ''
+ while n != 0
+ let bit = n % 2
+ "echo 'n: ' . n . ' bit: ' . bit
+ let bits = bit . bits
+ let n = (n-bit)/ 2
+ endwhile
+ if a:0 > 0
+ let bits = repeat(a:1 - len(bits)) . bits
+ endif
+ return bits
+endfu
+
+" helper function: convert a string consisted of '0' or '1' indicating number to a number
+" precondition: bits must not be empty string
+fu! s:Bits2Number(bits)
+ let len = len(a:bits)
+ let n = a:bits[0]
+ let i = 1
+ while i < len
+ let n = n * 2 + a:bits[i]
+ let i += 1
+ endwhile
+ return n
+endfu
+
+
+let s:modifier_keywords = ['strictfp', 'abstract', 'interface', 'native', 'transient', 'volatile', 'synchronized', 'final', 'static', 'protected', 'private', 'public']
+fu! s:String2Flags(str)
+ let mod = [0,0,0,0,0,0,0,0,0,0,0,0,]
+ for item in split(a:str, '\s\+')
+ if index(s:modifier_keywords, item) != -1
+ let mod[index(s:modifier_keywords, item)] = '1'
+ endif
+ endfor
+ return join(mod[index(mod, '1'):], '')
+endfu
+
+" Log utilities {{{1
+" level
+" 5 off/fatal
+" 4 error
+" 3 warn
+" 2 info
+" 1 debug
+" 0 trace
+fu! java_parser#SetLogLevel(level)
+ let b:loglevel = a:level
+endfu
+
+fu! java_parser#GetLogLevel()
+ return exists('b:loglevel') ? b:loglevel : 3
+endfu
+
+fu! java_parser#GetLogContent()
+ return b:log
+endfu
+
+fu! s:Trace(msg)
+ call s:Log(0, b:pos, a:msg)
+endfu
+
+fu! s:Debug(msg)
+ call s:Log(1, b:pos, a:msg)
+endfu
+
+fu! s:Info(msg)
+ call s:Log(2, b:pos, a:msg)
+endfu
+
+fu! s:Log(level, pos, key, ...)
+ if a:level >= java_parser#GetLogLevel()
+ echo a:key
+ call add(b:log, a:key)
+ endif
+endfu
+
+fu! s:ShowWatch(...)
+ let at = a:0 > 0 ? a:1 : ''
+ echo '-- b:bp ' . b:bp . string(java_parser#DecodePos(b:bp)) . ' b:ch "' . b:ch . '" b:name ' . b:name . ' b:token ' . b:token . ' b:pos ' .b:pos . ' endPos ' . b:endPos . ' prevEndPos ' . b:prevEndPos . ' errPos ' . b:errPos . ' errorEndPos ' . b:errorEndPos . at
+endfu
+
+fu! java_parser#Exe(cmd)
+ exe a:cmd
+endfu
+
+" Parser {{{1
+" skip() Skip forward until a suitable stop token is found. {{{2
+fu! s:skip(stopAtImport, stopAtMemberDecl, stopAtIdentifier, stopAtStatement)
+ while 1
+ if b:token == 'SEMI'
+ call s:nextToken()
+ return
+ elseif b:token =~# '^\(PUBLIC\|FINAL\|ABSTRACT\|MONKEYS_AT\|EOF\|CLASS\|INTERFACE\|ENUM\)$'
+ return
+ elseif b:token == 'IMPORT'
+ if a:stopAtImport
+ return
+ endif
+ elseif b:token =~# '^\(LBRACE\|RBRACE\|PRIVATE\|PROTECTED\|STATIC\|TRANSIENT\|NATIVE\|VOLATILE\|SYNCHRONIZED\|STRICTFP\|LT\|BYTE\|SHORT\|CHAR\|INT\|LONG\|FLOAT\|DOUBLE\|BOOLEAN\|VOID\)$'
+ if a:stopAtMemberDecl
+ return
+ endif
+ elseif b:token == 'IDENTIFIER'
+ if a:stopAtIdentifier
+ return
+ endif
+ elseif b:token =~# '^\(CASE\|DEFAULT\|IF\|FOR\|WHILE\|DO\|TRY\|SWITCH\|RETURN\|THROW\|BREAK\|CONTINUE\|ELSE\|FINALLY\|CATCH\)$'
+ if a:stopAtStatement
+ return
+ endif
+ endif
+ call s:nextToken()
+ endwhile
+endfu
+
+" syntax errors {{{2
+fu! s:SyntaxError(key, ...)
+ let pos = a:0 == 0 ? b:pos : a:1
+ let errs = a:0 > 1 ? a:2 : []
+ call s:setErrorEndPos(pos)
+ call s:ReportSyntaxError(pos, a:key)
+ return {'tag': 'ERRONEOUS', 'pos': pos, 'errs': errs}
+endfu
+
+fu! s:ReportSyntaxError(pos, key, ...)
+ if a:pos > b:errPos || a:pos == -1
+ let key = a:key . (b:token == 'EOF' ? ' and premature.eof' : '')
+ call s:Log(4, a:pos, '[syntax error]' . s:Pos2Str(a:pos) . ': ' . key)
+ endif
+ let b:errPos = a:pos
+endfu
+
+" accept() {{{2
+fu! s:accept(token_type)
+ "call s:Debug(b:token . ' == ' . a:token_type . (b:token == a:token_type))
+ if b:token == a:token_type
+ call s:nextToken()
+ else
+ call s:setErrorEndPos(b:pos)
+ call s:ReportSyntaxError(b:prevEndPos, s:token2string(a:token_type) . " expected")
+ "call s:nextToken()
+ endif
+endfu
+
+fu! s:token2string(token)
+ if a:token =~# '^\(DOT\|COMMA\|SEMI\|LPAREN\|RPAREN\|LBRACKET\|RBRACKET\|LBRACE\|RBRACE\)$'
+ return "'" . a:token . "'"
+ endif
+ return a:token
+endfu
+
+
+" illegal() {{{2
+fu! s:illegal(...)
+ call s:setErrorEndPos(b:pos)
+ return s:SyntaxError(s:modeAndEXPR() ? 'illegal.start.of.expr' : 'illegal.start.of.type', a:0 == 0 ? b:pos : a:1)
+endfu
+
+" setErrorEndPos() {{{2
+fu! s:setErrorEndPos(errPos)
+ if a:errPos > b:errorEndPos
+ let b:errorEndPos = a:errPos
+ endif
+endfu
+
+" ident() {{{2
+" Ident = IDENTIFIER
+fu! s:ident()
+ call s:Trace('s:ident ' . b:token)
+
+ if b:token == 'IDENTIFIER'
+ let name = b:name
+ call s:nextToken()
+ return name
+
+ elseif b:token == 'ASSERT'
+ if s:allowAsserts
+ call s:Log(4, b:pos, 'assert.as.identifier')
+ call s:nextToken()
+ return '<error>'
+ else
+ call s:Log(3, b:pos, 'assert.as.identifier')
+ let name = b:name
+ call s:nextToken()
+ return name
+ endif
+
+ elseif b:token == 'ENUM'
+ if b:allowEnums
+ call s:Log(4, b:pos, 'enum.as.identifier')
+ call s:nextToken()
+ return '<error>'
+ else
+ call s:Log(3, b:pos, 'enum.as.identifier')
+ let name = b:name
+ call s:nextToken()
+ return name
+ endif
+
+ else
+ call s:accept('IDENTIFIER')
+ return '<error>'
+ endif
+endfu
+
+" qualident() {{{2
+" Qualident = Ident { DOT Ident }
+fu! s:qualident()
+ let t = s:Ident(b:pos, s:ident())
+ while b:token == 'DOT'
+ let pos = b:pos
+ call s:nextToken()
+ let t = s:Select(b:pos, t, s:ident())
+ "let t.name .= '.' . s:ident() " FIXME
+ endwhile
+ return t
+endfu
+
+" literal() {{{2
+" Literal = INTLITERAL | LONGLITERAL | FLOATLITERAL | DOUBLELITERAL | CHARLITERAL | STRINGLITERAL | TRUE | FALSE | NULL
+fu! s:literal(prefix)
+ let t = {'tag': 'LITERAL', 'pos': b:pos}
+ if b:token == 'INTLITERAL'
+ let t.typetag = 'INT'
+ let t.value = b:sbuf
+ elseif b:token == 'LONGLITERAL'
+ let t.typetag = 'LONG'
+ let t.value = b:sbuf
+ elseif b:token == 'FLOATLITERAL'
+ let t.typetag = 'FLOAT'
+ let t.value = b:sbuf
+ elseif b:token == 'DOUBLELITERAL'
+ let t.typetag = 'DOUBLE'
+ let t.value = b:sbuf
+ elseif b:token == 'CHARLITERAL'
+ let t.typetag = 'CHAR'
+ let t.value = b:sbuf
+ elseif b:token == 'STRINGLITERAL'
+ let t.typetag = 'CLASS'
+ let t.value = b:sbuf
+ elseif b:token == 'TRUE'
+ let t.typetag = 'BOOLEAN'
+ let t.value = 1
+ elseif b:token == 'FALSE'
+ let t.typetag = 'BOOLEAN'
+ let t.value = 0
+ elseif b:token == 'NULL'
+ let t.typetag = 'BOT'
+ let t.value = 'null'
+ else
+ echoerr 'can not reach here'
+ endif
+ call s:nextToken()
+ return t
+endfu
+
+" {{{2
+" terms, expression, type {{{2
+" When terms are parsed, the mode determines which is expected:
+" mode = EXPR : an expression
+" mode = TYPE : a type
+" mode = NOPARAMS : no parameters allowed for type
+" mode = TYPEARG : type argument
+let s:EXPR = 1
+let s:TYPE = 2
+let s:NOPARAMS = 4
+let s:TYPEARG = 8
+let s:EXPR_OR_TYPE = 3
+let s:EXPR_OR_TYPE_OR_NOPARAMS = 7
+let s:TYPEARG_OR_NOPARAMS = 12
+
+fu! s:modeAndEXPR()
+ return b:mode % 2
+endfu
+
+fu! s:modeAndTYPE()
+ return s:BitAnd(b:mode, s:TYPE)
+endfu
+
+" terms can be either expressions or types.
+fu! s:expression()
+ return s:term(s:EXPR)
+endfu
+
+fu! s:type()
+ return s:term(s:TYPE)
+endfu
+
+fu! s:typeList()
+ let ts = []
+ call add(ts, s:type())
+ while b:token == 'COMMA'
+ call s:nextToken()
+ call add(ts, s:type())
+ endwhile
+ return ts
+endfu
+
+" Expression = Expression1 [ExpressionRest]
+" ExpressionRest = [AssignmentOperator Expression1]
+" AssignmentOperator = "=" | "+=" | "-=" | "*=" | "/=" | "&=" | "|=" | "^=" |
+" "%=" | "<<=" | ">>=" | ">>>="
+" Type = Type1
+" TypeNoParams = TypeNoParams1
+" StatementExpression = Expression
+" ConstantExpression = Expression
+fu! s:term(...)
+ let prevmode = b:mode
+ let b:mode = a:0 == 0 ? b:mode : a:1
+
+ let t = s:term1()
+ if s:modeAndEXPR() && b:token == 'EQ' || b:token =~# '^\(PLUSEQ\|SUBEQ\|STAREQ\|SLASHEQ\|AMPEQ\|BAREQ\|CARETEQ\|PERCENTEQ\|LTLTEQ\|GTGTEQ\|GTGTGTEQ\)$'
+ let t = s:termRest(t)
+ endif
+
+ let b:lastmode = b:mode
+ let b:mode = prevmode
+ return t
+endfu
+
+fu! s:termRest(t)
+ if b:token == 'EQ'
+ let pos = b:pos
+ call s:nextToken()
+ let b:mode = s:EXPR
+ return {'tag': 'ASSIGN', 'pos': pos, 'lhs': a:t, 'rhs': s:term()}
+
+ elseif b:token =~# '^\(PLUSEQ\|SUBEQ\|STAREQ\|SLASHEQ\|PERCENTEQ\|AMPEQ\|BAREQ\|CARETEQ\|LTLTEQ\|GTGTEQ\|GTGTGTEQ\)$'
+ let pos = b:pos
+ let token = b:token
+ call s:nextToken()
+ let b:mode = s:EXPR
+ return {'tag': s:optag(token), 'pos': pos, 'lhs': a:t, 'rhs': s:term()}
+
+ else
+ return a:t
+ endif
+endfu
+
+" Expression1 = Expression2 [Expression1Rest]
+" Type1 = Type2
+" TypeNoParams1 = TypeNoParams2
+fu! s:term1()
+ let t = s:term2()
+ if s:modeAndEXPR() && b:token == 'QUES'
+ let b:mode = s:EXPR
+ return s:term1Rest(t)
+ else
+ return t
+ endif
+endfu
+
+" Expression1Rest = ["?" Expression ":" Expression1]
+fu! s:term1Rest(t)
+ if b:token == 'QUES'
+ let t = {'tag': 'CONDEXPR', 'pos': b:pos, 'cond': a:t}
+ call s:nextToken()
+ let t.truepart = s:term()
+ call s:accept('COLON')
+ let t.falsepart = s:term1()
+ return t
+ else
+ return a:t
+ endif
+endfu
+
+" Expression2 = Expression3 [Expression2Rest]
+" Type2 = Type3
+" TypeNoParams2 = TypeNoParams3
+fu! s:term2()
+ let t = s:term3()
+ if s:modeAndEXPR() && s:prec(b:token) >= s:opprecedences.orPrec
+ let b:mode = s:EXPR
+ return s:term2Rest(t, s:opprecedences.orPrec)
+ else
+ return t
+ endif
+endfu
+
+" Expression2Rest = {infixop Expression3}
+"" | Expression3 instanceof Type
+" infixop = "||"
+" | "&&"
+" | "|"
+" | "^"
+" | "&"
+" | "==" | "!="
+" | "<" | ">" | "<=" | ">="
+" | "<<" | ">>" | ">>>"
+" | "+" | "-"
+" | "*" | "/" | "%"
+fu! s:term2Rest(t, minprec)
+ let odStack = [a:t] " for expressions
+ let opStack = [] " for tokens
+ let top = 0
+ let startPos = b:pos
+ let topOp = 'ERROR'
+ while s:prec(b:token) >= a:minprec
+ call add(opStack, topOp)
+ let top += 1
+ let topOp = b:token
+ let pos = b:pos
+ call s:nextToken()
+ call add(odStack, topOp == 'INSTANCEOF' ? s:type() : s:term3())
+ while top > 0 && s:prec(topOp) >= s:prec(b:token)
+ let odStack[top-1] = s:makeOp(pos, topOp, odStack[top-1], odStack[top])
+ let top -= 1
+ let topOp = opStack[top]
+ endwhile
+ endwhile
+ "assert top == 0
+ let t = odStack[0]
+
+ if t.tag == 'PLUS'
+ let buf = s:foldStrings(t)
+ if buf != ''
+ let t = {'tag': 'LITERAL', 'pos': startPos, 'typetag': 'CLASS', 'value': t}
+ endif
+ endif
+ return t
+endfu
+
+fu! s:makeOp(pos, topOp, od1, od2)
+ if a:topOp == 'INSTANCEOF'
+ return {'tag': 'TYPETEST', 'pos': a:pos, 'expr': a:od1, 'clazz': a:od2}
+ else
+ return s:Binary(a:pos, s:optag(a:topOp), a:od1, a:od2)
+ endif
+endfu
+
+fu! s:foldStrings(tree)
+ let tree = a:tree
+ let buf = ''
+ while 1
+ if tree.tag == 'LITERAL'
+ let lit = tree
+ if lit.typetag == 'CLASS'
+ let sbuf = lit.value
+ if buf != ''
+ let sbuf .= buf
+ endif
+ return sbuf
+ endif
+ elseif tree.tag == 'PLUS'
+ let op = tree
+ if op.rhs.tag == 'LITERAL'
+ let lit = op.rhs
+ if lit.typetag == 'CLASS'
+ let buf = lit.value . buf
+ let tree = op.lhs
+ continue
+ endif
+ endif
+ endif
+ return ''
+ endwhile
+endfu
+
+" Expression3 = PrefixOp Expression3 {{{2
+" | "(" Expr | TypeNoParams ")" Expression3
+" | Primary {Selector} {PostfixOp}
+" Primary = "(" Expression ")"
+" | Literal
+" | [TypeArguments] THIS [Arguments]
+" | [TypeArguments] SUPER SuperSuffix
+" | NEW [TypeArguments] Creator
+" | Ident { "." Ident }
+" [ "[" ( "]" BracketsOpt "." CLASS | Expression "]" )
+" | Arguments
+" | "." ( CLASS | THIS | [TypeArguments] SUPER Arguments | NEW [TypeArguments] InnerCreator )
+" ]
+" | BasicType BracketsOpt "." CLASS
+" PrefixOp = "++" | "--" | "!" | "~" | "+" | "-"
+" PostfixOp = "++" | "--"
+" Type3 = Ident { "." Ident } [TypeArguments] {TypeSelector} BracketsOpt
+" | BasicType
+" TypeNoParams3 = Ident { "." Ident } BracketsOpt
+" Selector = "." [TypeArguments] Ident [Arguments]
+" | "." THIS
+" | "." [TypeArguments] SUPER SuperSuffix
+" | "." NEW [TypeArguments] InnerCreator
+" | "[" Expression "]"
+" TypeSelector = "." Ident [TypeArguments]
+" SuperSuffix = Arguments | "." Ident [Arguments]
+" NOTE: We need only type expression.
+fu! s:term3()
+ let pos = b:pos
+ let t = copy(s:TTree)
+
+ call s:Debug('term3() b:token is ' . b:token)
+ let typeArgs = s:typeArgumentsOpt(s:EXPR)
+
+ if b:token == 'QUES'
+ if s:modeAndTYPE() && s:BitAnd(b:mode, s:TYPEARG_OR_NOPARAMS) == s:TYPEARG
+ let b:mode = s:TYPE
+ return s:typeArgument()
+ else
+ return s:illegal()
+ endif
+
+ elseif b:token =~# '^\(PLUSPLUS\|SUBSUB\|BANG\|TILDE\|PLUS\|SUB\)$'
+ if typeArgs == [] && s:modeAndEXPR()
+ let token = b:token
+ call s:nextToken()
+ let b:mode = s:EXPR
+ if token == 'SUB' && (b:token == 'INTLITERAL' || b:token == 'LONGLITERAL') && b:radix == 10
+ let b:mode = s:EXPR
+ let t = s:literal('-')
+ else
+ let t = s:term3()
+ return s:Unary(pos, s:unoptag(token), t)
+ endif
+ else
+ return s:illegal()
+ endif
+
+ elseif b:token == 'LPAREN'
+ if typeArgs == [] && s:modeAndEXPR()
+ call s:nextToken()
+ let b:mode = s:EXPR_OR_TYPE_OR_NOPARAMS
+ let t = s:term3()
+ if s:modeAndEXPR() && b:token == 'LT'
+ let op = 'LT'
+ let pos1 = b:pos
+ call s:nextToken()
+ let b:mode = s:BitAnd(b:mode, s:EXPR_OR_TYPE)
+ let b:mode = s:BitOr(b:mode, s:TYPEARG)
+ let t1 = s:term3()
+ if s:modeAndTYPE() && (b:token == 'COMMA' || b:token == 'GT')
+ let b:mode = s:TYPE
+ let args = []
+ call add(args, t1)
+ while b:token == 'COMMA'
+ call s:nextToken()
+ call add(args, s:typeArgument())
+ endwhile
+ call s:accept('GT')
+ let t = {'tag': 'TYPEAPPLY', 'pos': pos1, 'clazz': t, 'arguments': args}
+ " checkGenerics
+ let t = s:bracketsOpt(t)
+ elseif s:modeAndEXPR()
+ let b:mode = s:EXPR
+ let t = s:Binary(pos1, op, t, s:term2Rest(t1, s:opprecedences.shiftPrec))
+ let t = s:termRest(s:term1Rest(s:term2Rest(t, s:opprecedences.orPrec)))
+ else
+ call s:accept('GT')
+ endif
+ else
+ let t = s:termRest(s:term1Rest(s:term2Rest(t, s:opprecedences.orPrec)))
+ endif
+ call s:accept('RPAREN')
+ let b:lastmode = b:mode
+ let b:mode = s:EXPR
+ if s:BitAnd(b:lastmode, s:EXPR) == 0
+ return s:TypeCast(pos, t, s:term3())
+ elseif s:BitAnd(b:lastmode, s:TYPE) != 0
+ if b:token =~# '^\(BANG\|TILDE\|LPAREN\|THIS\|SUPER\|INTLITERAL\|LONGLITERAL\|FLOATLITERAL\|DOUBLELITERAL\|CHARLITERAL\|STRINGLITERAL\|TRUE\|FALSE\|NULL\|NEW\|IDENTIFIER\|ASSERT\|ENUM\|BYTE\|SHORT\|CHAR\|INT\|LONG\|FLOAT\|DOUBLE\|BOOLEAN\|VOID\)$'
+ return s:TypeCast(pos, t, s:term3())
+ endif
+ endif
+ else
+ return s:illegal()
+ endif
+ let t = {'tag': 'PARENS', 'pos': pos, 'expr': t}
+
+ elseif b:token == 'THIS'
+ if s:modeAndEXPR()
+ let b:mode = s:EXPR
+ let t = s:Ident(pos, 'this')
+ call s:nextToken()
+ if typeArgs == []
+ let t = s:argumentsOpt([], t)
+ else
+ let t = s:arguments(typeArgs, t)
+ endif
+ let typeArgs = []
+ else
+ return s:illegal()
+ endif
+
+ elseif b:token == 'SUPER'
+ if s:modeAndEXPR()
+ let b:mode = s:EXPR
+ let t = s:superSuffix(typeArgs, s:Ident(pos, 'super'))
+ let typeArgs = []
+ else
+ return s:illegal()
+ endif
+
+ elseif b:token =~# '^\(INTLITERAL\|LONGLITERAL\|FLOATLITERAL\|DOUBLELITERAL\|CHARLITERAL\|STRINGLITERAL\|TRUE\|FALSE\|NULL\)$'
+ if typeArgs == [] && s:modeAndEXPR()
+ let b:mode = s:EXPR
+ let t = s:literal('')
+ else
+ return s:illegal()
+ endif
+
+ elseif b:token == 'NEW'
+ if typeArgs != []
+ return s:illegal()
+ endif
+
+ if s:modeAndEXPR()
+ let b:mode = s:EXPR
+ call s:nextToken()
+ if b:token == 'LT'
+ let typeArgs = s:typeArguments()
+ endif
+ let t = s:creator(pos, typeArgs)
+ let typeArgs = []
+ else
+ return s:illegal()
+ endif
+
+ elseif b:token =~# '^\(IDENTIFIER\|ASSERT\|ENUM\)$'
+ if typeArgs != []
+ return s:illegal()
+ endif
+
+ let t = s:Ident(pos, s:ident())
+ while 1
+ if b:token == 'LBRACKET'
+ "let t.value = '[' " FIXME
+ call s:nextToken()
+ if b:token == 'RBRACKET'
+ "let t.value .= ']'
+ call s:nextToken()
+ let t = s:bracketsSuffix(s:TypeArray(pos, s:bracketsOpt(t)))
+ else
+ if s:modeAndEXPR()
+ let b:mode = s:EXPR
+ let t = {'tag': 'INDEXED', 'pos': pos, 'indexed': t, 'index': s:term()}
+ endif
+ call s:accept('RBRACKET')
+ "let t.value .= ']'
+ endif
+ break
+
+ elseif b:token == 'LPAREN'
+ if s:modeAndEXPR()
+ let b:mode = s:EXPR
+ let t = s:arguments(typeArgs, t)
+ let typeArgs = []
+ "call s:accept('LPAREN')
+ "call s:gotoMatchEnd('(', ')', b:token == 'LPAREN')
+ "call s:accept('RPAREN')
+ endif
+ break
+
+ elseif b:token == 'DOT'
+ call s:nextToken()
+ let typeArgs = s:typeArgumentsOpt(s:EXPR)
+ if s:modeAndEXPR()
+ if b:token == 'CLASS' || b:token == 'THIS'
+ if typeArgs != []
+ return s:illegal()
+ endif
+ let b:mode = s:EXPR
+ let t = s:Select(pos, t, b:token == 'CLASS' ? 'class' : 'this')
+ call s:nextToken()
+ break
+ elseif b:token == 'SUPER'
+ let b:mode = s:EXPR
+ let t = s:Select(pos, t, 'super')
+ let t = s:superSuffix(typeArgs, t)
+ let typeArgs = []
+ break
+ elseif b:token == 'NEW'
+ if typeArgs != []
+ return s:illegal()
+ endif
+ let b:mode = s:EXPR
+ let pos1 = b:pos
+ call s:nextToken()
+ if b:token == 'LT'
+ let typeArgs = s:typeArguments()
+ endif
+ let t = s:innerCreator(pos1, typeArgs, t)
+ let typeArgs = []
+ break
+ endif
+ endif
+ let t = s:Select(pos, t, s:ident())
+ else
+ break
+ endif
+ endwhile
+ if typeArgs != [] | call s:illegal() | endif
+ let t = s:typeArgumentsOpt2(t)
+
+ elseif b:token =~# '^\(BYTE\|SHORT\|CHAR\|INT\|LONG\|FLOAT\|DOUBLE\|BOOLEAN\)$'
+ if typeArgs != [] | call s:illegal() | endif
+ let t = s:bracketsSuffix(s:bracketsOpt(s:basicType()))
+
+ elseif b:token == 'VOID'
+ if typeArgs != [] | call s:illegal() | endif
+ if s:modeAndEXPR()
+ call s:nextToken()
+ if b:token == 'DOT'
+ let ti = {'tag': 'TYPEIDENT', 'pos': pos, 'typetag': 'void'} " FIXME
+ let t = s:bracketsSuffix(ti)
+ else
+ return s:illegal(pos)
+ endif
+ else
+ return s:illegal()
+ endif
+
+ else
+ return s:illegal()
+ endif
+
+ if typeArgs != []
+ return s:illegal()
+ endif
+
+ while 1
+ let pos1 = b:pos
+ if b:token == 'LBRACKET'
+ call s:nextToken()
+ if s:modeAndEXPR()
+ let oldmode = b:mode
+ let b:mode = s:TYPE
+ if b:token == 'RBRACKET'
+ call s:nextToken()
+ let t = s:bracketsOpt(t)
+ let t = s:TypeArray(pos1, t)
+ return t
+ endif
+ let b:mode = oldmode
+ endif
+ if s:modeAndEXPR()
+ let b:mode = s:EXPR
+ let t = {'tag': 'INDEXED', 'pos': pos1, 'indexed': t, 'index': s:term()}
+ endif
+ call s:accept('RBRACKET')
+
+ elseif b:token == 'DOT'
+ call s:nextToken()
+ let typeArgs = s:typeArgumentsOpt(s:EXPR)
+ if b:token == 'SUPER' && s:modeAndEXPR()
+ let b:mode = s:EXPR
+ let t = s:Select(pos1, t, 'super')
+ call s:nextToken()
+ let t = s:arguments(typeArgs, t)
+ let typeArgs = []
+ elseif b:token == 'NEW' && s:modeAndEXPR()
+ if typeArgs != []
+ return s:illegal()
+ endif
+ let b:mode = s:EXPR
+ let pos2 = b:pos
+ call s:nextToken()
+ if b:token == 'LT'
+ let typeArgs = s:typeArguments()
+ endif
+ let t = s:innerCreator(pos2, typeArgs, t)
+ let typeArgs = []
+ else
+ let t = s:Select(pos1, t, s:ident())
+ let t = s:argumentsOpt(typeArgs, s:typeArgumentsOpt2(t))
+ let typeArgs = []
+ endif
+ else
+ break
+ endif
+ endwhile
+
+
+ while (b:token == 'PLUSPLUS' || b:token == 'SUBSUB') && s:modeAndEXPR()
+ let b:mode = s:EXPR
+ let t = s:Unary(b:pos, b:token == 'PLUSPLUS' ? 'POSTINC' : 'POSTDEC', t)
+ call s:nextToken()
+ endwhile
+ return t
+endfu
+
+fu! s:superSuffix(typeArgs, t)
+ let typeArgs = a:typeArgs
+ let t = a:t
+ call s:nextToken()
+ if b:token == 'LPAREN' || typeArgs != []
+ let t = s:arguments(typeArgs, t)
+ else
+ let pos = b:pos
+ call s:accept('DOT')
+ let typeArgs = b:token == 'LT' ? s:typeArguments() : []
+ let t = s:Select(pos, t, s:ident())
+ let t = s:argumentsOpt(typeArgs, t)
+ endif
+ return t
+endfu
+
+" BasicType = BYTE | SHORT | CHAR | INT | LONG | FLOAT | DOUBLE | BOOLEAN {{{2
+fu! s:basicType()
+ let t = {'tag': 'TYPEIDENT', 'pos': b:pos, 'typetag': s:typetag(b:token)}
+ call s:nextToken()
+ return t
+endfu
+
+" ArgumentsOpt = [ Arguments ] {{{2
+fu! s:argumentsOpt(typeArgs, t)
+ if s:modeAndEXPR() && b:token == 'LPAREN' || a:typeArgs != []
+ let b:mode = s:EXPR
+ return s:arguments(a:typeArgs, a:t)
+ else
+ return a:t
+ endif
+endfu
+
+" Arguments = "(" [Expression { COMMA Expression }] ")"
+fu! s:arguments(...)
+ let pos = b:pos
+ let args = []
+ if b:token == 'LPAREN'
+ call s:nextToken()
+ if b:token != 'RPAREN'
+ call add(args, s:expression())
+ while b:token == 'COMMA'
+ call s:nextToken()
+ call add(args, s:expression())
+ endwhile
+ endif
+ call s:accept('RPAREN')
+ else
+ call s:SyntaxError(') expected')
+ endif
+
+ if a:0 == 0
+ return args
+ else
+ let typeArgs = a:1
+ let t = a:2
+ return {'tag': 'APPLY', 'pos': pos, 'typeargs': typeArgs, 'meth': t, 'args': args}
+ endif
+endfu
+
+" typeArgument generic type {{{2
+fu! s:typeArgumentsOpt2(t)
+ if b:token == 'LT' && s:modeAndTYPE() && s:BitAnd(b:mode, s:NOPARAMS) == 0
+ let b:mode = s:TYPE
+ " checkGenerics()
+ return s:typeArguments(a:t)
+ else
+ return a:t
+ endif
+endfu
+
+fu! s:typeArgumentsOpt(...)
+ let useMode = a:0 == 0 ? s:TYPE : a:1
+
+ if b:token == 'LT'
+ " checkGenerics()
+ if s:BitAnd(b:mode, useMode) == 0 || s:BitAnd(b:mode, s:NOPARAMS) != 0
+ return s:illegal()
+ endif
+ let b:mode = useMode
+ return s:typeArguments()
+ endif
+ return []
+endfu
+
+" TypeArguments = "<" TypeArgument {"," TypeArgument} ">"
+fu! s:typeArguments(...)
+ let pos = b:pos
+
+ let args = []
+ if b:token == 'LT'
+ call s:nextToken()
+ call add(args, s:modeAndEXPR() ? s:type() : s:typeArgument())
+ while b:token == 'COMMA'
+ call s:nextToken()
+ call add(args, s:modeAndEXPR() ? s:type() : s:typeArgument())
+ endwhile
+
+ if b:token == 'GTGTGTEQ'
+ let b:token = 'GTGTEQ'
+ elseif b:token == 'GTGTEQ'
+ let b:token = 'GTEQ'
+ elseif b:token == 'GTEQ'
+ let b:token = 'EQ'
+ elseif b:token == 'GTGTGT'
+ let b:token = 'GTGT'
+ elseif b:token == 'GTGT'
+ let b:token = 'GT'
+ else
+ call s:accept('GT')
+ endif
+ else
+ call s:SyntaxError("LT expected")
+ endif
+
+ if a:0 == 0
+ return args
+ else
+ return {'tag': 'TYPEAPPLY', 'pos': pos, 'clazz': a:1, 'arguments': args}
+ endif
+endfu
+
+" TypeArgument = Type
+" | "?"
+" | "?" EXTENDS Type {"&" Type}
+" | "?" SUPER Type
+fu! s:typeArgument()
+ if b:token != 'QUES'
+ return s:type()
+ endif
+
+ call s:nextToken()
+ if b:token == 'EXTENDS'
+ call s:nextToken()
+ return s:type()
+ elseif b:token == 'SUPER'
+ call s:nextToken()
+ return s:type()
+ elseif b:token == 'IDENTIFIER'
+ let id = ident()
+ else
+ endif
+endfu
+
+
+" BracketsOpt = {"[" "]"} {{{2
+fu! s:bracketsOpt(t)
+ let t = a:t
+ while b:token == 'LBRACKET'
+ let pos = b:pos
+ call s:nextToken()
+ let t = s:bracketsOptCont(t, pos)
+ endwhile
+ return t
+endfu
+
+fu! s:bracketsOptCont(t, pos)
+ let t = a:t
+ call s:accept('RBRACKET')
+ let t = s:bracketsOpt(t)
+ return s:TypeArray(a:pos, t)
+endfu
+
+" BracketsSuffixExpr = "." CLASS
+" BracketsSuffixType =
+fu! s:bracketsSuffix(t)
+ let t = a:t
+ if s:modeAndEXPR() && b:token == 'DOT'
+ let b:mode = s:EXPR
+ let pos = b:pos
+ call s:nextToken()
+ call s:accept('CLASS')
+ if b:pos == b:errorEndPos
+ let name = ''
+ if b:token == 'IDENTIFIER'
+ let name = b:name
+ call s:nextToken()
+ else
+ let name = '<error>'
+ endif
+ let t = {'tag': 'ERRONEOUS', 'pos': pos, 'errs': [s:Select(pos, t, name)]}
+ else
+ let t = s:Select(pos, t, 'class')
+ endif
+ elseif s:modeAndTYPE()
+ let b:mode = s:TYPE
+ else
+ call s:SyntaxError('dot.class.expected')
+ endif
+ return t
+endfu
+
+" creator {{{2
+fu! s:creator(newpos, typeArgs)
+ if b:token =~# '^\(BYTE\|SHORT\|CHAR\|INT\|LONG\|FLOAT\|DOUBLE\|BOOLEAN\)$'
+ if a:typeArgs == []
+ return s:arrayCreatorRest(a:newpos, s:basicType())
+ endif
+ endif
+
+ let t = s:qualident()
+ let oldmode = b:mode
+ let b:mode = s:TYPE
+ if b:token == 'LT'
+ "checkGenerics
+ let t = s:typeArguments(t)
+ endif
+ let b:mode = oldmode
+
+ if b:token == 'LBRACKET'
+ return s:arrayCreatorRest(a:newpos, t)
+ elseif b:token == 'LPAREN'
+ return s:classCreatorRest(a:newpos, {}, a:typeArgs, t)
+ else
+ call s:ReportSyntaxError(b:pos, '( or [ expected')
+ let t = {'tag': 'NEWCLASS', 'encl': {}, 'typeargs': a:typeArgs, 'clazz': t, 'args': [], 'def': {}}
+ return {'tag': 'ERRONEOUS', 'pos': a:newpos, 'errs': [t]}
+ endif
+endfu
+
+fu! s:innerCreator(newpos, typeArgs, encl)
+ let t = s:Ident(b:pos, s:ident())
+ if b:token == 'LT'
+ " checkGenerics
+ let t = TypeArguments(t)
+ endif
+ return s:classCreatorRest(a:newpos, a:encl, a:typeArgs, t)
+endfu
+
+fu! s:arrayCreatorRest(newpos, elemtype)
+ let elemtype = a:elemtype
+ call s:accept('LBRACKET')
+ if b:token == 'RBRACKET'
+ call s:accept('RBRACKET')
+ let elemtype = s:bracketsOpt(elemtype)
+ if b:token == 'LBRACE'
+ return s:arrayInitializer(a:newpos, elemtype)
+ else
+ return s:SyntaxError('array.dimension.missing')
+ endif
+ else
+ let dims = [s:expression()]
+ call s:accept('RBRACKET')
+ while b:token == 'LBRACKET'
+ let pos = b:pos
+ call s:nextToken()
+ if b:token == 'RBRACKET'
+ let elemtype = s:bracketsOptCont(elemtype, pos)
+ else
+ call add(dims, s:expression())
+ call s:accept('RBRACKET')
+ endif
+ endwhile
+ return {'tag': 'NEWARRAY', 'pos': a:newpos, 'elemtype': elemtype, 'dims': dims, 'elems': {}}
+ endif
+endfu
+
+fu! s:classCreatorRest(newpos, encl, typeArgs, t)
+ let args = s:arguments()
+ let body = {}
+ if b:token == 'LBRACE'
+ let body = s:ClassDef(b:pos, {})
+ let body.defs = s:classOrInterfaceBody('', 0)
+ let body.endpos = b:pos
+ endif
+ return {'tag': 'NEWCLASS', 'encl': a:encl, 'typeargs': a:typeArgs, 'clazz': a:t, 'args': args, 'def': body}
+endfu
+
+" ArrayInitializer = "{" [VariableInitializer {"," VariableInitializer}] [","] "}" {{{2
+fu! s:arrayInitializer(newpos, t)
+ call s:accept('LBRACE')
+ let elems = []
+ if b:token == 'COMMA'
+ call s:nextToken()
+ elseif b:token != 'RBRACE'
+ call add(elems, s:variableInitializer())
+ while b:token == 'COMMA'
+ call s:nextToken()
+ if b:token == 'RBRACE'
+ break
+ endif
+ call add(elems, s:variableInitializer())
+ endwhile
+ endif
+ call s:accept('RBRACE')
+ return {'tag': 'NEWARRAY', 'pos': a:newpos, 'elemtype': a:t, 'dims': [], 'elems': elems}
+endfu
+
+" VariableInitializer = ArrayInitializer | Expression {{{2
+fu! s:variableInitializer()
+ return b:token == 'LBRACE' ? s:arrayInitializer(b:pos, {}) : s:expression()
+endfu
+
+" ParExpression = "(" Expression ")" {{{2
+fu! s:parExpression()
+ call s:accept('LPAREN')
+ let t = s:expression()
+ call s:accept('RPAREN')
+ return t
+endfu
+
+" {{{2
+" Block = "{" BlockStatements "}" {{{2
+fu! s:block(...)
+ let t = {'tag': 'BLOCK', 'stats': []}
+ let t.pos = a:0 > 0 ? a:1 : b:pos
+ let t.flags = a:0 > 1 ? a:2 : 0
+
+ call s:accept('LBRACE')
+
+ " scan strategy: ignore statements?
+ if a:0 > 2 && a:3
+ if b:token !=# 'RBRACE'
+ let b:pos = s:gotoMatchEnd('{', '}', b:token == 'LBRACE')
+ endif
+ "let t.stats = Strpart(t.pos, t.endpos-t.pos-1)
+ else
+ let t.stats = s:blockStatements()
+ while b:token == 'CASE' || b:token == 'DEFAULT'
+ call s:SyntaxError("orphaned")
+ call s:switchBlockStatementGroups()
+ endwhile
+ endif
+
+ let t.endpos = b:pos
+ call s:accept('RBRACE')
+ return t
+endfu
+
+" BlockStatements = { BlockStatement } {{{2
+" BlockStatement = LocalVariableDeclarationStatement
+" | ClassOrInterfaceOrEnumDeclaration
+" | [Ident ":"] Statement
+" LocalVariableDeclarationStatement
+" = { FINAL | '@' Annotation } Type VariableDeclarators ";"
+fu! s:blockStatements()
+ let lastErrPos = -1
+ let stats = []
+ while 1
+ let pos = b:pos
+ if b:token =~# '^\(RBRACE\|CASE\|DEFAULT\|EOF\)$'
+ return stats
+ elseif b:token =~# '^\(LBRACE\|IF\|FOR\|WHILE\|DO\|TRY\|SWITCH\|SYNCHRONIZED\|RETURN\|THROW\|BREAK\|CONTINUE\|SEMI\|ELSE\|FINALLY\|CATCH\)$'
+ call add(stats, s:statement())
+ elseif b:token =~# '^\(MONKEYS_AT\|FINAL\)'
+ let dc = b:docComment
+ let mods = s:modifiersOpt()
+ if b:token =~# '^\(INTERFACE\|CLASS\|ENUM\)$'
+ call add(stats, s:classOrInterfaceOrEnumDeclaration(mods, dc))
+ else
+ let t = s:type()
+ let stats = stats + s:variableDeclarators(mods, t, [])
+ call s:accept('SEMI')
+ endif
+ elseif b:token =~# '^\(ABSTRACT\|STRICTFP\|CLASS\|INTERFACE\|ENUM\)$'
+ if b:token == 'ENUM'
+ call s:Log(4, b:pos, 'local.enum')
+ endif
+ call add(stats, s:classOrInterfaceOrEnumDeclaration(s:modifiersOpt(), b:docComment))
+ elseif b:token == 'ASSERT'
+ call add(stats, s:statement())
+ else
+ let name = b:name
+ let t = s:term(s:EXPR_OR_TYPE)
+ if b:token == 'COLON' && t.tag == 'IDENT'
+ call s:nextToken()
+ let stat = s:statement()
+ call add(stats, {'tag': 'LABELLED', 'pos': b:pos, 'label': name, 'body': stat})
+ elseif s:BitAnd(b:lastmode, s:TYPE) && b:token =~# '^\(IDENTIFIER\|ASSERT\|ENUM\)$'
+ let pos = b:pos
+ let mods = {} " {'tag': 'MODIFIERS', 'pos': -1, 'flags': 0}
+ let stats = stats + s:variableDeclarators(mods, t, [])
+ call s:accept('SEMI')
+ else
+ call add(stats, {'tag': 'EXEC', 'pos': pos, 'expr': s:checkExprStat(t)}) " TODO
+ call s:accept('SEMI')
+ endif
+ endif
+
+ if b:pos == lastErrPos
+ return stats
+ endif
+ if b:pos <= b:errorEndPos
+ call s:skip(0, 1, 1, 1)
+ let lastErrPos = b:pos
+ endif
+
+ " resetDeprecatedFlag()
+ endwhile
+endfu
+
+" Statement = Block {{{2
+" | IF ParExpression Statement [ELSE Statement]
+" | FOR "(" ForInitOpt ";" [Expression] ";" ForUpdateOpt ")" Statement
+" | FOR "(" FormalParameter : Expression ")" Statement
+" | WHILE ParExpression Statement
+" | DO Statement WHILE ParExpression ";"
+" | TRY Block ( Catches | [Catches] FinallyPart )
+" | SWITCH ParExpression "{" SwitchBlockStatementGroups "}"
+" | SYNCHRONIZED ParExpression Block
+" | RETURN [Expression] ";"
+" | THROW Expression ";"
+" | BREAK [Ident] ";"
+" | CONTINUE [Ident] ";"
+" | ASSERT Expression [ ":" Expression ] ";"
+" | ";"
+" | ExpressionStatement
+" | Ident ":" Statement
+" called only by BlockStatements or self
+fu! s:statement()
+ let pos = b:pos
+ if b:token == 'LBRACE'
+ return s:block()
+ elseif b:token == 'IF'
+ call s:nextToken()
+ let t = {'tag': 'IF', 'pos': pos, 'cond': s:parExpression(), 'thenpart': s:statement()}
+ if b:token == 'ELSE'
+ call s:nextToken()
+ let t.elsepart = s:statement()
+ endif
+ let t.endpos = b:pos
+ return t
+
+ elseif b:token == 'FOR'
+ call s:nextToken()
+ call s:accept('LPAREN')
+ let inits = b:token == 'SEMI' ? [] : s:forInit()
+ " foreach
+ if len(inits) == 1 && inits[0].tag == 'VARDEF' && (!has_key(inits[0], 'init') || inits[0].init == {}) && b:token == 'COLON'
+ " checkForeach
+ let var = inits[0]
+ call s:accept('COLON')
+ let expr = s:expression()
+ call s:accept('RPAREN')
+ let body = s:statement()
+ return {'tag': 'FOREACHLOOP', 'pos': pos, 'endpos': b:pos, 'var': var, 'expr': expr, 'body': body}
+ else
+ call s:accept('SEMI')
+ let cond = b:token == 'SEMI' ? {} : s:expression()
+ call s:accept('SEMI')
+ let steps = b:token == 'RPAREN' ? [] : s:forUpdate()
+ call s:accept('RPAREN')
+ let body = s:statement()
+ return {'tag': 'FORLOOP', 'pos': pos, 'endpos': b:pos, 'init': inits, 'cond': cond, 'step': steps, 'body': body}
+ endif
+
+ elseif b:token == 'WHILE'
+ call s:nextToken()
+ let cond = s:parExpression()
+ let body = s:statement()
+ return {'tag': 'WHILELOOP', 'pos': pos, 'endpos': b:pos, 'cond': cond, 'body': body}
+
+ elseif b:token == 'DO'
+ call s:nextToken()
+ let body = s:statement()
+ call s:accept('WHILE')
+ let cond = s:parExpression()
+ let t = {'tag': 'DOLOOP', 'pos': pos, 'endpos': b:pos, 'cond': cond, 'body': body}
+ call s:accept('SEMI')
+ return t
+
+ elseif b:token == 'TRY'
+ call s:nextToken()
+ let body = s:block()
+ let catchers = []
+ let finalizer = {}
+ if b:token == 'CATCH' || b:token == 'FINALLY'
+ while b:token == 'CATCH'
+ call add(catchers, s:catchClause())
+ endwhile
+ if b:token == 'FINALLY'
+ call s:nextToken()
+ let finalizer = s:block()
+ endif
+ else
+ call s:Log(4, b:pos, 'try.without.catch.or.finally')
+ endif
+ return {'tag': 'TRY', 'pos': pos, 'endpos': b:pos, 'body': body, 'catchers': catchers, 'finalizer': finalizer}
+
+ elseif b:token == 'SWITCH'
+ call s:nextToken()
+ let selector = s:parExpression()
+ call s:accept('LBRACE')
+ let cases = s:switchBlockStatementGroups()
+ call s:accept('RBRACE')
+ return {'tag': 'SWITCH', 'pos': pos, 'endpos': b:pos, 'selector': selector, 'cases': cases}
+
+ elseif b:token == 'SYNCHRONIZED'
+ call s:nextToken()
+ let lock = s:parExpression()
+ let body = s:block()
+ return {'tag': 'SYNCHRONIZED', 'pos': pos, 'endpos': b:pos, 'lock': lock, 'cases': body}
+
+ elseif b:token == 'RETURN'
+ call s:nextToken()
+ let result = b:token == 'SEMI' ? {} : s:expression()
+ call s:accept('SEMI')
+ return {'tag': 'RETURN', 'pos': pos, 'endpos': b:pos, 'expr': result}
+
+ elseif b:token == 'THROW'
+ call s:nextToken()
+ let exc = s:expression()
+ call s:accept('SEMI')
+ return {'tag': 'THROW', 'pos': pos, 'endpos': b:pos, 'expr': exc}
+
+ elseif b:token == 'BREAK' || b:token == 'CONTINUE'
+ let token = b:token
+ call s:nextToken()
+ let label = b:token =~# '^\(IDENTIFIER\|ASSERT\|ENUM\)$' ? s:ident() : ''
+ call s:accept('SEMI')
+ return {'tag': token, 'pos': pos, 'endpos': b:pos, 'label': label}
+
+ elseif b:token == 'SEMI'
+ call s:nextToken()
+ return {'tag': 'SKIP', 'pos': pos}
+
+ elseif b:token == 'ELSE'
+ return s:SyntaxError("else.without.if")
+ elseif b:token == 'FINALLY'
+ return s:SyntaxError("finally.without.try")
+ elseif b:token == 'CATCH'
+ return s:SyntaxError("catch.without.try")
+
+ elseif b:token == 'ASSERT'
+ "if b:allowAsserts && b:token == 'ASSERT'
+ call s:nextToken()
+ let t = {'tag': 'ASSERT', 'pos': pos, 'cond': s:expression()}
+ if b:token == 'COLON'
+ call s:nextToken()
+ let t.detail = s:expression()
+ endif
+ call s:accept('SEMI')
+ let t.endpos = b:pos
+ return t
+ "endif
+
+ else " also ENUM
+ let name = b:name
+ let expr = s:expression()
+ if b:token == 'COLON' && expr.tag == 'IDENT'
+ call s:nextToken()
+ let stat = s:statement()
+ return {'tag': 'LABELLED', 'pos': pos, 'endpos': b:pos, 'label': name, 'body': stat}
+ else
+ let stat = {'tag': 'EXEC', 'pos': pos, 'endpos': b:pos, 'expr': s:checkExprStat(expr)}
+ call s:accept('SEMI')
+ return stat
+ endif
+ endif
+endfu
+
+" CatchClause = CATCH "(" FormalParameter ")" Block
+fu! s:catchClause()
+ let pos = b:pos
+ call s:accept('CATCH')
+ call s:accept('LPAREN')
+ let formal = s:variableDeclaratorId(s:optFinalParameter(), s:qualident())
+ call s:accept('RPAREN')
+ let body = s:block()
+ return {'tag': 'CATCH', 'pos': pos, 'endpos': b:pos, 'param': formal, 'body': body}
+endfu
+
+" SwitchBlockStatementGroups = { SwitchBlockStatementGroup }
+" SwitchBlockStatementGroup = SwitchLabel BlockStatements
+" SwitchLabel = CASE ConstantExpression ":" | DEFAULT ":"
+fu! s:switchBlockStatementGroups()
+ let cases = []
+ while 1
+ let pos = b:pos
+ if b:token == 'CASE' || b:token == 'DEFAULT'
+ let token = b:token
+ call s:nextToken()
+ let pat = token == 'CASE' ? s:expression() : {}
+ call s:accept('COLON')
+ let stats = s:blockStatements()
+ call add(cases, {'tag': 'CASE', 'pos': pos, 'pat': pat, 'stats': stats})
+ elseif b:token == 'RBRACE' || b:token == 'EOF'
+ return cases
+ else
+ call s:nextToken()
+ call s:SyntaxError('case.default.rbrace.expected')
+ endif
+ endwhile
+endfu
+
+" MoreStatementExpressions = { COMMA StatementExpression }
+fu! s:moreStatementExpressions(pos, first, stats)
+ call add(a:stats, {'tag': 'EXEC', 'pos': a:pos, 'expr': s:checkExprStat(a:first)})
+ while b:token == 'COMMA'
+ call s:nextToken()
+ let pos = b:pos
+ let t = s:expression()
+ call add(a:stats, {'tag': 'EXEC', 'pos': pos, 'expr': s:checkExprStat(t)})
+ endwhile
+ return a:stats
+endfu
+
+" ForInit = StatementExpression MoreStatementExpressions
+" | { FINAL | '@' Annotation } Type VariableDeclarators
+fu! s:forInit()
+ let stats = []
+ let pos = b:pos
+ if b:token == 'FINAL' || b:token == 'MONKEYS_AT'
+ return s:variableDeclarators(s:optFinal(0), s:type(), stats)
+ else
+ let t = s:term(s:EXPR_OR_TYPE)
+ if s:BitAnd(b:lastmode, s:TYPE) && b:token =~# '^\(IDENTIFIER\|ASSERT\|ENUM\)$'
+ return s:variableDeclarators(s:modifiersOpt(), t, stats)
+ else
+ return s:moreStatementExpressions(pos, t, stats)
+ endif
+ endif
+endfu
+
+" ForUpdate = StatementExpression MoreStatementExpressions
+fu! s:forUpdate()
+ return s:moreStatementExpressions(b:pos, s:expression(), [])
+endfu
+
+" ModifiersOpt = { Modifier } {{{2
+" Modifier = PUBLIC | PROTECTED | PRIVATE | STATIC | ABSTRACT | FINAL
+" | NATIVE | SYNCHRONIZED | TRANSIENT | VOLATILE | "@"
+" | "@" Annotation
+" NOTE: flags is a string, not a long number
+fu! s:modifiersOpt(...)
+ let partial = a:0 == 0 ? {} : a:1
+
+ let flags = partial == {} ? 0 : partial.flags
+ let annotations = partial == {} ? [] : partial.annotations
+ " TODO: deprecatedFlag
+
+ let pos = b:pos
+ let lastPos = -1
+ while 1
+ let flag = 0
+ if b:token =~# '^\(PUBLIC\|PROTECTED\|PRIVATE\|STATIC\|ABSTRACT\|FINAL\|NATIVE\|SYNCHRONIZED\|TRANSIENT\|VOLATILE\|STRICTFP\|MONKEYS_AT\)$'
+ let flag = b:token == 'MONKEYS_AT' ? s:Flags.ANNOTATION : get(s:Flags, b:token, 0)
+ else
+ break
+ endif
+ "if s:BitAnd(flags, flag) != 0
+ " "log.error(S.pos(), "repeated.modifier")
+ "endif
+
+ let lastPos = b:pos
+ call s:nextToken()
+
+ if flag == s:Flags.ANNOTATION
+ "call s:checkAnnotations()
+ if b:token != 'INTERFACE'
+ let ann = s:annotation(lastPos)
+ if flags == 0 && annotations == []
+ let pos = ann.pos
+ endif
+ call add(annotations, ann)
+ let lastPos = ann.pos
+ let flag = 0
+ endif
+ endif
+ let flags = s:BitOr(flags, flag)
+ endwhile
+
+ if b:token == 'ENUM'
+ let flags = s:BitOr(flags, s:Flags.ENUM)
+ elseif b:token == 'INTERFACE'
+ let flags = s:BitOr(flags, s:Flags.INTERFACE)
+ endif
+
+ if flags == 0 && empty(annotations)
+ let pos = -1
+ endif
+
+ return {'tag': 'MODIFIERS', 'pos': pos, 'flags': flags, 'annotations': annotations}
+endfu
+
+" Annotation = "@" Qualident [ "(" AnnotationFieldValues ")" ] {{{2
+fu! s:annotation(pos)
+ "call s:checkAnnotations()
+ let ident = s:qualident()
+ "let endPos = b:prevEndPos
+ let fieldValues = s:annotationFieldValuesOpt()
+
+ return {'tag': 'ANNOTATION', 'pos': a:pos, 'annotationType': ident, 'args': fieldValues}
+endfu
+
+fu! s:annotationFieldValuesOpt()
+ return b:token == 'LPAREN' ? s:annotationFieldValues() : []
+endfu
+
+" AnnotationFieldValues = "(" [ AnnotationFieldValue { "," AnnotationFieldValue } ] ")"
+fu! s:annotationFieldValues()
+ let buf = []
+ call s:accept('LPAREN')
+ if b:token != 'RPAREN'
+ call add(buf, s:annotationFieldValue())
+ while b:token == 'COMMA'
+ call s:nextToken()
+ call add(buf, s:annotationFieldValue())
+ endwhile
+ endif
+ call s:accept('RPAREN')
+ return buf
+endfu
+
+" AnnotationFieldValue = AnnotationValue | Identifier "=" AnnotationValue
+fu! s:annotationFieldValue()
+ call s:Trace('s:annotationFieldValue ' . b:token)
+ if b:token == 'IDENTIFIER'
+ let b:mode = s:EXPR
+ let t1 = s:term1()
+ if t1.tag == 'IDENT' && b:token == 'EQ'
+ let pos = b:pos
+ call s:accept('EQ')
+ return {'tag': 'ASSIGN', 'pos': pos, 'lhs': t1, 'rhs': s:annotationValue()}
+ else
+ return t1
+ endif
+ endif
+ return s:annotationValue()
+endfu
+
+" AnnotationValue = ConditionalExpression | Annotation | "{" [ AnnotationValue { "," AnnotationValue } ] "}"
+fu! s:annotationValue()
+ let pos = 0
+ if b:token == 'MONKEYS_AT'
+ let pos = b:bp
+ call s:nextToken()
+ return s:annotation(pos)
+ elseif b:token == 'LBRACE'
+ let pos = b:pos
+ call s:accept('LBRACE')
+ let buf = []
+ if b:token != 'RBRACE'
+ call add(buf, s:annotationValue())
+ while b:token == 'COMMA'
+ call s:nextToken()
+ if b:token == 'RPAREN'
+ break
+ endif
+ call add(buf, s:annotationValue())
+ endwhile
+ endif
+ call s:accept('RBRACE')
+ return buf
+ else
+ let b:mode = s:EXPR
+ return s:term1()
+ endif
+endfu
+
+" AnnotationsOpt = { '@' Annotation }
+fu! s:annotationsOpt()
+ if b:token != 'MONKEYS_AT'
+ return []
+ endif
+
+ let buf = []
+ while b:token != 'MONKEYS_AT'
+ let pos = b:pos
+ call s:nextToken()
+ call add(buf, s:annotation(pos))
+ endwhile
+ return buf
+endfu
+
+" {{{2
+" CompilationUnit {{{2
+" CompilationUnit = [ { "@" Annotation } PACKAGE Qualident ";"] {ImportDeclaration} {TypeDeclaration}
+fu! s:compilationUnit()
+ let unit = {'tag': 'TOPLEVEL', 'pos': b:pos}
+
+ let mods = {}
+ if b:token == 'MONKEYS_AT'
+ let mods = s:modifiersOpt()
+ endif
+
+ if b:token == 'PACKAGE'
+ if mods != {}
+ " checkNoMods(mods.flags)
+ let unit.packageAnnotations = mods.annotations
+ let mods = {}
+ endif
+ call s:nextToken()
+ let unit.pid = s:qualident()
+ let unit.package = java_parser#type2Str(unit.pid)
+ call s:accept('SEMI')
+ endif
+
+ let imports = []
+ let s:types = []
+ let checkForImports = 1
+ while b:token != 'EOF'
+ if b:pos <= b:errorEndPos
+ call s:skip(checkForImports, 0, 0, 0)
+ if b:token == 'EOF'
+ break
+ endif
+ endif
+ if checkForImports && mods == {} && b:token == 'IMPORT'
+ call add(imports, s:importDeclaration())
+ else
+ let def = s:typeDeclaration(mods)
+ "if (def instanceof JCExpressionStatement)
+ " def = ((JCExpressionStatement)def).expr
+ "endif
+ call add(s:types, def)
+ if def.tag == 'CLASSDEF'
+ let checkForImports = 0
+ endif
+ let mods = {}
+ endif
+ endwhile
+ let unit.imports = imports
+ let unit.types = s:types
+ unlet s:types
+
+ return unit
+endfu
+
+" ImportDeclaration = IMPORT [ STATIC ] Ident { "." Ident } [ "." "*" ] ";" {{{2
+" return fqn
+fu! s:importDeclaration()
+ " OAO: Usualy it is in one line.
+ if b:scanStrategy < 0
+ let idx = matchend(b:lines[b:line], '\(\s\+static\>\)\?\s\+\([_$a-zA-Z][_$a-zA-Z0-9_]*\)\(\s*\.\s*[_$a-zA-Z][_$a-zA-Z0-9_]*\)*\(\s*\.\s*\*\)\?;')
+ if idx != -1
+ let fqn = strpart(b:lines[b:line], b:col, idx-b:col-1)
+ let b:col = idx
+ let b:bp = b:idxes[b:line] + b:col
+ call s:nextToken()
+ return fqn
+ endif
+ endif
+
+
+ call s:Info('==import==')
+ let pos = b:pos
+ call s:nextToken()
+
+ let importStatic = 0
+ if b:token == 'STATIC'
+ " checkStaticImports()
+ let importStatic = 1
+ call s:nextToken()
+ endif
+
+ let pid = s:Ident(b:pos, s:ident())
+
+ "
+ let pos1 = b:pos
+ call s:accept('DOT')
+ if b:token == 'STAR'
+ let pid = s:Select(pos1, pid, '*')
+ call s:nextToken()
+ else
+ let pid = s:Select(pos1, pid, s:ident())
+ endif
+ while b:token == 'DOT'
+ let pos1 = b:pos
+ call s:accept('DOT')
+ if b:token == 'STAR'
+ let pid = s:Select(pos1, pid, '*')
+ call s:nextToken()
+ break
+ else
+ let pid = s:Select(pos1, pid, s:ident())
+ endif
+ endwhile
+ let fqn = java_parser#type2Str(pid)
+ if b:token != 'SEMI'
+ let fqn .= '<SEMI expected>'
+ endif
+ call s:accept('SEMI')
+ "return {'tag': 'IMPORT', 'pos': b:pos, 'qualid': pid, 'staticImport': importStatic}
+ return fqn
+endfu
+
+" TypeDeclaration = ClassOrInterfaceOrEnumDeclaration | ";" {{{2
+fu! s:typeDeclaration(mods)
+ let pos = b:pos
+ if a:mods == {} && b:token == 'SEMI'
+ call s:nextToken()
+ return {'tag': 'SKIP', 'pos': pos}
+ else
+ let dc = b:docComment
+ let mods = s:modifiersOpt(a:mods)
+ return s:classOrInterfaceOrEnumDeclaration(mods, dc)
+ endif
+endfu
+
+fu! s:classOrInterfaceOrEnumDeclaration(mods, dc)
+ call s:Info('== type ==')
+ if b:token == 'CLASS'
+ return s:classDeclaration(a:mods, a:dc)
+ elseif b:token == 'INTERFACE'
+ return s:interfaceDeclaration(a:mods, a:dc)
+ elseif b:token == 'ENUM'
+ "if !exists('b:allowEnums') || !b:allowEnums
+ " call s:SyntaxError("enums.not.supported.in.source")
+ "endif
+ return s:enumDeclaration(a:mods, a:dc)
+ else
+ let pos = b:pos
+ let errs = []
+ if b:token == 'IDENTIFIER'
+ call add(errs, s:ident())
+ call s:setErrorEndPos(b:bp)
+ endif
+ return s:SyntaxError("class.or.intf.or.enum.expected", pos, errs)
+ endif
+endfu
+
+" ClassDeclaration = CLASS Ident TypeParametersOpt [EXTENDS Type] [IMPLEMENTS TypeList] ClassBody {{{2
+fu! s:classDeclaration(mods, dc)
+ let type = s:ClassDef(b:pos, a:mods)
+
+ call s:accept('CLASS')
+ let type.name = s:ident()
+
+ let type.typarams = s:typeParametersOpt()
+
+ " extends
+ if b:token == 'EXTENDS'
+ call s:nextToken()
+ let type.extends = [s:type()]
+ endif
+
+ " implements
+ if b:token == 'IMPLEMENTS'
+ call s:nextToken()
+ let type.implements = s:typeList()
+ endif
+
+ let type.defs = s:classOrInterfaceBody(type.name, 0)
+ let type.endpos = b:pos
+ return type
+endfu
+
+" InterfaceDeclaration = INTERFACE Ident TypeParametersOpt [EXTENDS TypeList] InterfaceBody {{{2
+fu! s:interfaceDeclaration(mods, dc)
+ let type = s:ClassDef(b:pos, a:mods)
+
+ call s:accept('INTERFACE')
+ let type.name = s:ident()
+
+ let type.typarams = s:typeParametersOpt()
+
+ " extends
+ if b:token == 'EXTENDS'
+ call s:nextToken()
+ let type.extends = s:typeList()
+ endif
+
+ let type.defs = s:classOrInterfaceBody(type.name, 1)
+ let type.endpos = b:pos
+ return type
+endfu
+
+" EnumDeclaration = ENUM Ident [IMPLEMENTS TypeList] EnumBody {{{2
+fu! s:enumDeclaration(mods, dc)
+ let type = s:ClassDef(b:pos, a:mods)
+
+ call s:accept('ENUM')
+ let type.name = s:ident()
+
+ if b:token == 'IMPLEMENTS'
+ call s:nextToken()
+ let type.implements = s:typeList()
+ endif
+
+ let type.defs = s:enumBody(type.name)
+ let type.endpos = b:pos
+ return type
+endfu
+
+" EnumBody = "{" { EnumeratorDeclarationList } [","]
+" [ ";" {ClassBodyDeclaration} ] "}"
+fu! s:enumBody(enumName)
+ let defs = []
+ call s:accept('LBRACE')
+
+ if b:token == 'COMMA'
+ call s:nextToken()
+ elseif b:token != 'RBRACE' && b:token != 'SEMI'
+ call add(defs, s:enumeratorDeclaration(a:enumName))
+ while b:token == 'COMMA'
+ call s:nextToken()
+ if b:token == 'RBRACE' || b:token == 'SEMI'
+ break
+ endif
+ call add(defs, s:enumeratorDeclaration(a:enumName))
+ endwhile
+ if b:token != 'RBRACE' && b:token != 'SEMI'
+ call s:SyntaxError('comma.or.rbrace.or.semi. expected')
+ call s:nextToken()
+ endif
+ endif
+
+ if b:token == 'SEMI'
+ call s:nextToken()
+ while b:token != 'RBRACE' && b:token != 'EOF'
+ call add(defs, s:classOrInterfaceBodyDeclaration(a:enumName, 0))
+ if b:pos <= b:errorEndPos
+ call s:skip(0, 1, 1, 0)
+ endif
+ endwhile
+ endif
+
+ call s:accept('RBRACE')
+ return defs
+endfu
+
+" EnumeratorDeclaration = AnnotationsOpt [TypeArguments] IDENTIFIER [ Arguments ] [ "{" ClassBody "}" ]
+fu! s:enumeratorDeclaration(enumName)
+ let vardef = {'tag': 'VARDEF', 'pos': b:pos}
+
+ let dc = b:docComment
+ let flags = 16409 " s:BitOr(s:Flags.PUBLIC, s:Flags.STATIC, s:Flags.FINAL, s:Flags.ENUM)
+ " if b:deprecatedFlag
+ " let flags = 147481 " s:BitOr(flags, s:Flags.DEPRECATED)
+ " let b:deprecatedFlag = 1
+ " endif
+ let pos = b:pos
+ let annotations = s:annotationsOpt()
+ let vardef.mods = s:Modifiers(pos, flags, annotations)
+ let vardef.mods.pos = empty(annotations) ? -1 : pos
+ let vardef.m = s:Number2Bits(flags)
+
+ let typeArgs = s:typeArgumentsOpt()
+ let identPos = b:pos
+ let vardef.name = s:ident()
+ let vardef.n = vardef.name
+
+ let args = b:token == 'LPAREN' ? s:arguments() : []
+
+ " NOTE: It may be either a class body or a method body. I dont care, just record it
+ if b:token == 'LBRACE'
+ "call s:accept('LBRACE')
+ "if b:token !=# 'RBRACE'
+ " call s:gotoMatchEnd('{', '}')
+ "endif
+ "call s:accept('RBRACE')
+
+ "let mods1 = s:Modifiers(-1, s:BitOr(s:Flags.ENUM, s:Flags.STATIC), [])
+ let defs = s:classOrInterfaceBody('', 0)
+ "let body = s:ClassDef(identPos, mods1)
+ "let body.defs = defs
+ endif
+ let vardef.endpos = b:bp
+
+ " TODO: create new class
+
+ return vardef
+endfu
+
+" classOrInterfaceBody {{{2
+" ClassBody = "{" {ClassBodyDeclaration} "}"
+" InterfaceBody = "{" {InterfaceBodyDeclaration} "}"
+fu! s:classOrInterfaceBody(classname, isInterface)
+ call s:Info('== type definition body ==')
+ call s:accept('LBRACE')
+
+ if b:pos <= b:errorEndPos
+ call s:skip(0, 1, 0, 0)
+ if b:token == 'LBRACE'
+ call s:nextToken()
+ endif
+ endif
+
+ let defs = []
+ while b:token != 'RBRACE' && b:token != 'EOF'
+ let defs += s:classOrInterfaceBodyDeclaration(a:classname, a:isInterface)
+
+ if b:pos <= b:errorEndPos
+ call s:skip(0, 1, 1, 0)
+ endif
+ endwhile
+ call s:accept('RBRACE')
+ return defs
+endfu
+
+" classOrInterfaceBodyDeclaration {{{2
+" ClassBodyDeclaration =
+" ";"
+" | [STATIC] Block
+" | ModifiersOpt
+" ( Type Ident
+" ( VariableDeclaratorsRest ";" | MethodDeclaratorRest )
+" | VOID Ident MethodDeclaratorRest
+" | TypeParameters (Type | VOID) Ident MethodDeclaratorRest
+" | Ident ConstructorDeclaratorRest
+" | TypeParameters Ident ConstructorDeclaratorRest
+" | ClassOrInterfaceOrEnumDeclaration
+" )
+" InterfaceBodyDeclaration =
+" ";"
+" | ModifiersOpt Type Ident
+" ( ConstantDeclaratorsRest | InterfaceMethodDeclaratorRest ";" )
+fu! s:classOrInterfaceBodyDeclaration(classname, isInterface)
+ call s:Info('s:classOrInterfaceBodyDeclaration')
+ if b:token == 'SEMI'
+ call s:nextToken()
+ return [{'tag': 'BLOCK', 'pos': -1, 'endpos': -1, 'stats': []}]
+ else
+ if b:scanStrategy < 0
+ let result = s:classOrInterfaceBodyDeclaration_opt(a:classname, a:isInterface)
+ if !empty(result)
+ return result
+ endif
+ endif
+
+
+ let dc = b:docComment
+ let pos = b:bp
+ let mods = s:modifiersOpt()
+
+ if b:token =~# '^\(CLASS\|INTERFACE\|ENUM\)$'
+ return [s:classOrInterfaceOrEnumDeclaration(mods, dc)]
+
+ " [STATIC] block
+ elseif b:token == 'LBRACE' && !a:isInterface
+ return [s:block(pos, mods.flags, b:scanStrategy < 1)]
+
+ else
+ let typarams = s:typeParametersOpt()
+
+ let token = b:token
+ let name = b:name
+
+ let type = copy(s:TTree)
+ let isVoid = b:token == 'VOID'
+ if isVoid
+ let type = {'tag': 'TYPEIDENT', 'pos': pos, 'typetag': 'void'} " FIXME
+ let type.value = ''
+ call s:nextToken()
+ else
+ let time = reltime()
+ let type = s:type()
+ let b:et_perf .= "\r" . reltimestr(reltime(time)) . ' s:type() '
+ endif
+
+
+ " ctor
+ if b:token == 'LPAREN' && !a:isInterface && type.tag == 'IDENT'
+ if a:isInterface || name != a:classname
+ call s:SyntaxError('invalid.meth.decl.ret.type.req')
+ endif
+ return [s:methodDeclaratorRest(pos, mods, type, name, typarams, a:isInterface, 1, dc)]
+ else
+ let name = s:ident()
+ " method
+ if b:token == 'LPAREN'
+ return [s:methodDeclaratorRest(pos, mods, type, name, typarams, a:isInterface, isVoid, dc)]
+ " field
+ elseif !isVoid && len(typarams) == 0
+ let defs = s:variableDeclaratorsRest(pos, mods, type, name, a:isInterface, dc, copy([]))
+ call s:accept('SEMI')
+ return defs
+ else
+ call s:SyntaxError("LPAREN expected")
+ return [{}]
+ endif
+ endif
+ endif
+ endif
+endfu
+
+" OAO: short way for common declaration of field or method, not for generic type yet.
+fu! s:classOrInterfaceBodyDeclaration_opt(classname, isInterface)
+ let str = b:lines[b:line]
+ let idx = matchend(str, s:RE_MEMBER_HEADER)
+ if idx != -1
+ let subs = split(substitute(strpart(str, 0, idx), s:RE_MEMBER_HEADER, '\1;\2;\3', ''), ';')
+ let name_ = subs[2]
+ let type_ = subs[1]
+ let flag_ = s:String2Flags(subs[0])
+
+" if type_ =~# '^\(class\|interface\|enum\)$'
+" return [s:classOrInterfaceOrEnumDeclaration(mods, dc)]
+" else
+ " methodDeclarator
+ let idx = matchend(str, '^\s*[,=;(]', idx)-1
+ if str[idx] == '('
+ let methoddef = s:methodDeclaratorRest_opt(b:pos, flag_, type_, name_, [], a:isInterface, type_ == 'void', '', str, idx)
+ if !empty(methoddef)
+ return [methoddef]
+ endif
+
+ " variableDeclarator
+ elseif str[idx] =~ '[=;]'
+ let vardef = {'tag': 'VARDEF', 'pos': b:pos, 'name': name_, 'n': name_, 'vartype': type_, 't': type_, 'm': flag_}
+ call s:gotoSemi()
+ call s:accept('SEMI')
+ let vardef.pos_end = b:pos
+ return [vardef]
+
+ " variableDeclarators
+ elseif str[idx] == ','
+ let ie = matchend(str, '^\(,\s*'. s:RE_IDENTIFIER .'\s*\)*;', idx)
+ if ie != -1
+ let vardef = {'tag': 'VARDEF', 'pos': b:pos, 'name': name_, 'n': name_, 'vartype': type_, 't': type_, 'm': flag_}
+ let vars = [vardef]
+ for item in split(substitute(strpart(str, idx+1, ie-idx-2), '\s', '', 'g'), ',')
+ let vardef = copy(vardef)
+ let vardef.name = item
+ let vardef.n = item
+ call add(vars, vardef)
+ endfor
+ let b:col = ie
+ let b:bp = b:idxes[b:line] + b:col
+ call s:nextToken()
+ return vars
+ endif
+ endif
+" endif
+ endif
+
+ let RE_CTOR_HEADER = '^\s*\(\(public\|protected\|private\)\s\+\)\=\C' .a:classname. '\s*('
+ let ie = matchend(str, RE_CTOR_HEADER)
+ if ie != -1 && !a:isInterface
+ let flag_ = s:String2Flags(substitute(strpart(str, 0, ie), RE_CTOR_HEADER, '\1', ''))
+ let methoddef = s:methodDeclaratorRest_opt(b:pos, flag_, a:classname, a:classname, [], a:isInterface, 1, '', str, ie-1)
+ if !empty(methoddef)
+ return [methoddef]
+ endif
+ endif
+
+ let RE_METHOD_HEADER = '^\s*\(' .s:RE_IDENTIFIER. '\%(\s*\.\s*' .s:RE_IDENTIFIER. '\)*\%(\s*\[\s*\]\)\=\)\s\+\(' .s:RE_IDENTIFIER. '\)\s*('
+ let ie = matchend(str, RE_METHOD_HEADER)
+ if ie != -1
+ let subs = split(substitute(strpart(str, 0, ie), RE_METHOD_HEADER, '\1;\2', ''), ';')
+ let methoddef = s:methodDeclaratorRest_opt(b:pos, 0, subs[0], subs[1], [], a:isInterface, subs[0] == 'void', '', str, ie-1)
+ if !empty(methoddef)
+ return [methoddef]
+ endif
+ endif
+endfu
+
+
+" MethodDeclaratorRest = {{{2
+" FormalParameters BracketsOpt [Throws TypeList] ( MethodBody | [DEFAULT AnnotationValue] ";")
+" VoidMethodDeclaratorRest = FormalParameters [Throws TypeList] ( MethodBody | ";")
+" InterfaceMethodDeclaratorRest = FormalParameters BracketsOpt [THROWS TypeList] ";"
+" VoidInterfaceMethodDeclaratorRest = FormalParameters [THROWS TypeList] ";"
+" ConstructorDeclaratorRest = "(" FormalParameterListOpt ")" [THROWS TypeList] MethodBody
+fu! s:methodDeclaratorRest(pos, mods, type, name, typarams, isInterface, isVoid, dc)
+ let time = reltime()
+ let methoddef = {'tag': 'METHODDEF', 'pos': a:pos, 'name': a:name, 'mods': a:mods, 'restype': a:type, 'typarams': a:typarams}
+ let methoddef.n = a:name
+ let methoddef.m = s:Number2Bits(a:mods.flags)
+ let methoddef.r = java_parser#type2Str(a:type)
+
+ " parameters
+ let methoddef.params = s:formalParameters()
+
+ " BracketsOpt
+ if !a:isVoid
+ let methoddef.r = java_parser#type2Str(s:bracketsOpt(a:type))
+ endif
+
+
+ " throws
+ if b:token == 'THROWS'
+ call s:nextToken()
+
+ " thrown = qualidentList()
+ let ts = [s:qualident()]
+ while b:token == 'COMMA'
+ call s:nextToken()
+ call add(ts, s:qualident())
+ endwhile
+ let methoddef.throws = ts
+ endif
+
+ " method body
+ if b:token == 'LBRACE'
+ let methoddef.body = s:block(b:pos, 0, b:scanStrategy < 1)
+ else
+ if b:token == 'DEFAULT'
+ call s:accept('DEFAULT')
+ let methoddef.defaultValue = s:annotationValue()
+ endif
+ call s:accept('SEMI')
+
+ if b:pos <= b:errorEndPos
+ call s:skip(0, 1, 0, 0)
+ if b:token == 'LBRACE'
+ let methoddef.body = s:block(b:pos, 0, b:scanStrategy < 1)
+ endif
+ endif
+ endif
+
+ let methoddef.d = s:method2Str(methoddef)
+ let b:et_perf .= "\r" . reltimestr(reltime(time)) . ' methodrest() '
+ return methoddef
+endfu
+
+" method header declared in one line,
+" NOTE: RE_FORMAL_PARAM_LIST do not recognize varargs and nested comments
+fu! s:methodDeclaratorRest_opt(pos, mods, type, name, typarams, isInterface, isVoid, dc, str, idx)
+ let str = a:str
+ let idx = a:idx
+
+ " params
+ let idxend = matchend(str, '^(\s*)', idx) " no params
+ if idxend == -1
+ let idxend = matchend(str, '^(\s*' . s:RE_FORMAL_PARAM_LIST . '\s*)', idx)
+ endif
+ if idxend == -1
+ return
+ endif
+
+ let methoddef = {'tag': 'METHODDEF', 'pos': a:pos, 'name': a:name, 'n': a:name, 'm': a:mods, 'r': a:type}
+
+ " params
+ let methoddef.params = []
+ let s = strpart(str, idx+1, idxend-idx-2)
+ if s !~ '^\s*$'
+ for item in split(s, ',')
+ let subs = split(substitute(item, s:RE_FORMAL_PARAM2, '\2;\5', ''), ';')
+ let param = {'tag': 'VARDEF', 'pos': -1}
+ let param.name = subs[1]
+ let param.vartype = substitute(subs[0], ' ', '', 'g')
+ let param.m = s:Flags.PARAMETER
+ call add(methoddef.params, param)
+ endfor
+ endif
+
+ " throws
+ let idx2 = matchend(str, '^\s*' . s:RE_THROWS, idxend)
+ let idx = idx2 == -1 ? idxend : idx2
+ if idx2 != -1
+ "let throws = strpart(str, idxend, idx-idxend)
+ endif
+
+ " in interface
+ if a:isInterface
+ let idx = matchend(str, '^\s*;', idx)
+ if idx != -1
+ let b:token = 'SEMI'
+ let b:col = idx
+ let b:bp = b:idxes[b:line] + b:col
+ let b:pos = b:bp - 1
+ let methoddef.d = substitute(str, '^\s*\([^{]*\)\s*;\=$', '\1', '')
+ return methoddef
+ endif
+ endif
+
+ let idx = matchend(str, '^\s*{', idx)
+ if idx == -1
+ let idx = matchend(b:lines[b:line+1], '^\s*{')
+ if idx != -1
+ let b:line += 1
+ endif
+ endif
+ if idx != -1
+ let b:token = 'LBRACE'
+ let b:col = idx
+ let b:bp = b:idxes[b:line] + b:col
+ let b:pos = b:bp - 1
+ let methoddef.d = substitute(str, '^\s*\([^{]*\)\s*{\=$', '\1', '')
+ let methoddef.body = s:block(b:pos, 0, b:scanStrategy < 1)
+ return methoddef
+ endif
+endfu
+
+" VariableDeclarators = VariableDeclarator { "," VariableDeclarator } {{{2
+fu! s:variableDeclarators(mods, type, vdefs)
+ return s:variableDeclaratorsRest(b:pos, a:mods, a:type, s:ident(), 0, '', a:vdefs)
+endfu
+
+" VariableDeclaratorsRest = VariableDeclaratorRest { "," VariableDeclarator }
+" ConstantDeclaratorsRest = ConstantDeclaratorRest { "," ConstantDeclarator }
+fu! s:variableDeclaratorsRest(pos, mods, type, name, reqInit, dc, vdefs)
+ call add(a:vdefs, s:variableDeclaratorRest(a:pos, a:mods, a:type, a:name, a:reqInit, a:dc))
+ while b:token == 'COMMA'
+ call s:nextToken()
+ call add(a:vdefs, s:variableDeclarator(a:mods, a:type, a:reqInit, a:dc))
+ endwhile
+ return a:vdefs
+endfu
+
+" VariableDeclarator = Ident VariableDeclaratorRest
+" ConstantDeclarator = Ident ConstantDeclaratorRest
+fu! s:variableDeclarator(mods, type, reqInit, dc)
+ return s:variableDeclaratorRest(b:pos, a:mods, a:type, s:ident(), a:reqInit, a:dc)
+endfu
+
+" VariableDeclaratorRest = BracketsOpt ["=" VariableInitializer]
+" ConstantDeclaratorRest = BracketsOpt "=" VariableInitializer
+fu! s:variableDeclaratorRest(pos, mods, type, name, reqInit, dc)
+ let vardef = s:VarDef(a:pos, a:mods, a:name, s:bracketsOpt(a:type))
+ let vardef.n = vardef.name
+ let vardef.m = a:mods == {} ? '0' : s:Number2Bits(a:mods.flags)
+ let vardef.t = java_parser#type2Str(vardef.vartype)
+
+ if b:token == 'EQ'
+ call s:nextToken()
+ call s:Info('field init ' . b:token)
+ let vardef.init = s:variableInitializer()
+ elseif a:reqInit
+ echo '[syntax error]:' . s:token2string('EQ') . " expected"
+ endif
+
+ let vardef.endpos = b:pos
+ return vardef
+endfu
+
+fu! s:variableDeclaratorId(mods, type)
+ let vardef = s:VarDef(b:pos, a:mods, s:ident(), a:type)
+ if len(a:mods.flags) <= 34 " (a:mods.flags & s:Flags.VARARGS) == 0
+ let vardef.type = s:bracketsOpt(vardef.vartype)
+ endif
+ return vardef
+endfu
+
+" {{{2
+" TypeParametersOpt = ["<" TypeParameter {"," TypeParameter} ">"] {{{2
+fu! s:typeParametersOpt()
+ if b:token == 'LT'
+ "call checkGenerics()
+ let typarams = []
+ call s:nextToken()
+ call add(typarams, s:typeParameter())
+ while b:token == 'COMMA'
+ call s:nextToken()
+ call add(typarams, s:typeParameter())
+ endwhile
+ call s:accept('GT')
+ return typarams
+ else
+ return []
+ endif
+endfu
+
+" TypeParameter = TypeVariable [TypeParameterBound] {{{2
+" TypeParameterBound = EXTENDS Type {"&" Type}
+" TypeVariable = Ident
+fu! s:typeParameter()
+ let pos = b:pos
+ let name = s:ident()
+ let bounds = []
+ if b:token == 'EXTENDS'
+ call s:nextToken()
+ call add(bounds, s:type())
+ while b:token == 'AMP'
+ call s:nextToken()
+ call add(bounds, s:type())
+ endwhile
+ endif
+
+ return {'tag': 'TYPEPARAMETER', 'pos': pos, 'name': name, 'bounds': bounds}
+endfu
+
+" FormalParameters = "(" [ FormalParameterList ] ")" {{{2
+" FormalParameterList = [ FormalParameterListNovarargs , ] LastFormalParameter
+" FormalParameterListNovarargs = [ FormalParameterListNovarargs , ] FormalParameter
+fu! s:formalParameters()
+ let params = []
+ let lastParam = {}
+ call s:accept('LPAREN')
+ if b:token != 'RPAREN'
+ let lastParam = s:formalParameter()
+ call add(params, lastParam)
+ while b:token == 'COMMA' && len(lastParam.mods.flags) <= 34 " (lastParam.mods.flags & s:Flags.VARARGS) == 0
+ call s:nextToken()
+ let lastParam = s:formalParameter()
+ call add(params, lastParam)
+ endwhile
+ endif
+ call s:accept('RPAREN')
+ return params
+endfu
+
+" FormalParameter = { FINAL | '@' Annotation } Type VariableDeclaratorId {{{2
+" LastFormalParameter = { FINAL | '@' Annotation } Type '...' Ident | FormalParameter
+fu! s:optFinal(flags)
+ let mods = s:modifiersOpt()
+ " checkNoMods(mods.flags & ~(Flags.FINAL | Flags.DEPRECATED))
+ let mods.flags = s:BitOr(mods.flags, a:flags)
+ return mods
+endfu
+
+" OAO: optional FINAL for parameter
+fu! s:optFinalParameter()
+ let mods = {'tag': 'MODIFIERS', 'pos': b:pos, 'flags': s:Flags.PARAMETER, 'annotations': []}
+ if b:token == 'FINAL'
+ let mods.flags = '1000000000000000000000000000010000'
+ call s:nextToken()
+ endif
+ return mods
+endfu
+
+fu! s:formalParameter()
+ let mods = s:optFinalParameter()
+ let type = s:type()
+
+ if b:token == 'ELLIPSIS'
+ " checkVarargs()
+ let mods.flags = '1' . mods.flags " s:BitOr_binary(mods.flags, s:Flags.VARARGS)
+ let type = s:TypeArray(b:pos, type)
+ call s:nextToken()
+ endif
+
+ return s:variableDeclaratorId(mods, type)
+endfu
+
+" {{{2
+" auxiliary methods {{{2
+let s:MapToken2Tag = {'BARBAR': 'OR', 'AMPAMP': 'AND', 'BAR': 'BITOR', 'BAREQ': 'BITOR_ASG', 'CARET': 'BITXOR', 'CARETEQ': 'BITXOR_ASG', 'AMP': 'BITAND', 'AMPEQ': 'BITAND_ASG', 'EQEQ': 'EQ', 'BANGEQ': 'NE', 'LT': 'LT', 'GT': 'GT', 'LTEQ': 'LE', 'GTEQ': 'GE', 'LTLT': 'SL', 'LTLTEQ': 'SL_ASG', 'GTGT': 'SR', 'GTGTEQ': 'SR_ASG', 'GTGTGT': 'USR', 'GTGTGTEQ': 'USR_ASG', 'PLUS': 'PLUS', 'PLUSEQ': 'PLUS_ASG', 'SUB': 'MINUS', 'SUBEQ': 'MINUS_ASG', 'STAR': 'MUL', 'STAREQ': 'MUL_ASG', 'SLASH': 'DIV', 'SLASHEQ': 'DIV_ASG', 'PERCENT': 'MOD', 'PERCENTEQ': 'MOD_ASG', 'INSTANCEOF': 'TYPETEST'}
+let s:opprecedences = {'notExpression': -1, 'noPrec': 0, 'assignPrec': 1, 'assignopPrec': 2, 'condPrec': 3, 'orPrec': 4, 'andPrec': 5, 'bitorPrec': 6, 'bitxorPrec': 7, 'bitandPrec': 8, 'eqPrec': 9, 'ordPrec': 10, 'shiftPrec': 11, 'addPrec': 12, 'mulPrec': 13, 'prefixPrec': 14, 'postfixPrec': 15, 'precCount': 16}
+
+fu! s:checkExprStat(t)
+ if a:t.tag =~# '^\(PREINC\|PREDEC\|POSTINC\|POSTDEC\|ASSIGN\|BITOR_ASG\|BITXOR_ASG\|BITAND_ASG\|SL_ASG\|SR_ASG\|USR_ASG\|PLUS_ASG\|MINUS_ASG\|MUL_ASG\|DIV_ASG\|MOD_ASG\|APPLY\|NEWCLASS\|ERRONEOUS\)$'
+ return a:t
+ else
+ call s:SyntaxError('not.stmt')
+ return {'tag': 'ERRONEOUS', 'pos': b:pos, 'errs': [a:t]}
+ endif
+endfu
+
+fu! s:prec(token)
+ let oc = s:optag(a:token)
+ return oc == -1 ? -1 : s:opPrec(oc)
+endfu
+
+fu! s:opPrec(tag)
+ if a:tag =~# '^\(POS\|NEG\|NOT\|COMPL\|PREINC\|PREDEC\)$'
+ return s:opprecedences.prefixPrec
+ elseif a:tag =~# '^\(POSTINC\|POSTDEC\|NULLCHK\)$'
+ return s:opprecedences.postfixPrec
+ elseif a:tag == 'ASSIGN'
+ return s:opprecedences.assignPrec
+ elseif a:tag =~# '^\(BITOR_ASG\|BITXOR_ASG\|BITAND_ASG\|SL_ASG\|SR_ASG\|USR_ASG\|PLUS_ASG\|MINUS_ASG\|MUL_ASG\|DIV_ASG\|MOD_ASG\)$'
+ return s:opprecedences.assignopPrec
+ elseif a:tag == 'OR'
+ return s:opprecedences.orPrec
+ elseif a:tag == 'AND'
+ return s:opprecedences.andPrec
+ elseif a:tag =~# '^\(EQ\|NE\)$'
+ return s:opprecedences.eqPrec
+ elseif a:tag =~# '^\(LT\|GT\|LE\|GE\)$'
+ return s:opprecedences.ordPrec
+ elseif a:tag == 'BITOR'
+ return s:opprecedences.bitorPrec
+ elseif a:tag == 'BITXOR'
+ return s:opprecedences.bitxorPrec
+ elseif a:tag == 'BITAND'
+ return s:opprecedences.bitandPrec
+ elseif a:tag =~# '^\(SL\|SR\|USR\)$'
+ return s:opprecedences.shiftPrec
+ elseif a:tag =~# '^\(PLUS\|MINUS\)$'
+ return s:opprecedences.addPrec
+ elseif a:tag =~# '^\(MUL\|DIV\|MOD\)$'
+ return s:opprecedences.mulPrec
+ elseif a:tag == 'TYPETEST'
+ return s:opprecedences.ordPrec
+ else
+ return -1
+ endif
+endfu
+
+fu! s:optag(token)
+ return get(s:MapToken2Tag, a:token, -1)
+endfu
+
+fu! s:unoptag(token)
+ if a:token == 'PLUS'
+ return 'POS'
+ elseif a:token == 'SUB'
+ return 'NEG'
+ elseif a:token == 'BANG'
+ return 'NOT'
+ elseif a:token == 'TILDE'
+ return 'COMPL'
+ elseif a:token == 'PLUSPLUS'
+ return 'PREINC'
+ elseif a:token == 'SUBSUB'
+ return 'PREDEC'
+ else
+ return -1
+ endif
+endfu
+
+fu! s:typetag(token)
+ if a:token =~# '\(BYTE\|CHAR\|SHORT\|INT\|LONG\|FLOAT\|DOUBLE\|BOOLEAN\)'
+ return tolower(a:token)
+ else
+ return -1
+ endif
+endfu
+
+"}}}1
+" vim:set fdm=marker sw=2 nowrap:
diff --git a/base/vim/bundle/javacomplete/autoload/javacomplete.vim b/base/vim/bundle/javacomplete/autoload/javacomplete.vim
new file mode 100644
index 0000000..c095dad
--- /dev/null
+++ b/base/vim/bundle/javacomplete/autoload/javacomplete.vim
@@ -0,0 +1,2932 @@
+" Vim completion script - hit 80% complete tasks
+" Version: 0.77.1.2
+" Language: Java
+" Maintainer: cheng fang <fangread@yahoo.com.cn>
+" Last Change: 2011-01-30
+" Copyright: Copyright (C) 2006-2007 cheng fang. All rights reserved.
+" License: Vim License (see vim's :help license)
+
+
+" constants {{{1
+" input context type
+let s:CONTEXT_AFTER_DOT = 1
+let s:CONTEXT_METHOD_PARAM = 2
+let s:CONTEXT_IMPORT = 3
+let s:CONTEXT_IMPORT_STATIC = 4
+let s:CONTEXT_PACKAGE_DECL = 6
+let s:CONTEXT_NEED_TYPE = 7
+let s:CONTEXT_OTHER = 0
+
+
+let s:ARRAY_TYPE_MEMBERS = [
+\ {'kind': 'm', 'word': 'clone(', 'abbr': 'clone()', 'menu': 'Object clone()', },
+\ {'kind': 'm', 'word': 'equals(', 'abbr': 'equals()', 'menu': 'boolean equals(Object)', },
+\ {'kind': 'm', 'word': 'getClass(', 'abbr': 'getClass()', 'menu': 'Class Object.getClass()', },
+\ {'kind': 'm', 'word': 'hashCode(', 'abbr': 'hashCode()', 'menu': 'int hashCode()', },
+\ {'kind': 'f', 'word': 'length', 'menu': 'int'},
+\ {'kind': 'm', 'word': 'notify(', 'abbr': 'notify()', 'menu': 'void Object.notify()', },
+\ {'kind': 'm', 'word': 'notifyAll(', 'abbr': 'notifyAll()', 'menu': 'void Object.notifyAll()', },
+\ {'kind': 'm', 'word': 'toString(', 'abbr': 'toString()', 'menu': 'String toString()', },
+\ {'kind': 'm', 'word': 'wait(', 'abbr': 'wait()', 'menu': 'void Object.wait() throws InterruptedException', },
+\ {'kind': 'm', 'dup': 1, 'word': 'wait(', 'abbr': 'wait()', 'menu': 'void Object.wait(long timeout) throws InterruptedException', },
+\ {'kind': 'm', 'dup': 1, 'word': 'wait(', 'abbr': 'wait()', 'menu': 'void Object.wait(long timeout, int nanos) throws InterruptedException', }]
+
+let s:ARRAY_TYPE_INFO = {'tag': 'CLASSDEF', 'name': '[', 'ctors': [],
+\ 'fields': [{'n': 'length', 'm': '1', 't': 'int'}],
+\ 'methods':[
+\ {'n': 'clone', 'm': '1', 'r': 'Object', 'p': [], 'd': 'Object clone()'},
+\ {'n': 'equals', 'm': '1', 'r': 'boolean', 'p': ['Object'], 'd': 'boolean Object.equals(Object obj)'},
+\ {'n': 'getClass', 'm': '100010001', 'r': 'Class', 'p': [], 'd': 'Class Object.getClass()'},
+\ {'n': 'hashCode', 'm': '100000001', 'r': 'int', 'p': [], 'd': 'int Object.hashCode()'},
+\ {'n': 'notify', 'm': '100010001', 'r': 'void', 'p': [], 'd': 'void Object.notify()'},
+\ {'n': 'notifyAll','m': '100010001', 'r': 'void', 'p': [], 'd': 'void Object.notifyAll()'},
+\ {'n': 'toString', 'm': '1', 'r': 'String', 'p': [], 'd': 'String Object.toString()'},
+\ {'n': 'wait', 'm': '10001', 'r': 'void', 'p': [], 'd': 'void Object.wait() throws InterruptedException'},
+\ {'n': 'wait', 'm': '100010001', 'r': 'void', 'p': ['long'], 'd': 'void Object.wait(long timeout) throws InterruptedException'},
+\ {'n': 'wait', 'm': '10001', 'r': 'void', 'p': ['long','int'], 'd': 'void Object.wait(long timeout, int nanos) throws InterruptedException'},
+\ ]}
+
+let s:PRIMITIVE_TYPE_INFO = {'tag': 'CLASSDEF', 'name': '!', 'fields': [{'n': 'class','m': '1','t': 'Class'}]}
+
+let s:JSP_BUILTIN_OBJECTS = {'session': 'javax.servlet.http.HttpSession',
+\ 'request': 'javax.servlet.http.HttpServletRequest',
+\ 'response': 'javax.servlet.http.HttpServletResponse',
+\ 'pageContext': 'javax.servlet.jsp.PageContext',
+\ 'application': 'javax.servlet.ServletContext',
+\ 'config': 'javax.servlet.ServletConfig',
+\ 'out': 'javax.servlet.jsp.JspWriter',
+\ 'page': 'javax.servlet.jsp.HttpJspPage', }
+
+
+let s:PRIMITIVE_TYPES = ['boolean', 'byte', 'char', 'int', 'short', 'long', 'float', 'double']
+let s:KEYWORDS_MODS = ['public', 'private', 'protected', 'static', 'final', 'synchronized', 'volatile', 'transient', 'native', 'strictfp', 'abstract']
+let s:KEYWORDS_TYPE = ['class', 'interface', 'enum']
+let s:KEYWORDS = s:PRIMITIVE_TYPES + s:KEYWORDS_MODS + s:KEYWORDS_TYPE + ['super', 'this', 'void'] + ['assert', 'break', 'case', 'catch', 'const', 'continue', 'default', 'do', 'else', 'extends', 'finally', 'for', 'goto', 'if', 'implements', 'import', 'instanceof', 'interface', 'new', 'package', 'return', 'switch', 'throw', 'throws', 'try', 'while', 'true', 'false', 'null']
+
+let s:PATH_SEP = ':'
+let s:FILE_SEP = '/'
+if has("win32") || has("win64") || has("win16") || has("dos32") || has("dos16")
+ let s:PATH_SEP = ';'
+ let s:FILE_SEP = '\'
+endif
+
+let s:RE_BRACKETS = '\%(\s*\[\s*\]\)'
+let s:RE_IDENTIFIER = '[a-zA-Z_$][a-zA-Z0-9_$]*'
+let s:RE_QUALID = s:RE_IDENTIFIER. '\%(\s*\.\s*' .s:RE_IDENTIFIER. '\)*'
+
+let s:RE_REFERENCE_TYPE = s:RE_QUALID . s:RE_BRACKETS . '*'
+let s:RE_TYPE = s:RE_REFERENCE_TYPE
+
+let s:RE_TYPE_ARGUMENT = '\%(?\s\+\%(extends\|super\)\s\+\)\=' . s:RE_TYPE
+let s:RE_TYPE_ARGUMENTS = '<' . s:RE_TYPE_ARGUMENT . '\%(\s*,\s*' . s:RE_TYPE_ARGUMENT . '\)*>'
+let s:RE_TYPE_WITH_ARGUMENTS_I = s:RE_IDENTIFIER . '\s*' . s:RE_TYPE_ARGUMENTS
+let s:RE_TYPE_WITH_ARGUMENTS = s:RE_TYPE_WITH_ARGUMENTS_I . '\%(\s*' . s:RE_TYPE_WITH_ARGUMENTS_I . '\)*'
+
+let s:RE_TYPE_MODS = '\%(public\|protected\|private\|abstract\|static\|final\|strictfp\)'
+let s:RE_TYPE_DECL_HEAD = '\(class\|interface\|enum\)[ \t\n\r ]\+'
+let s:RE_TYPE_DECL = '\<\C\(\%(' .s:RE_TYPE_MODS. '\s\+\)*\)' .s:RE_TYPE_DECL_HEAD. '\(' .s:RE_IDENTIFIER. '\)[< \t\n\r ]'
+
+let s:RE_ARRAY_TYPE = '^\s*\(' .s:RE_QUALID . '\)\(' . s:RE_BRACKETS . '\+\)\s*$'
+let s:RE_SELECT_OR_ACCESS = '^\s*\(' . s:RE_IDENTIFIER . '\)\s*\(\[.*\]\)\=\s*$'
+let s:RE_ARRAY_ACCESS = '^\s*\(' . s:RE_IDENTIFIER . '\)\s*\(\[.*\]\)\+\s*$'
+let s:RE_CASTING = '^\s*(\(' .s:RE_QUALID. '\))\s*\(' . s:RE_IDENTIFIER . '\)\>'
+
+let s:RE_KEYWORDS = '\<\%(' . join(s:KEYWORDS, '\|') . '\)\>'
+
+
+" local variables {{{1
+let b:context_type = s:CONTEXT_OTHER
+"let b:statement = '' " statement before cursor
+let b:dotexpr = '' " expression ends with '.'
+let b:incomplete = '' " incomplete word: 1. dotexpr.method(|) 2. new classname(|) 3. dotexpr.ab|, 4. ja|, 5. method(|
+let b:errormsg = ''
+
+" script variables {{{1
+let s:cache = {} " FQN -> member list, e.g. {'java.lang.StringBuffer': classinfo, 'java.util': packageinfo, '/dir/TopLevelClass.java': compilationUnit}
+let s:files = {} " srouce file path -> properties, e.g. {filekey: {'unit': compilationUnit, 'changedtick': tick, }}
+let s:history = {} "
+
+
+" This function is used for the 'omnifunc' option. {{{1
+function! javacomplete#Complete(findstart, base)
+ if a:findstart
+ let s:et_whole = reltime()
+ let start = col('.') - 1
+ let s:log = []
+
+ " reset enviroment
+ let b:dotexpr = ''
+ let b:incomplete = ''
+ let b:context_type = s:CONTEXT_OTHER
+
+ let statement = s:GetStatement()
+ call s:WatchVariant('statement: "' . statement . '"')
+
+ if statement =~ '[.0-9A-Za-z_]\s*$'
+ let valid = 1
+ if statement =~ '\.\s*$'
+ let valid = statement =~ '[")0-9A-Za-z_\]]\s*\.\s*$' && statement !~ '\<\H\w\+\.\s*$' && statement !~ '\<\(abstract\|assert\|break\|case\|catch\|const\|continue\|default\|do\|else\|enum\|extends\|final\|finally\|for\|goto\|if\|implements\|import\|instanceof\|interface\|native\|new\|package\|private\|protected\|public\|return\|static\|strictfp\|switch\|synchronized\|throw\|throws\|transient\|try\|volatile\|while\|true\|false\|null\)\.\s*$'
+ endif
+ if !valid
+ return -1
+ endif
+
+ let b:context_type = s:CONTEXT_AFTER_DOT
+
+ " import or package declaration
+ if statement =~# '^\s*\(import\|package\)\s\+'
+ let statement = substitute(statement, '\s\+\.', '.', 'g')
+ let statement = substitute(statement, '\.\s\+', '.', 'g')
+ if statement =~ '^\s*import\s\+'
+ let b:context_type = statement =~# '\<static\s\+' ? s:CONTEXT_IMPORT_STATIC : s:CONTEXT_IMPORT
+ let b:dotexpr = substitute(statement, '^\s*import\s\+\(static\s\+\)\?', '', '')
+ else
+ let b:context_type = s:CONTEXT_PACKAGE_DECL
+ let b:dotexpr = substitute(statement, '\s*package\s\+', '', '')
+ endif
+
+ " String literal
+ elseif statement =~ '"\s*\.\s*$'
+ let b:dotexpr = substitute(statement, '\s*\.\s*$', '\.', '')
+ return start - strlen(b:incomplete)
+
+ else
+ " type declaration NOTE: not supported generic yet.
+ let idx_type = matchend(statement, '^\s*' . s:RE_TYPE_DECL)
+ if idx_type != -1
+ let b:dotexpr = strpart(statement, idx_type)
+ " return if not after extends or implements
+ if b:dotexpr !~ '^\(extends\|implements\)\s\+'
+ return -1
+ endif
+ let b:context_type = s:CONTEXT_NEED_TYPE
+ endif
+
+ let b:dotexpr = s:ExtractCleanExpr(statement)
+ endif
+
+ " all cases: " java.ut|" or " java.util.|" or "ja|"
+ let b:incomplete = strpart(b:dotexpr, strridx(b:dotexpr, '.')+1)
+ let b:dotexpr = strpart(b:dotexpr, 0, strridx(b:dotexpr, '.')+1)
+ return start - strlen(b:incomplete)
+
+
+ " method parameters, treat methodname or 'new' as an incomplete word
+ elseif statement =~ '(\s*$'
+ " TODO: Need to exclude method declaration?
+ let b:context_type = s:CONTEXT_METHOD_PARAM
+ let pos = strridx(statement, '(')
+ let s:padding = strpart(statement, pos+1)
+ let start = start - (len(statement) - pos)
+
+ let statement = substitute(statement, '\s*(\s*$', '', '')
+
+ " new ClassName?
+ let str = matchstr(statement, '\<new\s\+' . s:RE_QUALID . '$')
+ if str != ''
+ let str = substitute(str, '^new\s\+', '', '')
+ if !s:IsKeyword(str)
+ let b:incomplete = '+'
+ let b:dotexpr = str
+ return start - len(b:dotexpr)
+ endif
+
+ " normal method invocations
+ else
+ let pos = match(statement, '\s*' . s:RE_IDENTIFIER . '$')
+ " case: "method(|)", "this(|)", "super(|)"
+ if pos == 0
+ let statement = substitute(statement, '^\s*', '', '')
+ " treat "this" or "super" as a type name.
+ if statement == 'this' || statement == 'super'
+ let b:dotexpr = statement
+ let b:incomplete = '+'
+ return start - len(b:dotexpr)
+
+ elseif !s:IsKeyword(statement)
+ let b:incomplete = statement
+ return start - strlen(b:incomplete)
+ endif
+
+ " case: "expr.method(|)"
+ elseif statement[pos-1] == '.' && !s:IsKeyword(strpart(statement, pos))
+ let b:dotexpr = s:ExtractCleanExpr(strpart(statement, 0, pos))
+ let b:incomplete = strpart(statement, pos)
+ return start - strlen(b:incomplete)
+ endif
+ endif
+ endif
+
+ return -1
+ endif
+
+
+ " Return list of matches.
+
+ call s:WatchVariant('b:context_type: "' . b:context_type . '" b:incomplete: "' . b:incomplete . '" b:dotexpr: "' . b:dotexpr . '"')
+ if b:dotexpr =~ '^\s*$' && b:incomplete =~ '^\s*$'
+ return []
+ endif
+
+
+ let result = []
+ if b:dotexpr !~ '^\s*$'
+ if b:context_type == s:CONTEXT_AFTER_DOT
+ let result = s:CompleteAfterDot(b:dotexpr)
+ elseif b:context_type == s:CONTEXT_IMPORT || b:context_type == s:CONTEXT_IMPORT_STATIC || b:context_type == s:CONTEXT_PACKAGE_DECL || b:context_type == s:CONTEXT_NEED_TYPE
+ let result = s:GetMembers(b:dotexpr[:-2])
+ elseif b:context_type == s:CONTEXT_METHOD_PARAM
+ if b:incomplete == '+'
+ let result = s:GetConstructorList(b:dotexpr)
+ else
+ let result = s:CompleteAfterDot(b:dotexpr)
+ endif
+ endif
+
+ " only incomplete word
+ elseif b:incomplete !~ '^\s*$'
+ " only need methods
+ if b:context_type == s:CONTEXT_METHOD_PARAM
+ let methods = s:SearchForName(b:incomplete, 0, 1)[1]
+ call extend(result, eval('[' . s:DoGetMethodList(methods) . ']'))
+
+ else
+ let result = s:CompleteAfterWord(b:incomplete)
+ endif
+
+ " then no filter needed
+ let b:incomplete = ''
+ endif
+
+
+ if len(result) > 0
+ " filter according to b:incomplete
+ if len(b:incomplete) > 0 && b:incomplete != '+'
+ let result = filter(result, "type(v:val) == type('') ? v:val =~ '^" . b:incomplete . "' : v:val['word'] =~ '^" . b:incomplete . "'")
+ endif
+
+ if exists('s:padding') && !empty(s:padding)
+ for item in result
+ if type(item) == type("")
+ let item .= s:padding
+ else
+ let item.word .= s:padding
+ endif
+ endfor
+ unlet s:padding
+ endif
+
+ call s:Debug('finish completion' . reltimestr(reltime(s:et_whole)) . 's')
+ return result
+ endif
+
+ if strlen(b:errormsg) > 0
+ echoerr 'javacomplete error: ' . b:errormsg
+ let b:errormsg = ''
+ endif
+endfunction
+
+" Precondition: incomplete must be a word without '.'.
+" return all the matched, variables, fields, methods, types, packages
+fu! s:CompleteAfterWord(incomplete)
+ " packages in jar files
+ if !exists('s:all_packages_in_jars_loaded')
+ call s:DoGetInfoByReflection('-', '-P')
+ let s:all_packages_in_jars_loaded = 1
+ endif
+
+ let pkgs = []
+ let types = []
+ for key in keys(s:cache)
+ if key =~# '^' . a:incomplete
+ if type(s:cache[key]) == type('') || get(s:cache[key], 'tag', '') == 'PACKAGE'
+ call add(pkgs, {'kind': 'P', 'word': key})
+
+ " filter out type info
+ elseif b:context_type != s:CONTEXT_PACKAGE_DECL && b:context_type != s:CONTEXT_IMPORT && b:context_type != s:CONTEXT_IMPORT_STATIC
+ call add(types, {'kind': 'C', 'word': key})
+ endif
+ endif
+ endfor
+
+ let pkgs += s:DoGetPackageInfoInDirs(a:incomplete, b:context_type == s:CONTEXT_PACKAGE_DECL, 1)
+
+
+ " add accessible types which name beginning with the incomplete in source files
+ " TODO: remove the inaccessible
+ if b:context_type != s:CONTEXT_PACKAGE_DECL
+ " single type import
+ for fqn in s:GetImports('imports_fqn')
+ let name = fqn[strridx(fqn, ".")+1:]
+ if name =~ '^' . a:incomplete
+ call add(types, {'kind': 'C', 'word': name})
+ endif
+ endfor
+
+ " current file
+ let lnum_old = line('.')
+ let col_old = col('.')
+ call cursor(1, 1)
+ while 1
+ let lnum = search('\<\C\(class\|interface\|enum\)[ \t\n\r ]\+' . a:incomplete . '[a-zA-Z0-9_$]*[< \t\n\r ]', 'W')
+ if lnum == 0
+ break
+ elseif s:InCommentOrLiteral(line('.'), col('.'))
+ continue
+ else
+ normal w
+ call add(types, {'kind': 'C', 'word': matchstr(getline(line('.'))[col('.')-1:], s:RE_IDENTIFIER)})
+ endif
+ endwhile
+ call cursor(lnum_old, col_old)
+
+ " other files
+ let filepatterns = ''
+ for dirpath in s:GetSourceDirs(expand('%:p'))
+ let filepatterns .= escape(dirpath, ' \') . '/*.java '
+ endfor
+ exe 'vimgrep /\s*' . s:RE_TYPE_DECL . '/jg ' . filepatterns
+ for item in getqflist()
+ if item.text !~ '^\s*\*\s\+'
+ let text = matchstr(s:Prune(item.text, -1), '\s*' . s:RE_TYPE_DECL)
+ if text != ''
+ let subs = split(substitute(text, '\s*' . s:RE_TYPE_DECL, '\1;\2;\3', ''), ';', 1)
+ if subs[2] =~# '^' . a:incomplete && (subs[0] =~ '\C\<public\>' || fnamemodify(bufname(item.bufnr), ':p:h') == expand('%:p:h'))
+ call add(types, {'kind': 'C', 'word': subs[2]})
+ endif
+ endif
+ endif
+ endfor
+ endif
+
+
+ let result = []
+
+ " add variables and members in source files
+ if b:context_type == s:CONTEXT_AFTER_DOT
+ let matches = s:SearchForName(a:incomplete, 0, 0)
+ let result += sort(eval('[' . s:DoGetFieldList(matches[2]) . ']'))
+ let result += sort(eval('[' . s:DoGetMethodList(matches[1]) . ']'))
+ endif
+ let result += sort(pkgs)
+ let result += sort(types)
+
+ return result
+endfu
+
+
+" Precondition: expr must end with '.'
+" return members of the value of expression
+function! s:CompleteAfterDot(expr)
+ let items = s:ParseExpr(a:expr) " TODO: return a dict containing more than items
+ if empty(items)
+ return []
+ endif
+
+
+ " 0. String literal
+ call s:Info('P0. "str".|')
+ if items[-1] =~ '"$'
+ return s:GetMemberList("java.lang.String")
+ endif
+
+
+ let ti = {}
+ let ii = 1 " item index
+ let itemkind = 0
+
+ "
+ " optimized process
+ "
+ " search the longest expr consisting of ident
+ let i = 1
+ let k = i
+ while i < len(items) && items[i] =~ '^\s*' . s:RE_IDENTIFIER . '\s*$'
+ let ident = substitute(items[i], '\s', '', 'g')
+ if ident == 'class' || ident == 'this' || ident == 'super'
+ let k = i
+ " return when found other keywords
+ elseif s:IsKeyword(ident)
+ return []
+ endif
+ let items[i] = substitute(items[i], '\s', '', 'g')
+ let i += 1
+ endwhile
+
+ if i > 1
+ " cases: "this.|", "super.|", "ClassName.this.|", "ClassName.super.|", "TypeName.class.|"
+ if items[k] ==# 'class' || items[k] ==# 'this' || items[k] ==# 'super'
+ call s:Info('O1. ' . items[k] . ' ' . join(items[:k-1], '.'))
+ let ti = s:DoGetClassInfo(items[k] == 'class' ? 'java.lang.Class' : join(items[:k-1], '.'))
+ if !empty(ti)
+ let itemkind = items[k] ==# 'this' ? 1 : items[k] ==# 'super' ? 2 : 0
+ let ii = k+1
+ else
+ return []
+ endif
+
+ " case: "java.io.File.|"
+ else
+ let fqn = join(items[:i-1], '.')
+ let srcpath = join(s:GetSourceDirs(expand('%:p'), s:GetPackageName()), ',')
+ call s:Info('O2. ' . fqn)
+ call s:DoGetTypeInfoForFQN([fqn], srcpath)
+ if get(get(s:cache, fqn, {}), 'tag', '') == 'CLASSDEF'
+ let ti = s:cache[fqn]
+ let itemkind = 11
+ let ii = i
+ endif
+ endif
+ endif
+
+
+ "
+ " first item
+ "
+ if empty(ti)
+ " cases:
+ " 1) "int.|", "void.|" - primitive type or pseudo-type, return `class`
+ " 2) "this.|", "super.|" - special reference
+ " 3) "var.|" - variable or field
+ " 4) "String.|" - type imported or defined locally
+ " 5) "java.|" - package
+ if items[0] =~ '^\s*' . s:RE_IDENTIFIER . '\s*$'
+ let ident = substitute(items[0], '\s', '', 'g')
+
+ if s:IsKeyword(ident)
+ " 1)
+ call s:Info('F1. "' . ident . '.|"')
+ if ident ==# 'void' || s:IsBuiltinType(ident)
+ let ti = s:PRIMITIVE_TYPE_INFO
+ let itemkind = 11
+
+ " 2)
+ call s:Info('F2. "' . ident . '.|"')
+ elseif ident ==# 'this' || ident ==# 'super'
+ let itemkind = ident ==# 'this' ? 1 : ident ==# 'super' ? 2 : 0
+ let ti = s:DoGetClassInfo(ident)
+ endif
+
+ else
+ " 3)
+ let typename = s:GetDeclaredClassName(ident)
+ call s:Info('F3. "' . ident . '.|" typename: "' . typename . '"')
+ if (typename != '')
+ if typename[0] == '[' || typename[-1:] == ']'
+ let ti = s:ARRAY_TYPE_INFO
+ elseif typename != 'void' && !s:IsBuiltinType(typename)
+ let ti = s:DoGetClassInfo(typename)
+ endif
+
+ else
+ " 4)
+ call s:Info('F4. "TypeName.|"')
+ let ti = s:DoGetClassInfo(ident)
+ let itemkind = 11
+
+ if get(ti, 'tag', '') != 'CLASSDEF'
+ let ti = {}
+ endif
+
+ " 5)
+ if empty(ti)
+ call s:Info('F5. "package.|"')
+ unlet ti
+ let ti = s:GetMembers(ident) " s:DoGetPackegInfo(ident)
+ let itemkind = 20
+ endif
+ endif
+ endif
+
+ " method invocation: "method().|" - "this.method().|"
+ elseif items[0] =~ '^\s*' . s:RE_IDENTIFIER . '\s*('
+ let ti = s:MethodInvocation(items[0], ti, itemkind)
+
+ " array type, return `class`: "int[] [].|", "java.lang.String[].|", "NestedClass[].|"
+ elseif items[0] =~# s:RE_ARRAY_TYPE
+ call s:Info('array type. "' . items[0] . '"')
+ let qid = substitute(items[0], s:RE_ARRAY_TYPE, '\1', '')
+ if s:IsBuiltinType(qid) || (!s:HasKeyword(qid) && !empty(s:DoGetClassInfo(qid)))
+ let ti = s:PRIMITIVE_TYPE_INFO
+ let itemkind = 11
+ endif
+
+ " class instance creation expr: "new String().|", "new NonLoadableClass().|"
+ " array creation expr: "new int[i=1] [val()].|", "new java.lang.String[].|"
+ elseif items[0] =~ '^\s*new\s\+'
+ call s:Info('creation expr. "' . items[0] . '"')
+ let subs = split(substitute(items[0], '^\s*new\s\+\(' .s:RE_QUALID. '\)\s*\([([]\)', '\1;\2', ''), ';')
+ if subs[1][0] == '['
+ let ti = s:ARRAY_TYPE_INFO
+ elseif subs[1][0] == '('
+ let ti = s:DoGetClassInfo(subs[0])
+ " exclude interfaces and abstract class. TODO: exclude the inaccessible
+ if get(ti, 'flags', '')[-10:-10] || get(ti, 'flags', '')[-11:-11]
+ echo 'cannot instantiate the type ' . subs[0]
+ let ti = {}
+ return []
+ endif
+ endif
+
+ " casting conversion: "(Object)o.|"
+ elseif items[0] =~ s:RE_CASTING
+ call s:Info('Casting conversion. "' . items[0] . '"')
+ let subs = split(substitute(items[0], s:RE_CASTING, '\1;\2', ''), ';')
+ let ti = s:DoGetClassInfo(subs[0])
+
+ " array access: "var[i][j].|" Note: "var[i][]" is incorrect
+ elseif items[0] =~# s:RE_ARRAY_ACCESS
+ let subs = split(substitute(items[0], s:RE_ARRAY_ACCESS, '\1;\2', ''), ';')
+ if get(subs, 1, '') !~ s:RE_BRACKETS
+ let typename = s:GetDeclaredClassName(subs[0])
+ call s:Info('ArrayAccess. "' .items[0]. '.|" typename: "' . typename . '"')
+ if (typename != '')
+ let ti = s:ArrayAccess(typename, items[0])
+ endif
+ endif
+ endif
+ endif
+
+
+ "
+ " next items
+ "
+ while !empty(ti) && ii < len(items)
+ " method invocation: "PrimaryExpr.method(parameters)[].|"
+ if items[ii] =~ '^\s*' . s:RE_IDENTIFIER . '\s*('
+ let ti = s:MethodInvocation(items[ii], ti, itemkind)
+ let itemkind = 0
+ let ii += 1
+ continue
+
+
+ " expression of selection, field access, array access
+ elseif items[ii] =~ s:RE_SELECT_OR_ACCESS
+ let subs = split(substitute(items[ii], s:RE_SELECT_OR_ACCESS, '\1;\2', ''), ';')
+ let ident = subs[0]
+ let brackets = get(subs, 1, '')
+
+ " package members
+ if itemkind/10 == 2 && empty(brackets) && !s:IsKeyword(ident)
+ let qn = join(items[:ii], '.')
+ if type(ti) == type([])
+ let idx = s:Index(ti, ident, 'word')
+ if idx >= 0
+ if ti[idx].kind == 'P'
+ unlet ti
+ let ti = s:GetMembers(qn)
+ let ii += 1
+ continue
+ elseif ti[idx].kind == 'C'
+ unlet ti
+ let ti = s:DoGetClassInfo(qn)
+ let itemkind = 11
+ let ii += 1
+ continue
+ endif
+ endif
+ endif
+
+
+ " type members
+ elseif itemkind/10 == 1 && empty(brackets)
+ if ident ==# 'class' || ident ==# 'this' || ident ==# 'super'
+ let ti = s:DoGetClassInfo(ident == 'class' ? 'java.lang.Class' : join(items[:ii-1], '.'))
+ let itemkind = ident ==# 'this' ? 1 : ident ==# 'super' ? 2 : 0
+ let ii += 1
+ continue
+
+ elseif !s:IsKeyword(ident) && type(ti) == type({}) && get(ti, 'tag', '') == 'CLASSDEF'
+ " accessible static field
+ "let idx = s:Index(get(ti, 'fields', []), ident, 'n')
+ "if idx >= 0 && s:IsStatic(ti.fields[idx].m)
+ " let ti = s:ArrayAccess(ti.fields[idx].t, items[ii])
+ let members = s:SearchMember(ti, ident, 1, itemkind, 1, 0)
+ if !empty(members[2])
+ let ti = s:ArrayAccess(members[2][0].t, items[ii])
+ let itemkind = 0
+ let ii += 1
+ continue
+ endif
+
+ " accessible nested type
+ "if !empty(filter(copy(get(ti, 'classes', [])), 'strpart(v:val, strridx(v:val, ".")) ==# "' . ident . '"'))
+ if !empty(members[0])
+ let ti = s:DoGetClassInfo(join(items[:ii], '.'))
+ let ii += 1
+ continue
+ endif
+ endif
+
+
+ " instance members
+ elseif itemkind/10 == 0 && !s:IsKeyword(ident)
+ if type(ti) == type({}) && get(ti, 'tag', '') == 'CLASSDEF'
+ "let idx = s:Index(get(ti, 'fields', []), ident, 'n')
+ "if idx >= 0
+ " let ti = s:ArrayAccess(ti.fields[idx].t, items[ii])
+ let members = s:SearchMember(ti, ident, 1, itemkind, 1, 0)
+ let itemkind = 0
+ if !empty(members[2])
+ let ti = s:ArrayAccess(members[2][0].t, items[ii])
+ let ii += 1
+ continue
+ endif
+ endif
+ endif
+ endif
+
+ return []
+ endwhile
+
+
+ " type info or package info --> members
+ if !empty(ti)
+ if type(ti) == type({})
+ if get(ti, 'tag', '') == 'CLASSDEF'
+ if get(ti, 'name', '') == '!'
+ return [{'kind': 'f', 'word': 'class', 'menu': 'Class'}]
+ elseif get(ti, 'name', '') == '['
+ return s:ARRAY_TYPE_MEMBERS
+ elseif itemkind < 20
+ return s:DoGetMemberList(ti, itemkind)
+ endif
+ elseif get(ti, 'tag', '') == 'PACKAGE'
+ " TODO: ti -> members, in addition to packages in dirs
+ return s:GetMembers( substitute(join(items, '.'), '\s', '', 'g') )
+ endif
+ elseif type(ti) == type([])
+ return ti
+ endif
+ endif
+
+ return []
+endfunction
+
+
+fu! s:MethodInvocation(expr, ti, itemkind)
+ let subs = split(substitute(a:expr, '\s*\(' . s:RE_IDENTIFIER . '\)\s*\((.*\)', '\1;\2', ''), ';')
+
+ " all methods matched
+ if empty(a:ti)
+ let methods = s:SearchForName(subs[0], 0, 1)[1]
+ elseif type(a:ti) == type({}) && get(a:ti, 'tag', '') == 'CLASSDEF'
+ let methods = s:SearchMember(a:ti, subs[0], 1, a:itemkind, 1, 0, a:itemkind == 2)[1]
+" let methods = s:filter(get(a:ti, 'methods', []), 'item.n == "' . subs[0] . '"')
+" if a:itemkind == 1 || a:itemkind == 2
+" let methods += s:filter(get(a:ti, 'declared_methods', []), 'item.n == "' . subs[0] . '"')
+" endif
+ else
+ let methods = []
+ endif
+
+ let method = s:DetermineMethod(methods, subs[1])
+ if !empty(method)
+ return s:ArrayAccess(method.r, a:expr)
+ endif
+ return {}
+endfu
+
+fu! s:ArrayAccess(arraytype, expr)
+ if a:expr =~ s:RE_BRACKETS | return {} | endif
+ let typename = a:arraytype
+
+ let dims = 0
+ if typename[0] == '[' || typename[-1:] == ']' || a:expr[-1:] == ']'
+ let dims = s:CountDims(a:expr) - s:CountDims(typename)
+ if dims == 0
+ let typename = matchstr(typename, s:RE_IDENTIFIER)
+ elseif dims < 0
+ return s:ARRAY_TYPE_INFO
+ else
+ "echoerr 'dims exceeds'
+ endif
+ endif
+ if dims == 0
+ if typename != 'void' && !s:IsBuiltinType(typename)
+ return s:DoGetClassInfo(typename)
+ endif
+ endif
+ return {}
+endfu
+
+
+" Quick information {{{1
+function! MyBalloonExpr()
+ if (searchdecl(v:beval_text, 1, 0) == 0)
+ return s:GetVariableDeclaration()
+ endif
+ return ''
+" return 'Cursor is at line ' . v:beval_lnum .
+" \', column ' . v:beval_col .
+" \ ' of file ' . bufname(v:beval_bufnr) .
+" \ ' on word "' . v:beval_text . '"'
+endfunction
+"set bexpr=MyBalloonExpr()
+"set ballooneval
+
+" parameters information {{{1
+fu! javacomplete#CompleteParamsInfo(findstart, base)
+ if a:findstart
+ return col('.') - 1
+ endif
+
+
+ let mi = s:GetMethodInvocationExpr(s:GetStatement())
+ if empty(mi.method)
+ return []
+ endif
+
+ " TODO: how to determine overloaded functions
+ "let mi.params = s:EvalParams(mi.params)
+ if empty(mi.expr)
+ let methods = s:SearchForName(mi.method, 0, 1)[1]
+ let result = eval('[' . s:DoGetMethodList(methods) . ']')
+ elseif mi.method == '+'
+ let result = s:GetConstructorList(mi.expr)
+ else
+ let result = s:CompleteAfterDot(mi.expr)
+ endif
+
+ if !empty(result)
+ if !empty(mi.method) && mi.method != '+'
+ let result = filter(result, "type(v:val) == type('') ? v:val ==# '" . mi.method . "' : v:val['word'] ==# '" . mi.method . "('")
+ endif
+ return result
+ endif
+endfu
+
+" scanning and parsing {{{1
+
+" Search back from the cursor position till meeting '{' or ';'.
+" '{' means statement start, ';' means end of a previous statement.
+" Return: statement before cursor
+" Note: It's the base for parsing. And It's OK for most cases.
+function! s:GetStatement()
+ if getline('.') =~ '^\s*\(import\|package\)\s\+'
+ return strpart(getline('.'), match(getline('.'), '\(import\|package\)'), col('.')-1)
+ endif
+
+ let lnum_old = line('.')
+ let col_old = col('.')
+
+ while 1
+ if search('[{};]\|<%\|<%!', 'bW') == 0
+ let lnum = 1
+ let col = 1
+ else
+ if s:InCommentOrLiteral(line('.'), col('.'))
+ continue
+ endif
+
+ normal w
+ let lnum = line('.')
+ let col = col('.')
+ endif
+ break
+ endwhile
+
+ silent call cursor(lnum_old, col_old)
+ return s:MergeLines(lnum, col, lnum_old, col_old)
+endfunction
+
+fu! s:MergeLines(lnum, col, lnum_old, col_old)
+ let lnum = a:lnum
+ let col = a:col
+
+ let str = ''
+ if lnum < a:lnum_old
+ let str = s:Prune(strpart(getline(lnum), a:col-1))
+ let lnum += 1
+ while lnum < a:lnum_old
+ let str .= s:Prune(getline(lnum))
+ let lnum += 1
+ endwhile
+ let col = 1
+ endif
+ let lastline = strpart(getline(a:lnum_old), col-1, a:col_old-col)
+ let str .= s:Prune(lastline, col)
+ let str = s:RemoveBlockComments(str)
+ " generic in JAVA 5+
+ while match(str, s:RE_TYPE_ARGUMENTS) != -1
+ let str = substitute(str, '\(' . s:RE_TYPE_ARGUMENTS . '\)', '\=repeat(" ", len(submatch(1)))', 'g')
+ endwhile
+ let str = substitute(str, '\s\s\+', ' ', 'g')
+ let str = substitute(str, '\([.()]\)[ \t]\+', '\1', 'g')
+ let str = substitute(str, '[ \t]\+\([.()]\)', '\1', 'g')
+ return s:Trim(str) . matchstr(lastline, '\s*$')
+endfu
+
+" Extract a clean expr, removing some non-necessary characters.
+fu! s:ExtractCleanExpr(expr)
+ let cmd = substitute(a:expr, '[ \t\r\n ]\+\([.()[\]]\)', '\1', 'g')
+ let cmd = substitute(cmd, '\([.()[\]]\)[ \t\r\n ]\+', '\1', 'g')
+
+ let pos = strlen(cmd)-1
+ while pos >= 0 && cmd[pos] =~ '[a-zA-Z0-9_.)\]]'
+ if cmd[pos] == ')'
+ let pos = s:SearchPairBackward(cmd, pos, '(', ')')
+ elseif cmd[pos] == ']'
+ let pos = s:SearchPairBackward(cmd, pos, '[', ']')
+ endif
+ let pos -= 1
+ endwhile
+
+ " try looking back for "new"
+ let idx = match(strpart(cmd, 0, pos+1), '\<new[ \t\r\n ]*$')
+
+ return strpart(cmd, idx != -1 ? idx : pos+1)
+endfu
+
+fu! s:ParseExpr(expr)
+ let items = []
+ let s = 0
+ " recognize ClassInstanceCreationExpr as a whole
+ let e = matchend(a:expr, '^\s*new\s\+' . s:RE_QUALID . '\s*[([]')-1
+ if e < 0
+ let e = match(a:expr, '[.([]')
+ endif
+ let isparen = 0
+ while e >= 0
+ if a:expr[e] == '.'
+ let subexpr = strpart(a:expr, s, e-s)
+ call extend(items, isparen ? s:ProcessParentheses(subexpr) : [subexpr])
+ let isparen = 0
+ let s = e + 1
+ elseif a:expr[e] == '('
+ let e = s:GetMatchedIndexEx(a:expr, e, '(', ')')
+ let isparen = 1
+ if e < 0
+ break
+ else
+ let e = matchend(a:expr, '^\s*[.[]', e+1)-1
+ continue
+ endif
+ elseif a:expr[e] == '['
+ let e = s:GetMatchedIndexEx(a:expr, e, '[', ']')
+ if e < 0
+ break
+ else
+ let e = matchend(a:expr, '^\s*[.[]', e+1)-1
+ continue
+ endif
+ endif
+ let e = match(a:expr, '[.([]', s)
+ endwhile
+ let tail = strpart(a:expr, s)
+ if tail !~ '^\s*$'
+ call extend(items, isparen ? s:ProcessParentheses(tail) : [tail])
+ endif
+
+ return items
+endfu
+
+" Given optional argument, call s:ParseExpr() to parser the nonparentheses expr
+fu! s:ProcessParentheses(expr, ...)
+ let s = matchend(a:expr, '^\s*(')
+ if s != -1
+ let e = s:GetMatchedIndexEx(a:expr, s-1, '(', ')')
+ if e >= 0
+ let tail = strpart(a:expr, e+1)
+ if tail =~ '^\s*[\=$'
+ return s:ProcessParentheses(strpart(a:expr, s, e-s), 1)
+ elseif tail =~ '^\s*\w'
+ return [strpart(a:expr, 0, e+1) . 'obj.']
+ endif
+ endif
+
+ " multi-dot-expr except for new expr
+ elseif a:0 > 0 && stridx(a:expr, '.') != match(a:expr, '\.\s*$') && a:expr !~ '^\s*new\s\+'
+ return s:ParseExpr(a:expr)
+ endif
+ return [a:expr]
+endfu
+
+" return {'expr': , 'method': , 'params': }
+fu! s:GetMethodInvocationExpr(expr)
+ let idx = strlen(a:expr)-1
+ while idx >= 0
+ if a:expr[idx] == '('
+ break
+ elseif a:expr[idx] == ')'
+ let idx = s:SearchPairBackward(a:expr, idx, '(', ')')
+ elseif a:expr[idx] == ']'
+ let idx = s:SearchPairBackward(a:expr, idx, '[', ']')
+ endif
+ let idx -= 1
+ endwhile
+
+ let mi = {'expr': strpart(a:expr, 0, idx+1), 'method': '', 'params': strpart(a:expr, idx+1)}
+ let idx = match(mi.expr, '\<new\s\+' . s:RE_QUALID . '\s*(\s*$')
+ if idx >= 0
+ let mi.method = '+'
+ let mi.expr = substitute(matchstr(strpart(mi.expr, idx+4), s:RE_QUALID), '\s', '', 'g')
+ else
+ let idx = match(mi.expr, '\<' . s:RE_IDENTIFIER . '\s*(\s*$')
+ if idx >= 0
+ let subs = s:SplitAt(mi.expr, idx-1)
+ let mi.method = substitute(subs[1], '\s*(\s*$', '', '')
+ let mi.expr = s:ExtractCleanExpr(subs[0])
+ endif
+ endif
+ return mi
+endfu
+
+" imports {{{1
+function! s:GenerateImports()
+ let imports = []
+
+ let lnum_old = line('.')
+ let col_old = col('.')
+ call cursor(1, 1)
+
+ if &ft == 'jsp'
+ while 1
+ let lnum = search('\<import\s*=[''"]', 'W')
+ if (lnum == 0)
+ break
+ endif
+
+ let str = getline(lnum)
+ if str =~ '<%\s*@\s*page\>' || str =~ '<jsp:\s*directive.page\>'
+ let str = substitute(str, '.*import=[''"]\([a-zA-Z0-9_$.*, \t]\+\)[''"].*', '\1', '')
+ for item in split(str, ',')
+ call add(imports, substitute(item, '\s', '', 'g'))
+ endfor
+ endif
+ endwhile
+ else
+ while 1
+ let lnum = search('\<import\>', 'W')
+ if (lnum == 0)
+ break
+ elseif !s:InComment(line("."), col(".")-1)
+ normal w
+ " TODO: search semicolon or import keyword, excluding comment
+ let stat = matchstr(getline(lnum)[col('.')-1:], '\(static\s\+\)\?\(' .s:RE_QUALID. '\%(\s*\.\s*\*\)\?\)\s*;')
+ if !empty(stat)
+ call add(imports, stat[:-2])
+ endif
+ endif
+ endwhile
+ endif
+
+ call cursor(lnum_old, col_old)
+ return imports
+endfunction
+
+fu! s:GetImports(kind, ...)
+ let filekey = a:0 > 0 && !empty(a:1) ? a:1 : s:GetCurrentFileKey()
+ let props = get(s:files, filekey, {})
+ if !has_key(props, a:kind)
+ let props['imports'] = filekey == s:GetCurrentFileKey() ? s:GenerateImports() : props.unit.imports
+ let props['imports_static'] = []
+ let props['imports_fqn'] = []
+ let props['imports_star'] = ['java.lang.']
+ if &ft == 'jsp' || filekey =~ '\.jsp$'
+ let props.imports_star += ['javax.servlet.', 'javax.servlet.http.', 'javax.servlet.jsp.']
+ endif
+
+ for import in props.imports
+ let subs = split(substitute(import, '^\s*\(static\s\+\)\?\(' .s:RE_QUALID. '\%(\s*\.\s*\*\)\?\)\s*$', '\1;\2', ''), ';', 1)
+ let qid = substitute(subs[1] , '\s', '', 'g')
+ if !empty(subs[0])
+ call add(props.imports_static, qid)
+ elseif qid[-1:] == '*'
+ call add(props.imports_star, qid[:-2])
+ else
+ call add(props.imports_fqn, qid)
+ endif
+ endfor
+ let s:files[filekey] = props
+ endif
+ return get(props, a:kind, [])
+endfu
+
+" search for name in
+" return the fqn matched
+fu! s:SearchSingleTypeImport(name, fqns)
+ let matches = s:filter(a:fqns, 'item =~# ''\<' . a:name . '$''')
+ if len(matches) == 1
+ return matches[0]
+ elseif !empty(matches)
+ echoerr 'Name "' . a:name . '" conflicts between ' . join(matches, ' and ')
+ return matches[0]
+ endif
+ return ''
+endfu
+
+" search for name in static imports, return list of members with the same name
+" return [types, methods, fields]
+fu! s:SearchStaticImports(name, fullmatch)
+ let result = [[], [], []]
+ let candidates = [] " list of the canonical name
+ for item in s:GetImports('imports_static')
+ if item[-1:] == '*' " static import on demand
+ call add(candidates, item[:-3])
+ elseif item[strridx(item, '.')+1:] ==# a:name
+ \ || (!a:fullmatch && item[strridx(item, '.')+1:] =~ '^' . a:name)
+ call add(candidates, item[:strridx(item, '.')])
+ endif
+ endfor
+ if empty(candidates)
+ return result
+ endif
+
+
+ " read type info which are not in cache
+ let commalist = ''
+ for typename in candidates
+ if !has_key(s:cache, typename)
+ let commalist .= typename . ','
+ endif
+ endfor
+ if commalist != ''
+ let res = s:RunReflection('-E', commalist, 's:SearchStaticImports in Batch')
+ if res =~ "^{'"
+ let dict = eval(res)
+ for key in keys(dict)
+ let s:cache[key] = s:Sort(dict[key])
+ endfor
+ endif
+ endif
+
+ " search in all candidates
+ for typename in candidates
+ let ti = get(s:cache, typename, 0)
+ if type(ti) == type({}) && get(ti, 'tag', '') == 'CLASSDEF'
+ let members = s:SearchMember(ti, a:name, a:fullmatch, 12, 1, 0)
+ let result[1] += members[1]
+ let result[2] += members[2]
+ "let pattern = 'item.n ' . (a:fullmatch ? '==# ''' : '=~# ''^') . a:name . ''' && s:IsStatic(item.m)'
+ "let result[1] += s:filter(get(ti, 'methods', []), pattern)
+ "let result[2] += s:filter(get(ti, 'fields', []), pattern)
+ else
+ " TODO: mark the wrong import declaration.
+ endif
+ endfor
+ return result
+endfu
+
+
+" search decl {{{1
+" Return: The declaration of identifier under the cursor
+" Note: The type of a variable must be imported or a fqn.
+function! s:GetVariableDeclaration()
+ let lnum_old = line('.')
+ let col_old = col('.')
+
+ silent call search('[^a-zA-Z0-9$_.,?<>[\] \t\r\n ]', 'bW') " call search('[{};(,]', 'b')
+ normal w
+ let lnum = line('.')
+ let col = col('.')
+ if (lnum == lnum_old && col == col_old)
+ return ''
+ endif
+
+" silent call search('[;){]')
+" let lnum_end = line('.')
+" let col_end = col('.')
+" let declaration = ''
+" while (lnum <= lnum_end)
+" let declaration = declaration . getline(lnum)
+" let lnum = lnum + 1
+" endwhile
+" let declaration = strpart(declaration, col-1)
+" let declaration = substitute(declaration, '\.[ \t]\+', '.', 'g')
+
+ silent call cursor(lnum_old, col_old)
+ return s:MergeLines(lnum, col, lnum_old, col_old)
+endfunction
+
+function! s:FoundClassDeclaration(type)
+ let lnum_old = line('.')
+ let col_old = col('.')
+ call cursor(1, 1)
+ while 1
+ let lnum = search('\<\C\(class\|interface\|enum\)[ \t\n\r ]\+' . a:type . '[< \t\n\r ]', 'W')
+ if lnum == 0 || !s:InCommentOrLiteral(line('.'), col('.'))
+ break
+ endif
+ endwhile
+
+ " search mainly for the cases: " class /* block comment */ Ident"
+ " " class // comment \n Ident "
+ if lnum == 0
+ let found = 0
+ while !found
+ let lnum = search('\<\C\(class\|interface\|enum\)[ \t\n\r ]\+', 'W')
+ if lnum == 0
+ break
+ elseif s:InCommentOrLiteral(line('.'), col('.'))
+ continue
+ else
+ normal w
+ " skip empty line
+ while getline(line('.'))[col('.')-1] == ''
+ normal w
+ endwhile
+ let lnum = line('.')
+ let col = col('.')
+ while 1
+ if match(getline(lnum)[col-1:], '^[ \t\n\r ]*' . a:type . '[< \t\n\r ]') >= 0
+ let found = 1
+ " meets comment
+ elseif match(getline(lnum)[col-1:], '^[ \t\n\r ]*\(//\|/\*\)') >= 0
+ if getline(lnum)[col-1:col] == '//'
+ normal $eb
+ else
+ let lnum = search('\*\/', 'W')
+ if lnum == 0
+ break
+ endif
+ normal web
+ endif
+ let lnum = line('.')
+ let col = col('.')
+ continue
+ endif
+ break
+ endwhile
+ endif
+ endwhile
+ endif
+
+ silent call cursor(lnum_old, col_old)
+ return lnum
+endfu
+
+fu! s:FoundClassLocally(type)
+ " current path
+ if globpath(expand('%:p:h'), a:type . '.java') != ''
+ return 1
+ endif
+
+ "
+ let srcpath = javacomplete#GetSourcePath(1)
+ let file = globpath(srcpath, substitute(fqn, '\.', '/', 'g') . '.java')
+ if file != ''
+ return 1
+ endif
+
+ return 0
+endfu
+
+" regexp samples:
+" echo search('\(\(public\|protected|private\)[ \t\n\r]\+\)\?\(\(static\)[ \t\n\r]\+\)\?\(\<class\>\|\<interface\>\)[ \t\n\r]\+HelloWorld[^a-zA-Z0-9_$]', 'W')
+" echo substitute(getline('.'), '.*\(\(public\|protected\|private\)[ \t\n\r]\+\)\?\(\(static\)[ \t\n\r]\+\)\?\(\<class\>\|\<interface\>\)\s\+\([a-zA-Z0-9_]\+\)\s\+\(\(implements\|extends\)\s\+\([^{]\+\)\)\?\s*{.*', '["\1", "\2", "\3", "\4", "\5", "\6", "\8", "\9"]', '')
+" code sample:
+function! s:GetClassDeclarationOf(type)
+ call cursor(1, 1)
+ let decl = []
+
+ let lnum = search('\(\<class\>\|\<interface\>\)[ \t\n\r]\+' . a:type . '[^a-zA-Z0-9_$]', 'W')
+ if (lnum != 0)
+ " TODO: search back for optional 'public | private' and 'static'
+ " join lines till to '{'
+ let lnum_end = search('{')
+ if (lnum_end != 0)
+ let str = ''
+ while (lnum <= lnum_end)
+ let str = str . getline(lnum)
+ let lnum = lnum + 1
+ endwhile
+
+ exe "let decl = " . substitute(str, '.*\(\<class\>\|\<interface\>\)\s\+\([a-zA-Z0-9_]\+\)\s\+\(\(implements\|extends\)\s\+\([^{]\+\)\)\?\s*{.*', '["\1", "\2", "\4", "\5"]', '')
+ endif
+ endif
+
+ return decl
+endfunction
+
+" return list
+" 0 class | interface
+" 1 name
+" [2 implements | extends ]
+" [3 parent list ]
+function! s:GetThisClassDeclaration()
+ let lnum_old = line('.')
+ let col_old = col('.')
+
+ while (1)
+ call search('\(\<class\C\>\|\<interface\C\>\|\<enum\C\>\)[ \t\r\n]\+', 'bW')
+ if !s:InComment(line("."), col(".")-1)
+ if getline('.')[col('.')-2] !~ '\S'
+ break
+ endif
+ end
+ endwhile
+
+ " join lines till to '{'
+ let str = ''
+ let lnum = line('.')
+ call search('{')
+ let lnum_end = line('.')
+ while (lnum <= lnum_end)
+ let str = str . getline(lnum)
+ let lnum = lnum + 1
+ endwhile
+
+
+ let declaration = substitute(str, '.*\(\<class\>\|\<interface\>\)\s\+\([a-zA-Z0-9_]\+\)\(\s\+\(implements\|extends\)\s\+\([^{]\+\)\)\?\s*{.*', '["\1", "\2", "\4", "\5"]', '')
+ call cursor(lnum_old, col_old)
+ if declaration !~ '^['
+ echoerr 'Some error occurs when recognizing this class:' . declaration
+ return ['', '']
+ endif
+ exe "let list = " . declaration
+ return list
+endfunction
+
+" searches for name of a var or a field and determines the meaning {{{1
+
+" The standard search order of a variable or field is as follows:
+" 1. Local variables declared in the code block, for loop, or catch clause
+" from current scope up to the most outer block, a method or an initialization block
+" 2. Parameters if the code is in a method or ctor
+" 3. Fields of the type
+" 4. Accessible inherited fields.
+" 5. If the type is a nested type,
+" local variables of the enclosing block or fields of the enclosing class.
+" Note that if the type is a static nested type, only static members of an enclosing block or class are searched
+" Reapply this rule to the upper block and class enclosing the enclosing type recursively
+" 6. Accessible static fields imported.
+" It is allowed that several fields with the same name.
+
+" The standard search order of a method is as follows:
+" 1. Methods of the type
+" 2. Accessible inherited methods.
+" 3. Methods of the enclosing class if the type is a nested type.
+" 4. Accessible static methods imported.
+" It is allowed that several methods with the same name and signature.
+
+" first return at once if found one.
+" fullmatch 1 - equal, 0 - match beginning
+" return [types, methods, fields, vars]
+fu! s:SearchForName(name, first, fullmatch)
+ let result = [[], [], [], []]
+ if s:IsKeyword(a:name)
+ return result
+ endif
+
+ " use java_parser.vim
+ if javacomplete#GetSearchdeclMethod() == 4
+ " declared in current file
+ let unit = javacomplete#parse()
+ let targetPos = java_parser#MakePos(line('.')-1, col('.')-1)
+ let trees = s:SearchNameInAST(unit, a:name, targetPos, a:fullmatch)
+ for tree in trees
+ if tree.tag == 'VARDEF'
+ call add(result[2], tree)
+ elseif tree.tag == 'METHODDEF'
+ call add(result[1], tree)
+ elseif tree.tag == 'CLASSDEF'
+ call add(result[0], tree.name)
+ endif
+ endfor
+
+ if a:first && result != [[], [], [], []] | return result | endif
+
+ " Accessible inherited members
+ let type = get(s:SearchTypeAt(unit, targetPos), -1, {})
+ if !empty(type)
+ let members = s:SearchMember(type, a:name, a:fullmatch, 2, 1, 0, 1)
+ let result[0] += members[0]
+ let result[1] += members[1]
+ let result[2] += members[2]
+" "let ti = s:AddInheritedClassInfo({}, type)
+" if !empty(ti)
+" let comparator = a:fullmatch ? "=~# '^" : "==# '"
+" let result[0] += s:filter(get(ti, 'classes', []), 'item ' . comparator . a:name . "'")
+" let result[1] += s:filter(get(ti, 'methods', []), 'item.n ' . comparator . a:name . "'")
+" let result[2] += s:filter(get(ti, 'fields', []), 'item.n ' . comparator . a:name . "'")
+" if a:0 > 0
+" let result[1] += s:filter(get(ti, 'declared_methods', []), 'item.n ' . comparator . a:name . "'")
+" let result[2] += s:filter(get(ti, 'declared_fields', []), 'item.n ' . comparator . a:name . "'")
+" endif
+" if a:first && result != [[], [], [], []] | return result | endif
+" endif
+ endif
+
+ " static import
+ let si = s:SearchStaticImports(a:name, a:fullmatch)
+ let result[1] += si[1]
+ let result[2] += si[2]
+ endif
+ return result
+endfu
+
+" TODO: how to determine overloaded functions
+fu! s:DetermineMethod(methods, parameters)
+ return get(a:methods, 0, {})
+endfu
+
+" Parser.GetType() in insenvim
+function! s:GetDeclaredClassName(var)
+ let var = s:Trim(a:var)
+ call s:Trace('GetDeclaredClassName for "' . var . '"')
+ if var =~# '^\(this\|super\)$'
+ return var
+ endif
+
+
+ " Special handling for builtin objects in JSP
+ if &ft == 'jsp'
+ if get(s:JSP_BUILTIN_OBJECTS, a:var, '') != ''
+ return s:JSP_BUILTIN_OBJECTS[a:var]
+ endif
+ endif
+
+ " use java_parser.vim
+ if javacomplete#GetSearchdeclMethod() == 4
+ let variable = get(s:SearchForName(var, 1, 1)[2], -1, {})
+ return get(variable, 'tag', '') == 'VARDEF' ? java_parser#type2Str(variable.vartype) : get(variable, 't', '')
+ endif
+
+
+ let ic = &ignorecase
+ setlocal noignorecase
+
+ let searched = javacomplete#GetSearchdeclMethod() == 2 ? s:Searchdecl(var, 1, 0) : searchdecl(var, 1, 0)
+ if (searched == 0)
+ " code sample:
+ " String tmp; java.
+ " lang. String str, value;
+ " for (int i = 0, j = 0; i < 10; i++) {
+ " j = 0;
+ " }
+ let declaration = s:GetVariableDeclaration()
+ " Assume it a class member, and remove modifiers
+ let class = substitute(declaration, '^\(public\s\+\|protected\s\+\|private\s\+\|abstract\s\+\|static\s\+\|final\s\+\|native\s\+\)*', '', '')
+ let class = substitute(class, '\s*\([a-zA-Z0-9_.]\+\)\(\[\]\)\?\s\+.*', '\1\2', '')
+ let class = substitute(class, '\([a-zA-Z0-9_.]\)<.*', '\1', '')
+ call s:Info('class: "' . class . '" declaration: "' . declaration . '" for ' . a:var)
+ let &ignorecase = ic
+ if class != '' && class !=# a:var && class !=# 'import' && class !=# 'class'
+ return class
+ endif
+ endif
+
+ let &ignorecase = ic
+ call s:Trace('GetDeclaredClassName: cannot find')
+ return ''
+endfunction
+
+" using java_parser.vim {{{1
+" javacomplete#parse() {{{2
+fu! javacomplete#parse(...)
+ let filename = a:0 == 0 ? '%' : a:1
+
+ let changed = 0
+ if filename == '%'
+ let filename = s:GetCurrentFileKey()
+ let props = get(s:files, filename, {})
+ if get(props, 'changedtick', -1) != b:changedtick
+ let changed = 1
+ let props.changedtick = b:changedtick
+ let lines = getline('^', '$')
+ endif
+ else
+ let props = get(s:files, filename, {})
+ if get(props, 'modifiedtime', 0) != getftime(filename)
+ let changed = 1
+ let props.modifiedtime = getftime(filename)
+ let lines = readfile(filename)
+ endif
+ endif
+
+ if changed
+ call java_parser#InitParser(lines)
+ call java_parser#SetLogLevel(5)
+ let props.unit = java_parser#compilationUnit()
+
+ let package = has_key(props.unit, 'package') ? props.unit.package . '.' : ''
+ call s:UpdateFQN(props.unit, package)
+ endif
+ let s:files[filename] = props
+ return props.unit
+endfu
+
+" update fqn for toplevel types or nested types.
+" not for local type or anonymous type
+fu! s:UpdateFQN(tree, qn)
+ if a:tree.tag == 'TOPLEVEL'
+ for def in a:tree.types
+ call s:UpdateFQN(def, a:qn)
+ endfor
+ elseif a:tree.tag == 'CLASSDEF'
+ let a:tree.fqn = a:qn . a:tree.name
+ for def in a:tree.defs
+ if def.tag == 'CLASSDEF'
+ call s:UpdateFQN(def, a:tree.fqn . '.')
+ endif
+ endfor
+ endif
+endfu
+
+" TreeVisitor {{{2
+fu! s:visitTree(tree, param) dict
+ if type(a:tree) == type({})
+ exe get(self, get(a:tree, 'tag', ''), '')
+ elseif type(a:tree) == type([])
+ for tree in a:tree
+ call self.visit(tree, a:param)
+ endfor
+ endif
+endfu
+
+let s:TreeVisitor = {'visit': function('s:visitTree'),
+ \ 'TOPLEVEL' : 'call self.visit(a:tree.types, a:param)',
+ \ 'BLOCK' : 'let stats = a:tree.stats | if stats == [] | call java_parser#GotoPosition(a:tree.pos) | let stats = java_parser#block().stats | endif | call self.visit(stats, a:param)',
+ \ 'DOLOOP' : 'call self.visit(a:tree.body, a:param) | call self.visit(a:tree.cond, a:param)',
+ \ 'WHILELOOP' : 'call self.visit(a:tree.cond, a:param) | call self.visit(a:tree.body, a:param)',
+ \ 'FORLOOP' : 'call self.visit(a:tree.init, a:param) | call self.visit(a:tree.cond, a:param) | call self.visit(a:tree.step, a:param) | call self.visit(a:tree.body, a:param)',
+ \ 'FOREACHLOOP' : 'call self.visit(a:tree.var, a:param) | call self.visit(a:tree.expr, a:param) | call self.visit(a:tree.body, a:param)',
+ \ 'LABELLED' : 'call self.visit(a:tree.body, a:param)',
+ \ 'SWITCH' : 'call self.visit(a:tree.selector, a:param) | call self.visit(a:tree.cases, a:param)',
+ \ 'CASE' : 'call self.visit(a:tree.pat, a:param) | call self.visit(a:tree.stats, a:param)',
+ \ 'SYNCHRONIZED': 'call self.visit(a:tree.lock, a:param) | call self.visit(a:tree.body, a:param)',
+ \ 'TRY' : 'call self.visit(a:tree.body, a:param) | call self.visit(a:tree.catchers, a:param) | call self.visit(a:tree.finalizer, a:param) ',
+ \ 'CATCH' : 'call self.visit(a:tree.param,a:param) | call self.visit(a:tree.body, a:param)',
+ \ 'CONDEXPR' : 'call self.visit(a:tree.cond, a:param) | call self.visit(a:tree.truepart, a:param) | call self.visit(a:tree.falsepart, a:param)',
+ \ 'IF' : 'call self.visit(a:tree.cond, a:param) | call self.visit(a:tree.thenpart, a:param) | if has_key(a:tree, "elsepart") | call self.visit(a:tree.elsepart, a:param) | endif',
+ \ 'EXEC' : 'call self.visit(a:tree.expr, a:param)',
+ \ 'APPLY' : 'call self.visit(a:tree.meth, a:param) | call self.visit(a:tree.args, a:param)',
+ \ 'NEWCLASS' : 'call self.visit(a:tree.def, a:param)'
+ \}
+
+let s:TV_CMP_POS = 'a:tree.pos <= a:param.pos && a:param.pos <= get(a:tree, "endpos", -1)'
+let s:TV_CMP_POS_BODY = 'has_key(a:tree, "body") && a:tree.body.pos <= a:param.pos && a:param.pos <= get(a:tree.body, "endpos", -1)'
+
+" Return a stack of enclosing types (including local or anonymous classes).
+" Given the optional argument, return all (toplevel or static member) types besides enclosing types.
+fu! s:SearchTypeAt(tree, targetPos, ...)
+ let s:TreeVisitor.CLASSDEF = 'if a:param.allNonLocal || ' . s:TV_CMP_POS . ' | call add(a:param.result, a:tree) | call self.visit(a:tree.defs, a:param) | endif'
+ let s:TreeVisitor.METHODDEF = 'if ' . s:TV_CMP_POS_BODY . ' | call self.visit(a:tree.body, a:param) | endif'
+ let s:TreeVisitor.VARDEF = 'if has_key(a:tree, "init") && !a:param.allNonLocal && ' . s:TV_CMP_POS . ' | call self.visit(a:tree.init, a:param) | endif'
+
+ let result = []
+ call s:TreeVisitor.visit(a:tree, {'result': result, 'pos': a:targetPos, 'allNonLocal': a:0 == 0 ? 0 : 1})
+ return result
+endfu
+
+" a:1 match beginning
+" return a stack of matching name
+fu! s:SearchNameInAST(tree, name, targetPos, fullmatch)
+ let comparator = a:fullmatch ? '==#' : '=~# "^" .'
+ let cmd = 'if a:tree.name ' .comparator. ' a:param.name | call add(a:param.result, a:tree) | endif'
+ let s:TreeVisitor.CLASSDEF = 'if ' . s:TV_CMP_POS . ' | ' . cmd . ' | call self.visit(a:tree.defs, a:param) | endif'
+ let s:TreeVisitor.METHODDEF = cmd . ' | if ' . s:TV_CMP_POS_BODY . ' | call self.visit(a:tree.params, a:param) | call self.visit(a:tree.body, a:param) | endif'
+ let s:TreeVisitor.VARDEF = cmd . ' | if has_key(a:tree, "init") && ' . s:TV_CMP_POS . ' | call self.visit(a:tree.init, a:param) | endif'
+
+ let result = []
+ call s:TreeVisitor.visit(a:tree, {'result': result, 'pos': a:targetPos, 'name': a:name})
+ "call s:Info(a:name . ' ' . string(result) . ' line: ' . line('.') . ' col: ' . col('.')) . ' ' . a:targetPos
+ return result
+endfu
+
+
+" javacomplete#Searchdecl {{{2
+" TODO:
+fu! javacomplete#Searchdecl()
+ let var = expand('<cword>')
+
+ let line = line('.')-1
+ let col = col('.')-1
+
+
+ if var =~# '^\(this\|super\)$'
+ if &ft == 'jsp'
+ return ''
+ endif
+
+ let matchs = s:SearchTypeAt(javacomplete#parse(), java_parser#MakePos(line, col))
+
+ let stat = s:GetStatement()
+ for t in matchs
+ if stat =~ t.name
+ let coor = java_parser#DecodePos(t.pos)
+ return var . '(' . (coor.line+1) . ',' . (coor.col) . ') ' . getline(coor.line+1)
+ endif
+ endfor
+ if len(matchs) > 0
+ let coor = java_parser#DecodePos(matchs[len(matchs)-1].pos)
+ return var . '(' . (coor.line+1) . ',' . (coor.col) . ') ' . getline(coor.line+1)
+ endif
+ return ''
+ endif
+
+ " Type.this.
+ " new Type()
+ " new Type(param1, param2)
+ " this.field
+ " super.field
+
+ let s:log = []
+
+
+ " It may be an imported class.
+ let imports = []
+ for fqn in s:GetImports('imports_fqn')
+ if fqn =~# '\<' . var . '\>$'
+ call add(imports, fqn)
+ endif
+ endfor
+ if len(imports) > 1
+ echoerr 'Imports conflicts between ' . join(imports, ' and ')
+ endif
+
+
+ " Search in this buffer
+ let matchs = s:SearchNameInAST(javacomplete#parse(), var, java_parser#MakePos(line, col), 1)
+
+
+ let hint = var . ' '
+ if !empty(matchs)
+ let tree = matchs[len(matchs)-1]
+ let coor = java_parser#DecodePos(tree.pos)
+ let hint .= '(' . (coor.line+1) . ',' . (coor.col) . ') '
+ let hint .= getline(coor.line+1) "string(tree)
+ else
+ for fqn in imports
+ let ci = s:DoGetClassInfo(fqn)
+ if !empty(ci)
+ let hint .= ' ' . fqn
+ endif
+ " TODO: get javadoc
+ endfor
+
+ endif
+ return hint
+endfu
+
+
+" java {{{1
+
+fu! s:IsBuiltinType(name)
+ return index(s:PRIMITIVE_TYPES, a:name) >= 0
+endfu
+
+fu! s:IsKeyword(name)
+ return index(s:KEYWORDS, a:name) >= 0
+endfu
+
+fu! s:HasKeyword(name)
+ return a:name =~# s:RE_KEYWORDS
+endfu
+
+fu! s:TailOfQN(qn)
+ return a:qn[strridx(a:qn, '.')+1:]
+endfu
+
+" options {{{1
+" Methods to search declaration {{{2
+" 1 - by builtin searchdecl()
+" 2 - by special Searchdecl()
+" 4 - by java_parser
+fu! javacomplete#GetSearchdeclMethod()
+ if &ft == 'jsp'
+ return 1
+ endif
+ return exists('s:searchdecl') ? s:searchdecl : 4
+endfu
+
+fu! javacomplete#SetSearchdeclMethod(method)
+ let s:searchdecl = a:method
+endfu
+
+" JDK1.1 {{{2
+fu! javacomplete#UseJDK11()
+ let s:isjdk11 = 1
+endfu
+
+" java compiler {{{2
+fu! javacomplete#GetCompiler()
+ return exists('s:compiler') && s:compiler !~ '^\s*$' ? s:compiler : 'javac'
+endfu
+
+fu! javacomplete#SetCompiler(compiler)
+ let s:compiler = a:compiler
+endfu
+
+" jvm launcher {{{2
+fu! javacomplete#GetJVMLauncher()
+ return exists('s:interpreter') && s:interpreter !~ '^\s*$' ? s:interpreter : 'java'
+endfu
+
+fu! javacomplete#SetJVMLauncher(interpreter)
+ if javacomplete#GetJVMLauncher() != a:interpreter
+ let s:cache = {}
+ endif
+ let s:interpreter = a:interpreter
+endfu
+
+" sourcepath {{{2
+fu! javacomplete#AddSourcePath(s)
+ if !isdirectory(a:s)
+ echoerr 'invalid source path: ' . a:s
+ return
+ endif
+ let path = fnamemodify(a:s, ':p:h')
+ if !exists('s:sourcepath')
+ let s:sourcepath = [path]
+ elseif index(s:sourcepath, path) == -1
+ call add(s:sourcepath, path)
+ endif
+endfu
+
+fu! javacomplete#DelSourcePath(s)
+ if !exists('s:sourcepath') || !isdirectory(a:s)| return | endif
+ let idx = index(s:sourcepath, a:s)
+ if idx != -1
+ call remove(s:sourcepath, idx)
+ endif
+endfu
+
+fu! javacomplete#SetSourcePath(s)
+ let paths = type(a:s) == type([]) ? a:s : split(a:s, javacomplete#GetClassPathSep())
+ let s:sourcepath = []
+ for path in paths
+ if isdirectory(path)
+ call add(s:sourcepath, fnamemodify(path, ':p:h'))
+ endif
+ endfor
+endfu
+
+" return the sourcepath. Given argument, add current path or default package root path
+" NOTE: Avoid path duplicate, otherwise globpath() will return duplicate result.
+fu! javacomplete#GetSourcePath(...)
+ return join(s:GetSourceDirs(a:0 > 0 && a:1 ? expand('%:p') : ''), s:PATH_SEP)
+endfu
+
+fu! s:GetSourceDirs(filepath, ...)
+ let dirs = exists('s:sourcepath') ? s:sourcepath : []
+
+ if !empty(a:filepath)
+ let filepath = fnamemodify(a:filepath, ':p:h')
+
+ " get source path according to file path and package name
+ let packageName = a:0 > 0 ? a:1 : s:GetPackageName()
+ if packageName != ''
+ let path = fnamemodify(substitute(filepath, packageName, '', 'g'), ':p:h')
+ if index(dirs, path) < 0
+ call add(dirs, path)
+ endif
+ endif
+
+ " Consider current path as a sourcepath
+ if index(dirs, filepath) < 0
+ call add(dirs, filepath)
+ endif
+ endif
+ return dirs
+endfu
+
+" classpath {{{2
+fu! javacomplete#AddClassPath(s)
+ if !isdirectory(a:s)
+ echoerr 'invalid classpath: ' . a:s
+ return
+ endif
+
+ if !exists('s:classpath')
+ let s:classpath = [a:s]
+ elseif index(s:classpath, a:s) == -1
+ call add(s:classpath, a:s)
+ endif
+ let s:cache = {}
+endfu
+
+fu! javacomplete#DelClassPath(s)
+ if !exists('s:classpath') | return | endif
+ let idx = index(s:classpath, a:s)
+ if idx != -1
+ call remove(s:classpath, idx)
+ endif
+endfu
+
+fu! javacomplete#SetClassPath(s)
+ if type(a:s) == type("")
+ let s:classpath = split(a:s, javacomplete#GetClassPathSep())
+ elseif type(a:s) == type([])
+ let s:classpath = a:s
+ endif
+ let s:cache = {}
+endfu
+
+fu! javacomplete#GetClassPathSep()
+ return s:PATH_SEP
+endfu
+
+fu! javacomplete#GetClassPath()
+ return exists('s:classpath') ? join(s:classpath, javacomplete#GetClassPathSep()) : ''
+endfu
+
+" s:GetClassPath() {{{2
+fu! s:GetClassPath()
+ let path = s:GetJavaCompleteClassPath() . javacomplete#GetClassPathSep()
+
+ if &ft == 'jsp'
+ let path .= s:GetClassPathOfJsp()
+ endif
+
+ if exists('b:classpath') && b:classpath !~ '^\s*$'
+ return path . b:classpath
+ endif
+
+ if exists('s:classpath')
+ return path . javacomplete#GetClassPath()
+ endif
+
+ if exists('g:java_classpath') && g:java_classpath !~ '^\s*$'
+ return path . g:java_classpath
+ endif
+
+ return path . $CLASSPATH
+endfu
+
+fu! s:GetJavaCompleteClassPath()
+ " remove *.class from wildignore if it exists, so that globpath doesn't ignore Reflection.class
+ " vim versions >= 702 can add the 1 flag to globpath which ignores '*.class" in wildingore
+ let has_class = 0
+ if &wildignore =~# "*.class"
+ set wildignore-=*.class
+ let has_class = 1
+ endif
+
+ let classfile = globpath(&rtp, 'autoload/Reflection.class')
+ if classfile == ''
+ let classfile = globpath($CLASS_DEST, 'Reflection.class')
+ endif
+ if classfile == ''
+ " try to find source file and compile to $HOME
+ let srcfile = globpath(&rtp, 'autoload/Reflection.java')
+ if srcfile != ''
+ exe '!' . javacomplete#GetCompiler() . ' -d "' . $CLASS_DEST . '" "' . srcfile . '"'
+ let classfile = globpath($CLASS_DEST, 'Reflection.class')
+ if classfile == ''
+ echo srcfile . ' can not be compiled. Please check it'
+ endif
+ else
+ echo 'No Reflection.class found in $CLASS_DEST or any autoload directory of the &rtp. And no Reflection.java found in any autoload directory of the &rtp to compile.'
+ endif
+ endif
+
+ " add *.class to wildignore if it existed before
+ if has_class == 1
+ set wildignore+=*.class
+ endif
+
+ return fnamemodify(classfile, ':p:h')
+endfu
+
+fu! s:GetClassPathOfJsp()
+ if exists('b:classpath_jsp')
+ return b:classpath_jsp
+ endif
+
+ let b:classpath_jsp = ''
+ let path = expand('%:p:h')
+ while 1
+ if isdirectory(path . '/WEB-INF' )
+ if isdirectory(path . '/WEB-INF/classes')
+ let b:classpath_jsp .= s:PATH_SEP . path . '/WEB-INF/classes'
+ endif
+ if isdirectory(path . '/WEB-INF/lib')
+ let libs = globpath(path . '/WEB-INF/lib', '*.jar')
+ if libs != ''
+ let b:classpath_jsp .= s:PATH_SEP . substitute(libs, "\n", s:PATH_SEP, 'g')
+ endif
+ endif
+ return b:classpath_jsp
+ endif
+
+ let prev = path
+ let path = fnamemodify(path, ":p:h:h")
+ if path == prev
+ break
+ endif
+ endwhile
+ return ''
+endfu
+
+" return only classpath which are directories
+fu! s:GetClassDirs()
+ let dirs = []
+ for path in split(s:GetClassPath(), s:PATH_SEP)
+ if isdirectory(path)
+ call add(dirs, fnamemodify(path, ':p:h'))
+ endif
+ endfor
+ return dirs
+endfu
+
+" s:GetPackageName() {{{2
+fu! s:GetPackageName()
+ let lnum_old = line('.')
+ let col_old = col('.')
+
+ call cursor(1, 1)
+ let lnum = search('^\s*package[ \t\r\n]\+\([a-zA-Z][a-zA-Z0-9.]*\);', 'w')
+ let packageName = substitute(getline(lnum), '^\s*package\s\+\([a-zA-Z][a-zA-Z0-9.]*\);', '\1', '')
+
+ call cursor(lnum_old, col_old)
+ return packageName
+endfu
+
+fu! s:IsStatic(modifier)
+ return a:modifier[strlen(a:modifier)-4]
+endfu
+
+" utilities {{{1
+" Convert a file name into the unique form.
+" Similar with fnamemodify(). NOTE that ':gs' should not be used.
+fu! s:fnamecanonize(fname, mods)
+ return fnamemodify(a:fname, a:mods . ':gs?[\\/]\+?/?')
+endfu
+
+" Similar with filter(), but returns a new list instead of operating in-place.
+" `item` has the value of the current item.
+fu! s:filter(expr, string)
+ if type(a:expr) == type([])
+ let result = []
+ for item in a:expr
+ if eval(a:string)
+ call add(result, item)
+ endif
+ endfor
+ return result
+ else
+ let result = {}
+ for item in items(a:expr)
+ if eval(a:string)
+ let result[item[0]] = item[1]
+ endif
+ endfor
+ return result
+ endif
+endfu
+
+fu! s:Index(list, expr, key)
+ let i = 0
+ while i < len(a:list)
+ if get(a:list[i], a:key, '') == a:expr
+ return i
+ endif
+ let i += 1
+ endwhile
+ return -1
+endfu
+
+fu! s:Match(list, expr, key)
+ let i = 0
+ while i < len(a:list)
+ if get(a:list[i], a:key, '') =~ a:expr
+ return i
+ endif
+ let i += 1
+ endwhile
+ return -1
+endfu
+
+fu! s:KeepCursor(cmd)
+ let lnum_old = line('.')
+ let col_old = col('.')
+ exe a:cmd
+ call cursor(lnum_old, col_old)
+endfu
+
+fu! s:InCommentOrLiteral(line, col)
+ if has("syntax") && &ft != 'jsp'
+ return synIDattr(synID(a:line, a:col, 1), "name") =~? '\(Comment\|String\|Character\)'
+ endif
+endfu
+
+function! s:InComment(line, col)
+ if has("syntax") && &ft != 'jsp'
+ return synIDattr(synID(a:line, a:col, 1), "name") =~? 'comment'
+ endif
+" if getline(a:line) =~ '\s*\*'
+" return 1
+" endif
+" let idx = strridx(getline(a:line), '//')
+" if idx >= 0 && idx < a:col
+" return 1
+" endif
+" return 0
+endfunction
+
+" set string literal empty, remove comments, trim begining or ending spaces
+" test case: ' sb. /* block comment*/ append( "stringliteral" ) // comment '
+function! s:Prune(str, ...)
+ if a:str =~ '^\s*$' | return '' | endif
+
+ let str = substitute(a:str, '"\(\\\(["\\''ntbrf]\)\|[^"]\)*"', '""', 'g')
+ let str = substitute(str, '\/\/.*', '', 'g')
+ let str = s:RemoveBlockComments(str)
+ return a:0 > 0 ? str : str . ' '
+endfunction
+
+" Given argument, replace block comments with spaces of same number
+fu! s:RemoveBlockComments(str, ...)
+ let result = a:str
+ let ib = match(result, '\/\*')
+ let ie = match(result, '\*\/')
+ while ib != -1 && ie != -1 && ib < ie
+ let result = strpart(result, 0, ib) . (a:0 == 0 ? ' ' : repeat(' ', ie-ib+2)) . result[ie+2: ]
+ let ib = match(result, '\/\*')
+ let ie = match(result, '\*\/')
+ endwhile
+ return result
+endfu
+
+fu! s:Trim(str)
+ let str = substitute(a:str, '^\s*', '', '')
+ return substitute(str, '\s*$', '', '')
+endfu
+
+fu! s:SplitAt(str, index)
+ return [strpart(a:str, 0, a:index+1), strpart(a:str, a:index+1)]
+endfu
+
+" TODO: search pair used in string, like
+" 'create(ao.fox("("), new String).foo().'
+function! s:GetMatchedIndexEx(str, idx, one, another)
+ let pos = a:idx
+ while 0 <= pos && pos < len(a:str)
+ let pos = match(a:str, '['. a:one . escape(a:another, ']') .']', pos+1)
+ if pos != -1
+ if a:str[pos] == a:one
+ let pos = s:GetMatchedIndexEx(a:str, pos, a:one, a:another)
+ elseif a:str[pos] == a:another
+ break
+ endif
+ endif
+ endwhile
+ return 0 <= pos && pos < len(a:str) ? pos : -3
+endfunction
+
+function! s:SearchPairBackward(str, idx, one, another)
+ let idx = a:idx
+ let n = 0
+ while idx >= 0
+ let idx -= 1
+ if a:str[idx] == a:one
+ if n == 0
+ break
+ endif
+ let n -= 1
+ elseif a:str[idx] == a:another " nested
+ let n += 1
+ endif
+ endwhile
+ return idx
+endfunction
+
+fu! s:CountDims(str)
+ if match(a:str, '[[\]]') == -1
+ return 0
+ endif
+
+ " int[] -> [I, String[] ->
+ let dims = len(matchstr(a:str, '^[\+'))
+ if dims == 0
+ let idx = len(a:str)-1
+ while idx >= 0 && a:str[idx] == ']'
+ let dims += 1
+ let idx = s:SearchPairBackward(a:str, idx, '[', ']')-1
+ endwhile
+ endif
+ return dims
+endfu
+
+fu! s:GotoUpperBracket()
+ let searched = 0
+ while (!searched)
+ call search('[{}]', 'bW')
+ if getline('.')[col('.')-1] == '}'
+ normal %
+ else
+ let searched = 1
+ endif
+ endwhile
+endfu
+
+" Improve recognition of variable declaration using my version of searchdecl() for accuracy reason.
+" TODO:
+fu! s:Searchdecl(name, ...)
+ let global = a:0 > 0 ? a:1 : 0
+ let thisblock = a:0 > 1 ? a:2 : 1
+
+ call search('\<' . a:name . '\>', 'bW')
+ let lnum_old = line('.')
+ let col_old = col('.')
+
+ call s:GotoUpperBracket()
+ let lnum_bracket = line('.')
+ let col_bracket = col('.')
+ call search('\<' . a:name . '\>', 'W', lnum_old)
+ if line('.') != lnum_old || col('.') != col_old
+ return 0
+ endif
+
+ " search globally
+ if global
+ call cursor(lnum_bracket, col_bracket)
+ " search backward
+ while (1)
+ if search('\([{}]\|\<' . a:name . '\>\)', 'bW') == 0
+ break
+ endif
+ if s:InComment(line('.'), col('.')) "|| s:InStringLiteral()
+ continue
+ endif
+ let cword = expand('<cword>')
+ if cword == a:name
+ return 0
+ endif
+ if getline('.')[col('.')-1] == '}'
+ normal %
+ endif
+ endwhile
+
+ call cursor(lnum_old, col_old)
+ " search forward
+ call search('[{};]', 'W')
+ while (1)
+ if search('\([{}]\|\<' . a:name . '\>\)', 'W') == 0
+ break
+ endif
+ if s:InComment(line('.'), col('.')) "|| s:InStringLiteral()
+ continue
+ endif
+ let cword = expand('<cword>')
+ if cword == a:name
+ return 0
+ endif
+ if getline('.')[col('.')-1] == '{'
+ normal %
+ endif
+ endwhile
+ endif
+ return 1
+endfu
+"nmap <F8> :call <SID>Searchdecl(expand('<cword>'))<CR>
+
+fu! javacomplete#Exe(cmd)
+ exe a:cmd
+endfu
+
+" cache utilities {{{1
+
+" key of s:files for current buffer. It may be the full path of current file or the bufnr of unnamed buffer, and is updated when BufEnter, BufLeave.
+fu! s:GetCurrentFileKey()
+ return has("autocmd") ? s:curfilekey : empty(expand('%')) ? bufnr('%') : expand('%:p')
+endfu
+
+fu! s:SetCurrentFileKey()
+ let s:curfilekey = empty(expand('%')) ? bufnr('%') : expand('%:p')
+endfu
+
+call s:SetCurrentFileKey()
+if has("autocmd")
+ autocmd BufEnter *.java call s:SetCurrentFileKey()
+ autocmd FileType java call s:SetCurrentFileKey()
+endif
+
+
+" Log utilities {{{1
+fu! s:WatchVariant(variant)
+ "echoerr a:variant
+endfu
+
+" level
+" 5 off/fatal
+" 4 error
+" 3 warn
+" 2 info
+" 1 debug
+" 0 trace
+fu! javacomplete#SetLogLevel(level)
+ let s:loglevel = a:level
+endfu
+
+fu! javacomplete#GetLogLevel()
+ return exists('s:loglevel') ? s:loglevel : 3
+endfu
+
+fu! javacomplete#GetLogContent()
+ return s:log
+endfu
+
+fu! s:Trace(msg)
+ call s:Log(0, a:msg)
+endfu
+
+fu! s:Debug(msg)
+ call s:Log(1, a:msg)
+endfu
+
+fu! s:Info(msg)
+ call s:Log(2, a:msg)
+endfu
+
+fu! s:Log(level, key, ...)
+ if a:level >= javacomplete#GetLogLevel()
+ echo a:key
+ call add(s:log, a:key)
+ endif
+endfu
+
+fu! s:System(cmd, caller)
+ call s:WatchVariant(a:cmd)
+ let t = reltime()
+ let res = system(a:cmd)
+ call s:Debug(reltimestr(reltime(t)) . 's to exec "' . a:cmd . '" by ' . a:caller)
+ return res
+endfu
+
+" functions to get information {{{1
+" utilities {{{2
+fu! s:MemberCompare(m1, m2)
+ return a:m1['n'] == a:m2['n'] ? 0 : a:m1['n'] > a:m2['n'] ? 1 : -1
+endfu
+
+fu! s:Sort(ci)
+ let ci = a:ci
+ if has_key(ci, 'fields')
+ call sort(ci['fields'], 's:MemberCompare')
+ endif
+ if has_key(ci, 'methods')
+ call sort(ci['methods'], 's:MemberCompare')
+ endif
+ return ci
+endfu
+
+" Function to run Reflection {{{2
+fu! s:RunReflection(option, args, log)
+ let classpath = ''
+ if !exists('s:isjdk11')
+ let classpath = ' -classpath "' . s:GetClassPath() . '" '
+ endif
+
+ let cmd = javacomplete#GetJVMLauncher() . classpath . ' Reflection ' . a:option . ' "' . a:args . '"'
+ return s:System(cmd, a:log)
+endfu
+" class information {{{2
+
+
+" The standard search order of a FQN is as follows:
+" 1. a file-name toplevel type or static member type accessed by the file-name type declared in source files
+" 2. other types declared in source files
+" 3. an accessible loadable type.
+" parameters:
+" fqns - list of fqn
+" srcpaths - a comma-separated list of directory names.
+" a:1 - search all.
+" return a dict of fqn -> type info
+" precondition:
+" NOTE: call expand() to convert path to standard form
+fu! s:DoGetTypeInfoForFQN(fqns, srcpath, ...)
+ if empty(a:fqns) || empty(a:srcpath)
+ return
+ endif
+
+ " 1
+ let files = {} " fqn -> java file path
+ for fqn in a:fqns
+ " toplevel type
+ let filepath = globpath(a:srcpath, substitute(fqn, '\.', '/', 'g') . '.java')
+ if filepath != ''
+ let files[fqn] = expand(filepath)
+
+ " nested type
+ elseif stridx(fqn, '.') >= 0
+ let idents = split(fqn, '\.')
+ let i = len(idents)-2
+ while i >= 0
+ let filepath = globpath(a:srcpath, join(idents[:i], '/') . '.java')
+ if filepath != ''
+ let files[fqn] = expand(filepath)
+ break
+ endif
+ let i -= 1
+ endwhile
+ endif
+ endfor
+
+
+ " 2
+ let dirs = {} " dir.idents -> names of nested type
+ " dir.qfitems -> items of quick fix
+ " dir.fqn -> fqn
+ for fqn in a:fqns
+ if !has_key(files, fqn)
+ for path in split(a:srcpath, ',')
+ let idents = split(fqn, '\.')
+ let i = len(idents)-2
+ while i >= 0
+ let dirpath = path . '/' . join(idents[:i], '/')
+ " it is a package
+ if isdirectory(dirpath)
+ let dirs[fnamemodify(dirpath, ':p:h:gs?[\\/]\+?/?')] = {'fqn': fqn, 'idents': idents[i+1:]}
+ break
+ endif
+ let i -= 1
+ endwhile
+ endfor
+ endif
+ endfor
+
+ if !empty(dirs)
+ let items = {} " dir -> items of quick fix
+
+ let filepatterns = ''
+ for dirpath in keys(dirs)
+ let filepatterns .= escape(dirpath, ' \') . '/*.java '
+ endfor
+
+ let cwd = fnamemodify(expand('%:p:h'), ':p:h:gs?[\\/]\+?/?')
+ exe 'vimgrep /\s*' . s:RE_TYPE_DECL . '/jg ' . filepatterns
+ for item in getqflist()
+ if item.text !~ '^\s*\*\s\+'
+ let text = matchstr(s:Prune(item.text, -1), '\s*' . s:RE_TYPE_DECL)
+ if text != ''
+ let subs = split(substitute(text, '\s*' . s:RE_TYPE_DECL, '\1;\2;\3', ''), ';', 1)
+ let dirpath = fnamemodify(bufname(item.bufnr), ':p:h:gs?[\\/]\+?/?')
+ let idents = dirs[dirpath].idents
+ if index(idents, subs[2]) >= 0 && (subs[0] =~ '\C\<public\>' || dirpath == cwd) " FIXME?
+ let item.subs = subs
+ let dirs[dirpath].qfitems = get(dirs[dirpath], 'qfitems', []) + [item]
+ endif
+ endif
+ endif
+ endfor
+
+ for dirpath in keys(dirs)
+ " a. names of nested type must be existed in the same file
+ " PackageName.NonFileNameTypeName.NestedType.NestedNestedType
+ let qfitems = get(dirs[dirpath], 'qfitems', [])
+ let nr = 0
+ for ident in dirs[dirpath].idents
+ for item in qfitems
+ if item.subs[2] == ident
+ let nr += 1
+ endif
+ endfor
+ endfor
+ if nr == len(dirs[dirpath].idents)
+ " b. TODO: Check whether one enclosed another is correct
+ let files[fqn] = expand(bufname(qfitems[0].bufnr))
+ endif
+ endfor
+ endif
+
+
+ call s:Info('FQN1&2: ' . string(keys(files)))
+ for fqn in keys(files)
+ if !has_key(s:cache, fqn) || get(get(s:files, files[fqn], {}), 'modifiedtime', 0) != getftime(files[fqn])
+ let ti = s:GetClassInfoFromSource(fqn[strridx(fqn, '.')+1:], files[fqn])
+ if !empty(ti)
+ let s:cache[fqn] = s:Sort(ti)
+ endif
+ endif
+ if (a:0 == 0 || !a:1)
+ return
+ endif
+ endfor
+
+
+ " 3
+ let commalist = ''
+ for fqn in a:fqns
+ if has_key(s:cache, fqn) && (a:0 == 0 || !a:1)
+ return
+ else "if stridx(fqn, '.') >= 0
+ let commalist .= fqn . ','
+ endif
+ endfor
+ if !empty(commalist)
+ let res = s:RunReflection('-E', commalist, 'DoGetTypeInfoForFQN in Batch')
+ if res =~ "^{'"
+ let dict = eval(res)
+ for key in keys(dict)
+ if !has_key(s:cache, key)
+ if type(dict[key]) == type({})
+ let s:cache[key] = s:Sort(dict[key])
+ elseif type(dict[key]) == type([])
+ let s:cache[key] = sort(dict[key])
+ endif
+ endif
+ endfor
+ endif
+ endif
+endfu
+
+" a:1 filepath
+" a:2 package name
+fu! s:DoGetClassInfo(class, ...)
+ if has_key(s:cache, a:class)
+ return s:cache[a:class]
+ endif
+
+ " array type: TypeName[] or '[I' or '[[Ljava.lang.String;'
+ if a:class[-1:] == ']' || a:class[0] == '['
+ return s:ARRAY_TYPE_INFO
+ endif
+
+ " either this or super is not qualified
+ if a:class == 'this' || a:class == 'super'
+ if &ft == 'jsp'
+ let ci = s:DoGetReflectionClassInfo('javax.servlet.jsp.HttpJspPage')
+ if a:class == 'this'
+ " TODO: search methods defined in <%! [declarations] %>
+ " search methods defined in other jsp files included
+ " avoid including self directly or indirectly
+ endif
+ return ci
+ endif
+
+ call s:Info('A0. ' . a:class)
+ " this can be a local class or anonymous class as well as static type
+ let t = get(s:SearchTypeAt(javacomplete#parse(), java_parser#MakePos(line('.')-1, col('.')-1)), -1, {})
+ if !empty(t)
+ " What will be returned for super?
+ " - the protected or public inherited fields and methods. No ctors.
+ " - the (public static) fields of interfaces.
+ " - the methods of the Object class.
+ " What will be returned for this?
+ " - besides the above, all fields and methods of current class. No ctors.
+ return s:Sort(s:Tree2ClassInfo(t))
+ "return s:Sort(s:AddInheritedClassInfo(a:class == 'this' ? s:Tree2ClassInfo(t) : {}, t, 1))
+ endif
+
+ return {}
+ endif
+
+
+ if a:class !~ '^\s*' . s:RE_QUALID . '\s*$' || s:HasKeyword(a:class)
+ return {}
+ endif
+
+
+ let typename = substitute(a:class, '\s', '', 'g')
+ let filekey = a:0 > 0 ? a:1 : s:GetCurrentFileKey()
+ let packagename = a:0 > 1 ? a:2 : s:GetPackageName()
+ let srcpath = join(s:GetSourceDirs(a:0 > 0 && a:1 != bufnr('%') ? a:1 : expand('%:p'), packagename), ',')
+
+ let names = split(typename, '\.')
+ " remove the package name if in the same packge
+ if len(names) > 1
+ if packagename == join(names[:-2], '.')
+ let names = names[-1:]
+ endif
+ endif
+
+ " a FQN
+ if len(names) > 1
+ call s:DoGetTypeInfoForFQN([typename], srcpath)
+ let ci = get(s:cache, typename, {})
+ if get(ci, 'tag', '') == 'CLASSDEF'
+ return s:cache[typename]
+ elseif get(ci, 'tag', '') == 'PACKAGE'
+ return {}
+ endif
+ endif
+
+
+ " The standard search order of a simple type name is as follows:
+ " 1. The current type including inherited types.
+ " 2. A nested type of the current type.
+ " 3. Explicitly named imported types (single type import).
+ " 4. Other types declared in the same package. Not only current directory.
+ " 5. Implicitly named imported types (import on demand).
+
+ " 1 & 2.
+ " NOTE: inherited types are treated as normal
+ if filekey == s:GetCurrentFileKey()
+ let simplename = typename[strridx(typename, '.')+1:]
+ if s:FoundClassDeclaration(simplename) != 0
+ call s:Info('A1&2')
+ let ci = s:GetClassInfoFromSource(simplename, '%')
+ " do not cache it
+ if !empty(ci)
+ return ci
+ endif
+ endif
+ else
+ let ci = s:GetClassInfoFromSource(typename, filekey)
+ if !empty(ci)
+ return ci
+ endif
+ endif
+
+ " 3.
+ " NOTE: PackageName.Ident, TypeName.Ident
+ let fqn = s:SearchSingleTypeImport(typename, s:GetImports('imports_fqn', filekey))
+ if !empty(fqn)
+ call s:Info('A3')
+ call s:DoGetTypeInfoForFQN([fqn], srcpath)
+ let ti = get(s:cache, fqn, {})
+ if get(ti, 'tag', '') != 'CLASSDEF'
+ " TODO: mark the wrong import declaration.
+ endif
+ return ti
+ endif
+
+ " 4 & 5
+ " NOTE: Keeps the fqn of the same package first!!
+ call s:Info('A4&5')
+ let fqns = [empty(packagename) ? typename : packagename . '.' . typename]
+ for p in s:GetImports('imports_star', filekey)
+ call add(fqns, p . typename)
+ endfor
+ call s:DoGetTypeInfoForFQN(fqns, srcpath)
+ for fqn in fqns
+ if has_key(s:cache, fqn)
+ return get(s:cache[fqn], 'tag', '') == 'CLASSDEF' ? s:cache[fqn] : {}
+ endif
+ endfor
+
+ return {}
+endfu
+
+" Rules of overriding and hiding:
+" 1. Fields cannot be overridden; they can only be hidden.
+" In the subclass, the hidden field of superclass can no longer be accessed
+" directly by its simple name. `super` or another reference must be used.
+" 2. A method can be overriden only if it is accessible.
+" When overriding methods, both the signature and return type must be the
+" same as in the superclass.
+" 3. Static members cannot be overridden; they can only be hidden
+" -- whether a field or a method. But hiding static members has little effect,
+" because static should be accessed via the name of its declaring class.
+" Given optional argument, add protected, default (package) access, private members.
+"fu! s:MergeClassInfo(ci, another, ...)
+" if empty(a:another) | return a:ci | endif
+"
+" if empty(a:ci)
+" let ci = copy(a:another)
+"" if a:0 > 0 && a:1
+"" call extend(ci.fields, get(a:another, 'declared_fields', []))
+"" call extend(ci.methods, get(a:another, 'declared_methods', []))
+"" endif
+" return ci
+" endif
+"
+" call extend(a:ci.methods, a:another.methods)
+"
+" for f in a:another.fields
+" if s:Index(a:ci.fields, f.n, 'n') < 0
+" call add(a:ci.fields, f)
+" endif
+" endfor
+" return a:ci
+"endfu
+
+
+" Parameters:
+" class the qualified class name
+" Return: TClassInfo or {} when not found
+" See ClassInfoFactory.getClassInfo() in insenvim.
+function! s:DoGetReflectionClassInfo(fqn)
+ if !has_key(s:cache, a:fqn)
+ let res = s:RunReflection('-C', a:fqn, 's:DoGetReflectionClassInfo')
+ if res =~ '^{'
+ let s:cache[a:fqn] = s:Sort(eval(res))
+ elseif res =~ '^['
+ for type in eval(res)
+ if get(type, 'name', '') != ''
+ let s:cache[type.name] = s:Sort(type)
+ endif
+ endfor
+ else
+ let b:errormsg = res
+ endif
+ endif
+ return get(s:cache, a:fqn, {})
+endfunction
+
+fu! s:GetClassInfoFromSource(class, filename)
+ let ci = {}
+ if len(tagfiles()) > 0
+ let ci = s:DoGetClassInfoFromTags(a:class)
+ endif
+
+ if empty(ci)
+ call s:Info('Use java_parser.vim to generate class information')
+ let unit = javacomplete#parse(a:filename)
+ let targetPos = a:filename == '%' ? java_parser#MakePos(line('.')-1, col('.')-1) : -1
+ for t in s:SearchTypeAt(unit, targetPos, 1)
+ if t.name == a:class
+ let t.filepath = a:filename == '%' ? s:GetCurrentFileKey() : expand(a:filename)
+ return s:Tree2ClassInfo(t)
+ "return s:AddInheritedClassInfo(s:Tree2ClassInfo(t), t)
+ endif
+ endfor
+ endif
+ return ci
+endfu
+
+fu! s:Tree2ClassInfo(t)
+ let t = a:t
+
+ " fill fields and methods
+ let t.fields = []
+ let t.methods = []
+ let t.ctors = []
+ let t.classes = []
+ for def in t.defs
+ if def.tag == 'METHODDEF'
+ call add(def.n == t.name ? t.ctors : t.methods, def)
+ elseif def.tag == 'VARDEF'
+ call add(t.fields, def)
+ elseif def.tag == 'CLASSDEF'
+ call add(t.classes, t.fqn . '.' . def.name)
+ endif
+ endfor
+
+ " convert type name in extends to fqn for class defined in source files
+ if !has_key(a:t, 'classpath') && has_key(a:t, 'extends')
+ if has_key(a:t, 'filepath') && a:t.filepath != s:GetCurrentFileKey()
+ let filepath = a:t.filepath
+ let packagename = get(s:files[filepath].unit, 'package', '')
+ else
+ let filepath = expand('%:p')
+ let packagename = s:GetPackageName()
+ endif
+
+ let extends = a:t.extends
+ let i = 0
+ while i < len(extends)
+ let ci = s:DoGetClassInfo(java_parser#type2Str(extends[i]), filepath, packagename)
+ if has_key(ci, 'fqn')
+ let extends[i] = ci.fqn
+ endif
+ let i += 1
+ endwhile
+ endif
+
+ return t
+endfu
+
+"fu! s:AddInheritedClassInfo(ci, t, ...)
+" let ci = a:ci
+" " add inherited fields and methods
+" let list = []
+" for i in get(a:t, 'extends', [])
+" call add(list, java_parser#type2Str(i))
+" endfor
+"
+" if has_key(a:t, 'filepath') && a:t.filepath != expand('%:p')
+" let filepath = a:t.filepath
+" let props = get(s:files, a:t.filepath, {})
+" let packagename = get(props.unit, 'package', '')
+" else
+" let filepath = expand('%:p')
+" let packagename = s:GetPackageName()
+" endif
+"
+" for id in list
+" let ci = s:MergeClassInfo(ci, s:DoGetClassInfo(id, filepath, packagename), a:0 > 0 && a:1)
+" endfor
+" return ci
+"endfu
+
+" To obtain information of the class in current file or current folder, or
+" even in current project.
+function! s:DoGetClassInfoFromTags(class)
+ " find tag of a:class declaration
+ let tags = taglist('^' . a:class)
+ let filename = ''
+ let cmd = ''
+ for tag in tags
+ if has_key(tag, 'kind')
+ if tag['kind'] == 'c'
+ let filename = tag['filename']
+ let cmd = tag['cmd']
+ break
+ endif
+ endif
+ endfor
+
+ let tags = taglist('^' . (empty(b:incomplete) ? '.*' : b:incomplete) )
+ if filename != ''
+ call filter(tags, "v:val['filename'] == '" . filename . "' && has_key(v:val, 'class') ? v:val['class'] == '" . a:class . "' : 1")
+ endif
+
+ let ci = {'name': a:class}
+ " extends and implements
+ let ci['ctors'] = []
+ let ci['fields'] = []
+ let ci['methods'] = []
+
+ " members
+ for tag in tags
+ let member = {'n': tag['name']}
+
+ " determine kind
+ let kind = 'm'
+ if has_key(tag, 'kind')
+ let kind = tag['kind']
+ endif
+
+ let cmd = tag['cmd']
+ if cmd =~ '\<static\>'
+ let member['m'] = '1000'
+ else
+ let member['m'] = ''
+ endif
+
+ let desc = substitute(cmd, '/^\s*', '', '')
+ let desc = substitute(desc, '\s*{\?\s*$/$', '', '')
+
+ if kind == 'm'
+ " description
+ if cmd =~ '\<static\>'
+ let desc = substitute(desc, '\s\+static\s\+', ' ', '')
+ endif
+ let member['d'] = desc
+
+ let member['p'] = ''
+ let member['r'] = ''
+ if tag['name'] == a:class
+ call add(ci['ctors'], member)
+ else
+ call add(ci['methods'], member)
+ endif
+ elseif kind == 'f'
+ let member['t'] = substitute(desc, '\([a-zA-Z0-9_[\]]\)\s\+\<' . tag['name'] . '\>.*$', '\1', '')
+ call add(ci['fields'], member)
+ endif
+ endfor
+ return ci
+endfu
+
+" package information {{{2
+
+fu! s:DoGetInfoByReflection(class, option)
+ if has_key(s:cache, a:class)
+ return s:cache[a:class]
+ endif
+
+ let res = s:RunReflection(a:option, a:class, 's:DoGetInfoByReflection')
+ if res =~ '^[{\[]'
+ let v = eval(res)
+ if type(v) == type([])
+ let s:cache[a:class] = sort(v)
+ elseif type(v) == type({})
+ if get(v, 'tag', '') =~# '^\(PACKAGE\|CLASSDEF\)$'
+ let s:cache[a:class] = v
+ else
+ call extend(s:cache, v, 'force')
+ endif
+ endif
+ unlet v
+ else
+ let b:errormsg = res
+ endif
+
+ return get(s:cache, a:class, {})
+endfu
+
+" search in members {{{2
+" TODO: what about default access?
+" public for all
+" protected for this or super
+" private for this
+fu! s:CanAccess(mods, kind)
+ return (a:mods[-4:-4] || a:kind/10 == 0)
+ \ && (a:kind == 1 || a:mods[-1:]
+ \ || (a:mods[-3:-3] && (a:kind == 1 || a:kind == 2))
+ \ || (a:mods[-2:-2] && a:kind == 1))
+endfu
+
+fu! s:SearchMember(ci, name, fullmatch, kind, returnAll, memberkind, ...)
+ let result = [[], [], []]
+
+ if a:kind != 13
+ for m in (a:0 > 0 && a:1 ? [] : get(a:ci, 'fields', [])) + ((a:kind == 1 || a:kind == 2) ? get(a:ci, 'declared_fields', []) : [])
+ if empty(a:name) || (a:fullmatch ? m.n ==# a:name : m.n =~# '^' . a:name)
+ if s:CanAccess(m.m, a:kind)
+ call add(result[2], m)
+ endif
+ endif
+ endfor
+
+ for m in (a:0 > 0 && a:1 ? [] : get(a:ci, 'methods', [])) + ((a:kind == 1 || a:kind == 2) ? get(a:ci, 'declared_methods', []) : [])
+ if empty(a:name) || (a:fullmatch ? m.n ==# a:name : m.n =~# '^' . a:name)
+ if s:CanAccess(m.m, a:kind)
+ call add(result[1], m)
+ endif
+ endif
+ endfor
+ endif
+
+ if a:kind/10 != 0
+ let types = get(a:ci, 'classes', [])
+ for t in types
+ if empty(a:name) || (a:fullmatch ? t[strridx(t, '.')+1:] ==# a:name : t[strridx(t, '.')+1:] =~# '^' . a:name)
+ if !has_key(s:cache, t) || !has_key(s:cache[t], 'flags') || a:kind == 1 || s:cache[t].flags[-1:]
+ call add(result[0], t)
+ endif
+ endif
+ endfor
+ endif
+
+ " key `classpath` indicates it is a loaded class from classpath
+ " All public members of a loaded class are stored in current ci
+ if !has_key(a:ci, 'classpath') || (a:kind == 1 || a:kind == 2)
+ for i in get(a:ci, 'extends', [])
+ let ci = s:DoGetClassInfo(java_parser#type2Str(i))
+ let members = s:SearchMember(ci, a:name, a:fullmatch, a:kind == 1 ? 2 : a:kind, a:returnAll, a:memberkind)
+ let result[0] += members[0]
+ let result[1] += members[1]
+ let result[2] += members[2]
+ endfor
+ endif
+ return result
+endfu
+
+
+" generate member list {{{2
+
+fu! s:DoGetFieldList(fields)
+ let s = ''
+ for field in a:fields
+ let s .= "{'kind':'" . (s:IsStatic(field.m) ? "F" : "f") . "','word':'" . field.n . "','menu':'" . field.t . "','dup':1},"
+ endfor
+ return s
+endfu
+
+fu! s:DoGetMethodList(methods, ...)
+ let paren = a:0 == 0 || !a:1 ? '(' : ''
+ let s = ''
+ for method in a:methods
+ let s .= "{'kind':'" . (s:IsStatic(method.m) ? "M" : "m") . "','word':'" . method.n . paren . "','abbr':'" . method.n . "()','menu':'" . method.d . "','dup':'1'},"
+ endfor
+ return s
+endfu
+
+" kind:
+" 0 - for instance, 1 - this, 2 - super, 3 - class, 4 - array, 5 - method result, 6 - primitive type
+" 11 - for type, with `class` and static member and nested types.
+" 12 - for import static, no lparen for static methods
+" 13 - for import or extends or implements, only nested types
+" 20 - for package
+fu! s:DoGetMemberList(ci, kind)
+ if type(a:ci) != type({}) || a:ci == {}
+ return []
+ endif
+
+ let s = a:kind == 11 ? "{'kind': 'C', 'word': 'class', 'menu': 'Class'}," : ''
+
+ let members = s:SearchMember(a:ci, '', 1, a:kind, 1, 0, a:kind == 2)
+
+ " add accessible member types
+ if a:kind / 10 != 0
+ " Use dup here for member type can share name with field.
+ for class in members[0]
+ "for class in get(a:ci, 'classes', [])
+ let v = get(s:cache, class, {})
+ if v == {} || v.flags[-1:]
+ let s .= "{'kind': 'C', 'word': '" . substitute(class, a:ci.name . '\.', '\1', '') . "','dup':1},"
+ endif
+ endfor
+ endif
+
+ if a:kind != 13
+ let fieldlist = []
+ let sfieldlist = []
+ for field in members[2]
+ "for field in get(a:ci, 'fields', [])
+ if s:IsStatic(field['m'])
+ call add(sfieldlist, field)
+ elseif a:kind / 10 == 0
+ call add(fieldlist, field)
+ endif
+ endfor
+
+ let methodlist = []
+ let smethodlist = []
+ for method in members[1]
+ if s:IsStatic(method['m'])
+ call add(smethodlist, method)
+ elseif a:kind / 10 == 0
+ call add(methodlist, method)
+ endif
+ endfor
+
+ if a:kind / 10 == 0
+ let s .= s:DoGetFieldList(fieldlist)
+ let s .= s:DoGetMethodList(methodlist)
+ endif
+ let s .= s:DoGetFieldList(sfieldlist)
+ let s .= s:DoGetMethodList(smethodlist, a:kind == 12)
+
+ let s = substitute(s, '\<' . a:ci.name . '\.', '', 'g')
+ let s = substitute(s, '\<java\.lang\.', '', 'g')
+ let s = substitute(s, '\<\(public\|static\|synchronized\|transient\|volatile\|final\|strictfp\|serializable\|native\)\s\+', '', 'g')
+ endif
+ return eval('[' . s . ']')
+endfu
+
+" interface {{{2
+
+function! s:GetMemberList(class)
+ if s:IsBuiltinType(a:class)
+ return []
+ endif
+
+ return s:DoGetMemberList(s:DoGetClassInfo(a:class), 0)
+endfunction
+
+fu! s:GetStaticMemberList(class)
+ return s:DoGetMemberList(s:DoGetClassInfo(a:class), 11)
+endfu
+
+function! s:GetConstructorList(class)
+ let ci = s:DoGetClassInfo(a:class)
+ if empty(ci)
+ return []
+ endif
+
+ let s = ''
+ for ctor in get(ci, 'ctors', [])
+ let s .= "{'kind': '+', 'word':'". a:class . "(','abbr':'" . ctor.d . "','dup':1},"
+ endfor
+
+ let s = substitute(s, '\<java\.lang\.', '', 'g')
+ let s = substitute(s, '\<public\s\+', '', 'g')
+ return eval('[' . s . ']')
+endfunction
+
+" Name can be a (simple or qualified) package name, or a (simple or qualified)
+" type name.
+fu! s:GetMembers(fqn, ...)
+ let list = []
+ let isClass = 0
+
+ let v = s:DoGetInfoByReflection(a:fqn, '-E')
+ if type(v) == type([])
+ let list = v
+ elseif type(v) == type({}) && v != {}
+ if get(v, 'tag', '') == 'PACKAGE'
+ if b:context_type == s:CONTEXT_IMPORT_STATIC || b:context_type == s:CONTEXT_IMPORT
+ call add(list, {'kind': 'P', 'word': '*;'})
+ endif
+ if b:context_type != s:CONTEXT_PACKAGE_DECL
+ for c in sort(get(v, 'classes', []))
+ call add(list, {'kind': 'C', 'word': c})
+ endfor
+ endif
+ for p in sort(get(v, 'subpackages', []))
+ call add(list, {'kind': 'P', 'word': p})
+ endfor
+ else " elseif get(v, 'tag', '') == 'CLASSDEF'
+ let isClass = 1
+ let list += s:DoGetMemberList(v, b:context_type == s:CONTEXT_IMPORT || b:context_type == s:CONTEXT_NEED_TYPE ? 13 : b:context_type == s:CONTEXT_IMPORT_STATIC ? 12 : 11)
+ endif
+ endif
+
+ if !isClass
+ let list += s:DoGetPackageInfoInDirs(a:fqn, b:context_type == s:CONTEXT_PACKAGE_DECL)
+ endif
+
+ return list
+endfu
+
+" a:1 incomplete mode
+" return packages in classes directories or source pathes
+fu! s:DoGetPackageInfoInDirs(package, onlyPackages, ...)
+ let list = []
+
+ let pathes = s:GetSourceDirs(expand('%:p'))
+ for path in s:GetClassDirs()
+ if index(pathes, path) <= 0
+ call add(pathes, path)
+ endif
+ endfor
+
+ let globpattern = a:0 > 0 ? a:package . '*' : substitute(a:package, '\.', '/', 'g') . '/*'
+ let matchpattern = a:0 > 0 ? a:package : a:package . '[\\/]'
+ for f in split(globpath(join(pathes, ','), globpattern), "\n")
+ for path in pathes
+ let idx = matchend(f, escape(path, ' \') . '[\\/]\?\C' . matchpattern)
+ if idx != -1
+ let name = (a:0 > 0 ? a:package : '') . strpart(f, idx)
+ if f[-5:] == '.java'
+ if !a:onlyPackages
+ call add(list, {'kind': 'C', 'word': name[:-6]})
+ endif
+ elseif name =~ '^' . s:RE_IDENTIFIER . '$' && isdirectory(f) && f !~# 'CVS$'
+ call add(list, {'kind': 'P', 'word': name})
+ endif
+ endif
+ endfor
+ endfor
+ return list
+endfu
+" }}}
+"}}}
+" vim:set fdm=marker sw=2 nowrap:
diff --git a/base/vim/bundle/javacomplete/doc/javacomplete.txt b/base/vim/bundle/javacomplete/doc/javacomplete.txt
new file mode 100644
index 0000000..ef5f211
--- /dev/null
+++ b/base/vim/bundle/javacomplete/doc/javacomplete.txt
@@ -0,0 +1,568 @@
+*javacomplete.txt* For Vim version 7.0 and above. Last change: 2011-01-30
+
+ JAVACOMPLETE REFERENCE MANUAL by cheng fang~
+ fangread@yahoo.com.cn~
+
+
+1. Overview |javacomplete-overview|
+ 1.1 Features |javacomplete-features|
+ 1.2 Requirements |javacomplete-requirements|
+ 1.3 Download |javacomplete-download|
+ 1.4 Install |javacomplete-install|
+2. Usage |javacomplete-usage|
+ 2.1 Input contexts |javacomplete-contexts|
+ 2.2 Kind letter |javacomplete-kindletter|
+ 2.3 Options |javacomplete-options|
+ 2.4 Commands |javacomplete-commands|
+3. Java parser in Vim |javacomplete-parser|
+ 3.1 Abstract Syntax Tree |javacomplete-ast|
+ 3.2 Global Constants |javacomplete-constants|
+ 3.3 Parsing Functions |javacomplete-functions|
+ 3.4 Sample |javacomplete-sample|
+4. FAQ |javacomplete-faq|
+5. Limitations |javacomplete-limitations|
+6. History
+ 6.1 javacomplete |javacomplete-history|
+ 6.2 Parser |java-parser-history|
+ 6.2 Reflection.java |javacomplete-reflection|
+7. Todo |javacomplete-todo|
+8. Thanks |javacomplete-thanks|
+
+==============================================================================
+1. Overview *javacomplete-overview*
+
+This is javacomplete, an omni-completion script of JAVA language for vim 7 and
+above. It includes javacomplete.vim, java_parser.vim, Reflection.java, and
+javacomplete.txt.
+
+1.1 Features *javacomplete-features*
+
+- List members of a class, including (static) fields, (static) methods and ctors.
+- List classes or subpackages of a package.
+- Provide parameters information of a method, list all overload methods.
+- Complete an incomplete word.
+- Provide a complete JAVA parser written in Vim script language.
+- Use the JVM to obtain most information.
+- Use the embedded parser to obtain the class information from source files.
+- Tags generated by ctags can also be used.
+- JSP is supported, Builtin objects such as request, session can be recognized.
+ The classes and jar files in the WEB-INF will be appended automatically to the classpath.
+
+1.2 Requirements *javacomplete-requirements*
+
+It works on all the platforms wherever
+- Vim version 7.0 and above,
+- JDK version 1.1 and above,
+exists.
+
+1.3 Download *javacomplete-download*
+
+You can download the lastest version from this url:
+ http://www.vim.org/scripts/script.php?script_id=1785
+
+1.4 Install *javacomplete-install*
+
+1. Unzip javacomplete.zip to a directory of 'runtimepath', e.g.
+$HOME/.vim (unix/linux), $VIM/vimfiles (windows). >
+ > unzip javacomplete.zip -d ~/.vim
+
+< To update Vim help tags, run vim and run command: >
+ :helptags $HOME/.vim/doc
+< or >
+ :helptags $VIM/vimfiles/doc
+
+NOTE: javacomplete.vim, java_parser.vim and Reflection.java should be in one
+autoload directory of 'runtimepath'.
+javacomplete.txt should be in one doc directory of 'runtimepath'.
+
+2. Set 'omnifunc' option. e.g. >
+ :setlocal omnifunc=javacomplete#Complete
+< Or, use autocmd: >
+ :" Only do this part when compiled with support for autocommands.
+ :if has("autocmd")
+ : autocmd Filetype java setlocal omnifunc=javacomplete#Complete
+ :endif
+You can add this command to your .vimrc or _vimrc.
+
+3. Set 'completefunc' option to show parameters information IF YOU LIKE. e.g. >
+ :setlocal completefunc=javacomplete#CompleteParamsInfo
+You can map as follows for better display: >
+ :inoremap <buffer> <C-X><C-U> <C-X><C-U><C-P>
+ :inoremap <buffer> <C-S-Space> <C-X><C-U><C-P>
+
+4. Reflection.java will be automatcally compiled and placed to $HOME when you
+use first time. Assure that Reflection.java is in the same directory with
+javacomplete.vim to be searched in autoload subdirectory of &rtp.
+If no Reflection.class is generated, check that you have the write permission
+in $HOME directory.
+If a previous Reflection.java is not compatible with the new version
+javacomplete.vim, please compile Reflection.java manually.
+
+==============================================================================
+2. Usage *javacomplete-usage*
+
+You can use it like other omni-completion script. Many samples of input context
+are gived in the following section.
+
+Make sure a JVM launcher (default 'java') can be searched in the PATH enviroment
+variable, otherwise Use javacomplete#SetJVMLauncher() to specify one. See option
+`javacomplete-launcher`.
+
+See FAQ in time if some problem occurs. When meeting other problems not
+described in FAQ, you can contact with the auther by the following e-mail:
+fangread@yahoo.com.cn
+
+2.1 Input contexts |javacomplete-contexts|
+It recognize nearly all kinds of Primary Expressions (see langspec-3.0)
+except for "Primary.new Indentifier". Casting conversion is also supported.
+
+Samples of input contexts are as following: (Note that '|' indicates cursor)
+ (1). after '.', list members of a class or a package
+ - package.| subpackages and classes of a package
+ - Type.| static members of the 'Type' class and "class"
+ - var.| or field.| members of a variable or a field
+ - method().| members of result of method()
+ - this.| members of the current class
+ - ClassName.this.| members of the qualified class
+ - super.| members of the super class
+ - array.| members of an array object
+ - array[i].| array access, return members of the element of array
+ - "String".| String literal, return members of java.lang.String
+ - int.| or void.| primitive type or pseudo-type, return "class"
+ - int[].| array type, return members of a array type and "class"
+ - java.lang.String[].|
+ - new int[].| members of the new array instance
+ - new java.lang.String[i=1][].|
+ - new Type().| members of the new class instance
+ - Type.class.| class literal, return members of java.lang.Class
+ - void.class.| or int.class.|
+ - ((Type)var).| cast var as Type, return members of Type.
+ - (var.method()).| same with "var.|"
+ - (new Class()).| same with "new Class().|"
+
+ (2). after '(', list matching methods with parameters information.
+ - method(|) methods matched
+ - var.method(|) methods matched
+ - new ClassName(|) constructors matched
+ - this(|) constructors of current class matched
+ - super(|) constructors of super class matched
+ Any place between '(' and ')' will be supported soon.
+ Help information of javadoc is not supported yet.
+
+ (3). after an incomplete word, list all the matched beginning with it.
+ - var.ab| subset of members of var beginning with `ab`
+ - ab| list of all maybes
+
+ (4). import statement
+ - " import java.util.|"
+ - " import java.ut|"
+ - " import ja|"
+ - " import java.lang.Character.|" e.g. "Subset"
+ - " import static java.lang.Math.|" e.g. "PI, abs"
+
+ (5). package declaration
+ - " package com.|"
+
+ The above are in simple expression.
+ (6). after compound expression:
+ - PrimaryExpr.var.|
+ - PrimaryExpr.method().|
+ - PrimaryExpr.method(|)
+ - PrimaryExpr.var.ab|
+ e.g.
+ - "java.lang . System.in .|"
+ - "java.lang.System.getenv().|"
+ - "int.class.toString().|"
+ - "list.toArray().|"
+ - "new ZipFile(path).|"
+ - "new ZipFile(path).entries().|"
+
+ (7). Nested expression:
+ - "System.out.println( str.| )"
+ - "System.out.println(str.charAt(| )"
+ - "for (int i = 0; i < str.|; i++)"
+ - "for ( Object o : a.getCollect| )"
+
+
+2.2 Kind letter *javacomplete-kindletter*
+
+A single letter indicates the kind of compeltion item. These kinds are:
+ + ctor
+ v local variable or parameter
+ f nonstatic field
+ F static field
+ m nonstatic method
+ M static method
+ P package
+ C class type
+ I interface type
+
+2.3 Options *javacomplete-options*
+
+1. Set java compiler (default 'javac') using the following function:
+ javacomplete#SetCompiler('javac') *javacomplete-compiler*
+
+2. Set java launcher (default 'java') using the following function:
+ javacomplete#SetJVMLauncher('java') *javacomplete-launcher*
+
+3. Set classpath using the following function: >
+ javacomplete#AddClassPath('jarfile_or_classes_path')
+ javacomplete#DelClassPath('jarfile_or_classes_path')
+ javacomplete#SetClassPath('semicolon_separated_string')
+<
+ Another two variables will be used if they are existing:
+ |g:java_classpath| global classpath
+ |b:classpath| associated with current buffer
+ In one sense, s:classpath is like a classpath option for a PROJECT.
+ If some of them are body set, the priority of these variables is:
+ first, b:classpath first,
+ second, s:classpath
+ third, g:java_classpath
+ last, $CLASSPATH
+
+4. Set sourcepath using the following function: >
+ javacomplete#AddSourcePath('sources_file_path')
+ javacomplete#DelSourcePath('sources_file_path')
+ javacomplete#SetSourcePath('sources_file_path')
+
+5. Set option for using JDK1.1 if you meet the problem described in FAQ 3: >
+ javacomplete#UseJDK11()
+
+6. Set methods to search declaration: >
+ " 1 - by builtin searchdecl(), quickest but inaccurate in many cases.
+ " 2 - by special Searchdecl(), work NOT WELL YET.
+ " 4 - by java_parser, slowest but accurate in most cases. Not for JSP.
+ javacomplete#SetSearchdeclMethod()
+
+2.4 Commands *javacomplete-commands*
+
+==============================================================================
+3. Java parser in Vim *javacomplete-parser*
+
+3.1 Abstract Syntax Tree *javacomplete-ast*
+
+3.2 Global Constants *javacomplete-constants*
+
+3.3 Parsing Functions *javacomplete-functions*
+
+3.4 Sample Codes *javacomplete-sample*
+This parser can be a good candidate for anyone who needs a java parser to get
+a abstract syntax tree for many use. The following are sample codes: >
+
+ " NOTE: The script contains a single parser instance. You cannot create
+ " another parser! The only way to parse another JAVA code is reset the
+ " parser by calling java_parser#InitParser().
+
+ " 1. Initialize the parser
+ " for a code snippet,
+ call java_parser#InitParser(['for (int i = 0; i < N; i++) {', '', '}'])
+ " or for the current buffer,
+ call java_parser#InitParser(getline('^', '$'))
+ " or for a whole source file
+ call java_parser#InitParser(readfile('java/util/Arrays.java'))
+
+ " 2. Get the result tree
+ call java_parser#compilationUnit()
+ " or others according to the input code
+ call java_parser#expression()
+ call java_parser#block()
+ call java_parser#statement()
+
+ " 3. Use the tree as you like
+
+ " 4. The default scan strategy is scanning only sklenton.
+ " You can change it by set the option 'scanStrategy'.
+ " The values for 'scanStrategy' option are:
+ " 0 - only class members when parse full file;
+ " 1 - keep statement as a whole string;
+ " 2 - all
+ call java_parser#InitParser(getline('^', '$'), {'scanStrategy': 2})
+
+ " 5. I recommend that keeping scanStrategy as default.
+ " If you want to parse a code snippet such as a method body of the whole
+ " file, you can call java_parser#GotoPosition() to go to what you are going
+ " to start parsing.
+ " Then, call java_parser#block(), java_parser#statement() or
+ " java_parser#expression() to parse the smaller snippet.
+ " NOTE: This way will keep the result tree reserved.
+ call java_parser#GotoPosition(def.body.pos)
+ call java_parser#block()
+
+==============================================================================
+4. FAQ *javacomplete-faq*
+
+(1). When you meets the following problem: >
+ omni-completion error: Exception in thread "main"
+ java.lang.NoClassDefFoundError: Reflection
+It is Reflection.class not found in autoload directory or $HOME that cause
+this problem.
+There are several reasons causing this problem:
+ o No compiler. Use javacomplete#SetCompiler() to specify one.
+ o No write permission for $HOME directory.
+
+(2). Reflection.java should be searched in autoload subdirectory of &rtp.
+Reflection.class should be searched in $HOME or autoload subdirectory of &rtp.
+If not found, javacomplete try to compile it and place the generated class
+file in $HOME.
+
+(3). A error when using JDK1.1:
+ Unable to initialize threads: cannot find class java/lang/Thread
+When I tested JDK1.1.8 on Windows XP, I found -classpath options cause it.
+There are two way to avoid it is:
+ o Add the runtime classes to classpath, like
+ "${JDK118}\classes;${JDK118}\lib\classes.zip;${JDK118}\lib\classes.jar;"
+ o Add Reflection.class and others to the CLASSPATH enviroment variable.
+ And call javacomplete#UseJDK11() to set option.
+
+==============================================================================
+5. Limitations *javacomplete-limitations*
+
+The embedded parser works a bit slower than expected.
+
+==============================================================================
+6. History
+
+6.1 javacomplete *javacomplete-history*
+
+v0.77.1.2
+ 2011-01-30 Fixed to adapt globpath() (vim < 7.2). Patched by Sam Lidder.
+
+v0.77.1.1
+ 2010-11-12 Fixed to ignore the 'suffixes' and 'wildignore' options which
+ make Reflection.class can not be found.
+
+v0.77.1
+ 2007-09-19 Supported showing method parameters information in any place
+ between parenthesises.
+
+v0.77
+ 2007-09-19 bug fix
+ 2007-09-18 Added GetCurrentFileKey() avoid empty key of s:files for current buffer.
+ 2007-09-16 Use a new strategy for searching inherited members.
+ 2007-09-11
+ - Supported new contexts "jav|", "var|", just after an incomplete word.
+ - Supported new context "abs(|)", a imported static method.
+ 2007-09-10
+ - Improved FoundClassDeclaration()
+ - Fixed bug calling cursor(0, 0)
+ 2007-09-09 Rewrote DoGetClassInfo(), GetFQN() and IsFQN()¡£
+ 2007-09-08 Fixed a bug when merging superclass's members
+ 2007-09-05 -- 07
+ - Improved s:MergeLines() and s:ExtractCleanExpr().
+ - Rewrote CompleteAfterDot(). Added ParseExpr(). Removed s:GetNextSubexprType()
+ - Supported accessible static imported members.
+ - Supported accessible inherited members.
+
+ 2007-09-04 Used b:changedtick and getftime() to check buffer (or other file) for changing.
+ 2007-09-01 Supported not-file-name toplevel or static member class in source files.
+
+v0.76.8
+ 2007-08-30
+ - Created the s:TreeVisitor to search type or symbol names.
+ - Supported local and anonymous class.
+
+ 2007-08-29
+ - Supported appending automatically classpath under WEB-INF for jsp files.
+
+v0.76.7
+ 2007-08-28
+ - Fixed case of "new java.util.zip.ZipFile().|"
+ - Improved process of type arguments and method parameters. JAVA5+
+ - Reorganize codes in javacomplete#Complete()
+ - Added CONTEXT_NEED_TYPE, removed CONTEXT_INCOMPLETE_WORD
+
+ 2007-08-24 Add Context types for type declaration: CONTEXT_NEED_TYPE
+
+v0.76.6
+ 2007-08-23 Improved GetStatement() and related. Bug fixed.
+
+v0.76.5
+ 2007-08-21
+ - Fixed bug: "foo().|", "getFoo().foo().|",
+ "for (Enumeration entries = ; entries.|; )".
+ - Supported input contexts: "((Object)o).|", "((Object)o).getClass().|",
+ "new ZipFile(path).|", "(new String().)|".
+
+v0.76.4
+ 2007-08-17
+ - Improved input contexts: "int.class.toString().|", "list.toArray().|".
+ - Fixed recognizing "this(|)", "method1(|)"
+ - Added the 'kind' letter to distinguish between classes and packages.
+ 2007-08-14
+ - Support accessible nested classes.
+ - Support import static members and import accessible nested classes.
+ 2007-08-11
+ - Fixed a bug when Reflection.java is in the path which contains space.
+ - Improved process of this and super in JSP.
+ - Fixed an severe bug parsing current jsp file.
+
+v0.76.3
+ 2007-08-10
+ - Add an option 'searchdecl' set by javacomplete#SetSearchdeclMethod().
+ - Make an improvement for jsp file.
+ - Clear cache when set options affecting classpath.
+ - Improved DoGetPackageList() and s:GenerateImports().
+ - Replace codes searching list of string with index().
+
+v0.76.2
+ 2007-08-08
+ - Fix failing to list members of nested class.
+ - Combine members of local packages and loadable packages.
+ - Add quick recognition of package or import.
+ 2007-08-06 Add inherited fields and methods to local class.
+
+v0.76.1
+ 2007-08-04
+ - Fix using a: in javacomplete#SetClassPath()
+ - Fix a bug in javacomplete#GetClassPath()
+
+v0.76 2007-08-04
+ 2007-08-04
+ - Fix a infinite loop bug in s:GetMatchedIndexEx()
+ - Fix that array type not recognised in compound expression.
+ - Add a option for JDK1.1. See FAQ 3.
+ 2007-08-03
+ - Improve for 'this' or 'super'.
+ - Support searching toplevel class in sourcepath.
+ - Clean
+ 2007-08-02
+ - Improve the process of checking a class in one of packages.
+ 2007-08-01
+ - Add Searchdecl() using java_parser.vim to provide quick information.
+ - Supports input context: "StringLiteral".|, "int.|", "void.|"
+ 2007-07-28
+ - Automatcally compile Reflection.java and place it to $HOME.
+ - Add option 'javacompiler', default 'javac'
+ - Add option 'java', default 'java'
+
+v0.75 2007-02-13
+ - Add java_parser.vim.
+ - Add b:sourcepath option.
+ - Improve recognition of classes defined in current buffer or in source path.
+ - Support generating class information from tags instead of returning list directly.
+
+v0.74 2007-02-03
+ - Support jre1.2 (and above).
+ - Support input context like "boolean.class.|"
+ - Handle java primitive types like 'int'.
+
+v0.73 2007-02-01
+ - Fix bug that CLASSPATH not used when b:classpath or g:java_classpath not set.
+ - Fix bug that call filter() without making a copy for incomplete.
+ - Improve recognition of declaration of this class
+
+v0.72 2007-01-31 Handle nested expression.
+v0.71 2007-01-28 Add Basic support for class in current folder.
+v0.70 2007-01-27 Complete the reflection part.
+v0.60 2007-01-25 Design TClassInfo, etc.
+v0.50 2007-01-21 Use java and Reflection.class directly.
+
+
+6.2 Parser *java-parser-history*
+
+v0.67
+ 2007-09-11 Append a error string to imported qid when error occurs.
+ 2007-09-10 Improved regexp constants.
+ 2007-09-07 Fixed type2Str(). Removed qualident2Str().
+
+v0.66.1 08-30 Changed classCreatorRest().
+v0.66 08-27 Minor changes
+
+v0.65
+ 2007-08-23
+ - Improved s:scanComment(), s:Strpart(), s:String2Flags().
+ - Improved recognizing methods, ctors, and variable declarators declared in most common form.
+ - Added s:optFinalParameter(), s:methodDeclaratorRest_opt().
+ - Removed s:GetLine() and s:GetCol().
+ - Rewrote binary functions.
+
+v0.64
+ 2007-08-21
+ - Added quick recognizing fields or methods declared in most common form.
+ - Optimized code: s:modeAndEXPR(), formalParameter(), and others.
+
+v0.63
+ 2007-08-10
+ - Removed the unclear s:tokens and s:modifier_keywords.
+ - Add java_parser#GetSnapshot() and java_parser#Restore().
+ 2007-08-09 Fixed a bug when no top level class defined
+
+v0.62 2007-08-08
+ 2007-08-08 Fix values in s:Flags and s:modifiersOpt() and the related.
+ 2007-08-07 Optimize code of scanDoubleQuote() and importDeclaration().
+
+v0.61 2007-08-04
+ 2007-08-01 Fix a bug typetag(). return a:token -> return tolower(a:token)
+ 2007-07-31
+ - Rename all script functions matching "s:Java_\(\i\+\)" to "s:\1".
+ - Change s:EOI = ''
+ - Use get() instead of s:GetOption(). Remove it.
+ - Use repeat() instead of s:PrependChar(). Remove it.
+ - Improve scanChar()
+
+v0.60 2007-07-31 Now it nearly is a complete parser and support Java5,6.
+ And tested correctly on all java files in jdk6 src.zip.
+ 2007-07-19 Support new language features in java 5 and above.
+ 2007-07-25 Add supports for parsing statement, block or expression
+ 2007-07-28 Place it to autoload directory.
+ 2007-07-30 Clean this script.
+
+v0.51 2007-02-13 Optimize several scan function.
+v0.50 2007-02-10 Complete the skeleton.
+
+
+6.3 Reflection.java *javacomplete-reflection*
+
+v0.77
+ 2007-09-14 Improved generating information of all packages in jar files.
+ 2007-09-06
+ - Improved getting paths of all system jar files for different JDKs
+ in different platforms.
+ 2007-08-14 Major improvement. Support nontoplevel classes.
+
+v0.76.3
+ 2007-08-09 Redefined '-P' option for returning all packages and subpackages info in one time.
+
+v0.76.2
+ 2007-08-06 Return a modifier value as a string because it more than 32bit.
+
+v0.76
+ 2007-08-04 Support checking and reading package members for '-E'.
+ 2007-08-02
+ - Add an option '-E'.
+ - Use ZipFile and ZipEntry instead of JarFile and JarEntry,
+ so that it can be compiled by and run on JDK1.1 and above.
+v0.7 2007-02-17
+
+==============================================================================
+7. Todo *javacomplete-todo*
+
+- Improve performance of the embedded parser. Incremental parser.
+- Add quick information using balloonexpr, ballooneval, balloondelay.
+- Add javadoc
+- Give a hint for class name conflict in different packages.
+- Support parameter information for template
+
+==============================================================================
+8. Thanks *javacomplete-thanks*
+ * Bram Moolenaar and all Vim contributors for Vim
+ * The insenvim project
+ * The javac and gjc sources
+ * All of you for using this script :)
+
+ * For help, documentation, bug report :
+ Martin Stubenschrott author of IComplete
+ Vissale NEANG author of OmniCppComplete
+ David Fishburn author of SQLComplete and others
+ Nico Weber testing on the Mac
+ Thomas Link testing on cygwin+bash
+ Zhixing Yu
+ * For the bug of 'wildignore' options
+ Rodrigo Rosenfeld Rosas
+ Alexandru Mo?oi
+
+FeedBack:
+Any problem, bug or suggest are welcome to send to fangread@yahoo.com.cn
+
+==============================================================================
+ vim:tw=78:ts=8:ft=help:norl:
diff --git a/base/vim/bundle/syntastic b/base/vim/bundle/syntastic
new file mode 160000
+Subproject 008ac98299bb4fcd3fc4dedcd5f99dc04ff1509
diff --git a/base/vim/doc/.gitignore b/base/vim/doc/.gitignore
new file mode 100644
index 0000000..6e92f57
--- /dev/null
+++ b/base/vim/doc/.gitignore
@@ -0,0 +1 @@
+tags
diff --git a/base/vim/doc/git-vim.txt b/base/vim/doc/git-vim.txt
new file mode 100644
index 0000000..5fba807
--- /dev/null
+++ b/base/vim/doc/git-vim.txt
@@ -0,0 +1,95 @@
+*git-vim.txt* Git Bindings for Vim
+
+==============================================================================
+CONTENTS *git-vim-contents*
+
+ 1. Introduction.............................|git-vim|
+ 2. Commands.................................|git-vim-commands|
+ 3. Keymaps..................................|git-vim-keymaps|
+ 4. License..................................|git-vim-license|
+
+==============================================================================
+1. Introduction *git-vim*
+
+Git-vim provides:
+
+* Plugin files for calling git functions from inside Vim
+* Syntax files for git displays
+
+==============================================================================
+2. Commands *git-vim-commands*
+
+:GitAdd <file>
+ git-add <file> or current file if not specified.
+
+:GitCommit <args>
+ git-commit.
+
+:GitStatus
+ Show git-status of current file or repository.
+
+:GitLog
+ Show git-log of current file or repository.
+
+:GitCheckout <args>
+ git-checkout. Completes git commits.
+
+:GitDiff <args>
+ git-diff. Completes git commits.
+
+:GitPull <args>
+ git-pull.
+
+:GitPullRebase
+ git-pull —rebase.
+
+:GitPush <args>
+ git-push. Defaults to +git push origin <current-branch>+.
+
+:GitCatFile <args>
+ git-cat-file.
+
+:Git <args>
+ Does any git command.
+
+:GitVimDiffMerge
+ Experimental. Call this command on unmerged file to enter vimdiff mode.
+
+:GitVimDiffMergeDone
+ Call this command after merging.
+
+==============================================================================
+3. Keymaps *git-vim-keymaps*
+
+<Leader>gd
+ :GitDiff
+
+<Leader>gD
+ :GitDiff —cached
+
+<Leader>gs
+ :GitStatus
+
+<Leader>gl
+ :GitLog
+
+<Leader>ga
+ :GitAdd
+
+<Leader>gA
+ :GitAdd <cfile>
+
+<Leader>gc
+ :GitCommit
+
+In the git-status buffer:
+
+<Enter>
+ :GitAdd <cfile>
+
+==============================================================================
+4. License *git-vim-license*
+
+The MIT License
+
+ vim:tw=78:ts=8:ft=help
diff --git a/base/vim/ftdetect/markdown.vim b/base/vim/ftdetect/markdown.vim
new file mode 100644
index 0000000..1c5a36a
--- /dev/null
+++ b/base/vim/ftdetect/markdown.vim
@@ -0,0 +1,6 @@
+autocmd BufNewFile,BufRead *.markdown,*.md,*.mdown,*.mkd,*.mkdn
+ \ if &ft =~# '^\%(conf\|modula2\)$' |
+ \ set ft=markdown |
+ \ else |
+ \ setf markdown |
+ \ endif
diff --git a/base/vim/ftdetect/mediawiki.vim b/base/vim/ftdetect/mediawiki.vim
new file mode 100644
index 0000000..2f1bc69
--- /dev/null
+++ b/base/vim/ftdetect/mediawiki.vim
@@ -0,0 +1,6 @@
+if has("autocmd")
+ au BufRead,BufNewFile *.wiki set filetype=mediawiki
+ au BufRead,BufNewFile *.wikipedia.org* set filetype=mediawiki
+ au BufRead,BufNewFile *.wikibooks.org* set filetype=mediawiki
+ au BufRead,BufNewFile *.wikimedia.org* set filetype=mediawiki
+endif
diff --git a/base/vim/ftplugin/html.vim b/base/vim/ftplugin/html.vim
new file mode 100644
index 0000000..4317610
--- /dev/null
+++ b/base/vim/ftplugin/html.vim
@@ -0,0 +1 @@
+setl ts=2 sw=2 sts=2
diff --git a/base/vim/ftplugin/javascript.vim b/base/vim/ftplugin/javascript.vim
new file mode 100644
index 0000000..4317610
--- /dev/null
+++ b/base/vim/ftplugin/javascript.vim
@@ -0,0 +1 @@
+setl ts=2 sw=2 sts=2
diff --git a/base/vim/ftplugin/mail.vim b/base/vim/ftplugin/mail.vim
new file mode 100644
index 0000000..0495ccf
--- /dev/null
+++ b/base/vim/ftplugin/mail.vim
@@ -0,0 +1 @@
+setl fo+=aw
diff --git a/base/vim/ftplugin/markdown.vim b/base/vim/ftplugin/markdown.vim
new file mode 100644
index 0000000..022da06
--- /dev/null
+++ b/base/vim/ftplugin/markdown.vim
@@ -0,0 +1,21 @@
+" Vim filetype plugin
+" Language: Markdown
+" Maintainer: Tim Pope <vimNOSPAM@tpope.org>
+
+if exists("b:did_ftplugin")
+ finish
+endif
+
+runtime! ftplugin/html.vim ftplugin/html_*.vim ftplugin/html/*.vim
+
+setlocal comments=fb:*,fb:-,fb:+,n:> commentstring=>\ %s
+setlocal formatoptions+=tcqln formatoptions-=r formatoptions-=o
+setlocal formatlistpat=^\\s*\\d\\+\\.\\s\\+\\\|^[-*+]\\s\\+
+
+if exists('b:undo_ftplugin')
+ let b:undo_ftplugin .= "|setl cms< com< fo< flp<"
+else
+ let b:undo_ftplugin = "setl cms< com< fo< flp<"
+endif
+
+" vim:set sw=2:
diff --git a/base/vim/ftplugin/php.vim b/base/vim/ftplugin/php.vim
new file mode 100644
index 0000000..76b6d58
--- /dev/null
+++ b/base/vim/ftplugin/php.vim
@@ -0,0 +1,2 @@
+setl ts=4 sw=4 sts=4
+
diff --git a/base/vim/ftplugin/xml.vim b/base/vim/ftplugin/xml.vim
new file mode 100644
index 0000000..4317610
--- /dev/null
+++ b/base/vim/ftplugin/xml.vim
@@ -0,0 +1 @@
+setl ts=2 sw=2 sts=2
diff --git a/base/vim/plugin/git.vim b/base/vim/plugin/git.vim
new file mode 100644
index 0000000..a88329b
--- /dev/null
+++ b/base/vim/plugin/git.vim
@@ -0,0 +1,372 @@
+" Author: motemen <motemen@gmail.com>
+" License: The MIT License
+" URL: http://github.com/motemen/git-vim/
+
+if !exists('g:git_command_edit')
+ let g:git_command_edit = 'new'
+endif
+
+if !exists('g:git_bufhidden')
+ let g:git_bufhidden = ''
+endif
+
+if !exists('g:git_bin')
+ let g:git_bin = 'git'
+endif
+
+if !exists('g:git_author_highlight')
+ let g:git_author_highlight = { }
+endif
+
+if !exists('g:git_highlight_blame')
+ let g:git_highlight_blame = 0
+endif
+
+if !exists('g:git_no_map_default') || !g:git_no_map_default
+ nnoremap <Leader>gd :GitDiff<Enter>
+ nnoremap <Leader>gD :GitDiff --cached<Enter>
+ nnoremap <Leader>gs :GitStatus<Enter>
+ nnoremap <Leader>gl :GitLog<Enter>
+ nnoremap <Leader>ga :GitAdd<Enter>
+ nnoremap <Leader>gA :GitAdd <cfile><Enter>
+ nnoremap <Leader>gc :GitCommit<Enter>
+ nnoremap <Leader>gp :GitPullRebase<Enter>
+ nnoremap <Leader>gb :GitBlame<Enter>
+endif
+
+" Ensure b:git_dir exists.
+function! s:GetGitDir()
+ if !exists('b:git_dir')
+ let b:git_dir = s:SystemGit('rev-parse --git-dir')
+ if !v:shell_error
+ let b:git_dir = fnamemodify(split(b:git_dir, "\n")[0], ':p') . '/'
+ endif
+ endif
+ return b:git_dir
+endfunction
+
+" Returns current git branch.
+" Call inside 'statusline' or 'titlestring'.
+function! GitBranch()
+ let git_dir = <SID>GetGitDir()
+
+ if strlen(git_dir) && filereadable(git_dir . '/HEAD')
+ let lines = readfile(git_dir . '/HEAD')
+ if !len(lines)
+ return ''
+ else
+ return matchstr(lines[0], 'refs/heads/\zs.\+$')
+ endif
+ else
+ return ''
+ endif
+endfunction
+
+" List all git local branches.
+function! ListGitBranches(arg_lead, cmd_line, cursor_pos)
+ let branches = split(s:SystemGit('branch'), '\n')
+ if v:shell_error
+ return []
+ endif
+
+ return map(branches, 'matchstr(v:val, ''^[* ] \zs.*'')')
+endfunction
+
+" List all git commits.
+function! ListGitCommits(arg_lead, cmd_line, cursor_pos)
+ let commits = split(s:SystemGit('log --pretty=format:%h'))
+ if v:shell_error
+ return []
+ endif
+
+ let commits = ['HEAD'] + ListGitBranches(a:arg_lead, a:cmd_line, a:cursor_pos) + commits
+
+ if a:cmd_line =~ '^GitDiff'
+ " GitDiff accepts <commit>..<commit>
+ if a:arg_lead =~ '\.\.'
+ let pre = matchstr(a:arg_lead, '.*\.\.\ze')
+ let commits = map(commits, 'pre . v:val')
+ endif
+ endif
+
+ return filter(commits, 'match(v:val, ''\v'' . a:arg_lead) == 0')
+endfunction
+
+" Show diff.
+function! GitDiff(args)
+ let git_output = s:SystemGit('diff ' . a:args . ' -- ' . s:Expand('%'))
+ if !strlen(git_output)
+ echo "No output from git command"
+ return
+ endif
+
+ call <SID>OpenGitBuffer(git_output)
+ setlocal filetype=git-diff
+endfunction
+
+" Show Status.
+function! GitStatus()
+ let git_output = s:SystemGit('status')
+ call <SID>OpenGitBuffer(git_output)
+ setlocal filetype=git-status
+ nnoremap <buffer> <Enter> :GitAdd <cfile><Enter>:call <SID>RefreshGitStatus()<Enter>
+ nnoremap <buffer> - :silent !git reset HEAD -- =expand('<cfile>')<Enter><Enter>:call <SID>RefreshGitStatus()<Enter>
+endfunction
+
+function! s:RefreshGitStatus()
+ let pos_save = getpos('.')
+ GitStatus
+ call setpos('.', pos_save)
+endfunction
+
+" Show Log.
+function! GitLog(args)
+ let git_output = s:SystemGit('log ' . a:args . ' -- ' . s:Expand('%'))
+ call <SID>OpenGitBuffer(git_output)
+ setlocal filetype=git-log
+endfunction
+
+" Add file to index.
+function! GitAdd(expr)
+ let file = s:Expand(strlen(a:expr) ? a:expr : '%')
+
+ call GitDoCommand('add ' . file)
+endfunction
+
+" Commit.
+function! GitCommit(args)
+ let git_dir = <SID>GetGitDir()
+
+ let args = a:args
+
+ if args !~ '\v\k@<!(-a|--all)>' && s:SystemGit('diff --cached --stat') =~ '^\(\s\|\n\)*$'
+ let args .= ' -a'
+ endif
+
+ " Create COMMIT_EDITMSG file
+ let editor_save = $EDITOR
+ let $EDITOR = ''
+ let git_output = s:SystemGit('commit ' . args)
+ let $EDITOR = editor_save
+
+ let cur_dir = getcwd()
+ execute printf('%s %sCOMMIT_EDITMSG', g:git_command_edit, git_dir)
+ execute printf("lcd %s", cur_dir)
+
+ setlocal filetype=git-status bufhidden=wipe
+ augroup GitCommit
+ autocmd BufWritePre <buffer> g/^#\|^\s*$/d | setlocal fileencoding=utf-8
+ execute printf("autocmd BufEnter <buffer> lcd %s", cur_dir)
+ execute printf("autocmd BufWritePost <buffer> call GitDoCommand('commit %s -F ' . expand('%%')) | autocmd! GitCommit * <buffer>", args)
+ augroup END
+endfunction
+
+" Checkout.
+function! GitCheckout(args)
+ call GitDoCommand('checkout ' . a:args)
+endfunction
+
+" Push.
+function! GitPush(args)
+" call GitDoCommand('push ' . a:args)
+ " Wanna see progress...
+ let args = a:args
+ if args =~ '^\s*$'
+ let args = 'origin ' . GitBranch()
+ endif
+ execute '!' g:git_bin 'push' args
+endfunction
+
+" Pull.
+function! GitPull(args)
+" call GitDoCommand('pull ' . a:args)
+ " Wanna see progress...
+ execute '!' g:git_bin 'pull' a:args
+endfunction
+
+" Show commit, tree, blobs.
+function! GitCatFile(file)
+ let file = s:Expand(a:file)
+ let git_output = s:SystemGit('cat-file -p ' . file)
+ if !strlen(git_output)
+ echo "No output from git command"
+ return
+ endif
+
+ call <SID>OpenGitBuffer(git_output)
+endfunction
+
+" Show revision and author for each line.
+function! GitBlame(...)
+ let git_output = s:SystemGit('blame -- ' . expand('%'))
+ if !strlen(git_output)
+ echo "No output from git command"
+ return
+ endif
+
+ let l:git_blame_width = 20
+ if strlen(a:1)
+ let l:git_blame_width = a:1
+ elseif exists('g:git_blame_width') && g:git_blame_width
+ let l:git_blame_width = g:git_blame_width
+ endif
+
+ setlocal noscrollbind
+
+ " :/
+ let git_command_edit_save = g:git_command_edit
+ let g:git_command_edit = 'leftabove vnew'
+ call <SID>OpenGitBuffer(git_output)
+ let g:git_command_edit = git_command_edit_save
+
+ setlocal modifiable
+ silent %s/\d\d\d\d\zs \+\d\+).*//
+ exe 'vertical resize ' . git_blame_width
+ setlocal nomodifiable
+ setlocal nowrap scrollbind
+
+ if g:git_highlight_blame
+ call s:DoHighlightGitBlame()
+ endif
+
+ wincmd p
+ setlocal nowrap scrollbind
+
+ syncbind
+endfunction
+
+" Experimental
+function! s:DoHighlightGitBlame()
+ for l in range(1, line('$'))
+ let line = getline(l)
+ let [commit, author] = matchlist(line, '\(\x\+\) (\(.\{-}\)\s* \d\d\d\d-\d\d-\d\d')[1:2]
+ call s:LoadSyntaxRuleFor({ 'author': author })
+ endfor
+endfunction
+
+function! s:LoadSyntaxRuleFor(params)
+ let author = a:params.author
+ let name = 'gitBlameAuthor_' . substitute(author, '\s', '_', 'g')
+ if (!hlID(name))
+ if has_key(g:git_author_highlight, author)
+ execute 'highlight' name g:git_author_highlight[author]
+ else
+ let [n1, n2] = [0, 1]
+ for c in split(author, '\zs')
+ let n1 = (n1 + char2nr(c)) % 8
+ let n2 = (n2 + char2nr(c) * 3) % 8
+ endfor
+ if n1 == n2
+ let n1 = (n2 + 1) % 8
+ endif
+ execute 'highlight' name printf('ctermfg=%d ctermbg=%d', n1, n2)
+ endif
+ execute 'syntax match' name '"\V\^\x\+ (' . escape(author, '\') . '\.\*"'
+ endif
+endfunction
+
+function! GitDoCommand(args)
+ let git_output = s:SystemGit(a:args)
+ let git_output = substitute(git_output, '\n*$', '', '')
+ if v:shell_error
+ echohl Error
+ echo git_output
+ echohl None
+ else
+ echo git_output
+ endif
+endfunction
+
+function! s:SystemGit(args)
+ " workardound for MacVim, on which shell does not inherit environment
+ " variables
+ if has('mac') && &shell =~ 'sh$'
+ return system('EDITOR="" '. g:git_bin . ' ' . a:args)
+ else
+ return system(g:git_bin . ' ' . a:args)
+ endif
+endfunction
+
+" Show vimdiff for merge. (experimental)
+function! GitVimDiffMerge()
+ let file = s:Expand('%')
+ let filetype = &filetype
+ let t:git_vimdiff_original_bufnr = bufnr('%')
+ let t:git_vimdiff_buffers = []
+
+ topleft new
+ setlocal buftype=nofile
+ file `=':2:' . file`
+ call add(t:git_vimdiff_buffers, bufnr('%'))
+ execute 'silent read!git show :2:' . file
+ 0d
+ diffthis
+ let &filetype = filetype
+
+ rightbelow vnew
+ setlocal buftype=nofile
+ file `=':3:' . file`
+ call add(t:git_vimdiff_buffers, bufnr('%'))
+ execute 'silent read!git show :3:' . file
+ 0d
+ diffthis
+ let &filetype = filetype
+endfunction
+
+function! GitVimDiffMergeDone()
+ if exists('t:git_vimdiff_original_bufnr') && exists('t:git_vimdiff_buffers')
+ if getbufline(t:git_vimdiff_buffers[0], 1, '$') == getbufline(t:git_vimdiff_buffers[1], 1, '$')
+ execute 'sbuffer ' . t:git_vimdiff_original_bufnr
+ 0put=getbufline(t:git_vimdiff_buffers[0], 1, '$')
+ normal! jdG
+ update
+ execute 'bdelete ' . t:git_vimdiff_buffers[0]
+ execute 'bdelete ' . t:git_vimdiff_buffers[1]
+ close
+ else
+ echohl Error
+ echo 'There still remains conflict'
+ echohl None
+ endif
+ endif
+endfunction
+
+function! s:OpenGitBuffer(content)
+ if exists('b:is_git_msg_buffer') && b:is_git_msg_buffer
+ enew!
+ else
+ execute g:git_command_edit
+ endif
+
+ setlocal buftype=nofile readonly modifiable
+ execute 'setlocal bufhidden=' . g:git_bufhidden
+
+ silent put=a:content
+ keepjumps 0d
+ setlocal nomodifiable
+
+ let b:is_git_msg_buffer = 1
+endfunction
+
+function! s:Expand(expr)
+ if has('win32')
+ return substitute(expand(a:expr), '\', '/', 'g')
+ else
+ return expand(a:expr)
+ endif
+endfunction
+
+command! -nargs=1 -complete=customlist,ListGitCommits GitCheckout call GitCheckout(<q-args>)
+command! -nargs=* -complete=customlist,ListGitCommits GitDiff call GitDiff(<q-args>)
+command! GitStatus call GitStatus()
+command! -nargs=? GitAdd call GitAdd(<q-args>)
+command! -nargs=* GitLog call GitLog(<q-args>)
+command! -nargs=* GitCommit call GitCommit(<q-args>)
+command! -nargs=1 GitCatFile call GitCatFile(<q-args>)
+command! -nargs=? GitBlame call GitBlame(<q-args>)
+command! -nargs=+ Git call GitDoCommand(<q-args>)
+command! GitVimDiffMerge call GitVimDiffMerge()
+command! GitVimDiffMergeDone call GitVimDiffMergeDone()
+command! -nargs=* GitPull call GitPull(<q-args>)
+command! GitPullRebase call GitPull('--rebase')
+command! -nargs=* GitPush call GitPush(<q-args>)
diff --git a/base/vim/plugin/gnupg.vim b/base/vim/plugin/gnupg.vim
new file mode 100644
index 0000000..15bd931
--- /dev/null
+++ b/base/vim/plugin/gnupg.vim
@@ -0,0 +1,1457 @@
+" Name: gnupg.vim
+" Last Change: 2016 Apr 24
+" Maintainer: James McCoy <jamessan@jamessan.com>
+" Original Author: Markus Braun <markus.braun@krawel.de>
+" Summary: Vim plugin for transparent editing of gpg encrypted files.
+" License: This program is free software; you can redistribute it and/or
+" modify it under the terms of the GNU General Public License
+" as published by the Free Software Foundation; either version
+" 2 of the License, or (at your option) any later version.
+" See http://www.gnu.org/copyleft/gpl-2.0.txt
+"
+" Section: Documentation {{{1
+"
+" Description: {{{2
+"
+" This script implements transparent editing of gpg encrypted files. The
+" filename must have a ".gpg", ".pgp" or ".asc" suffix. When opening such
+" a file the content is decrypted, when opening a new file the script will
+" ask for the recipients of the encrypted file. The file content will be
+" encrypted to all recipients before it is written. The script turns off
+" viminfo, swapfile, and undofile to increase security.
+"
+" Installation: {{{2
+"
+" Copy the gnupg.vim file to the $HOME/.vim/plugin directory.
+" Refer to ':help add-plugin', ':help add-global-plugin' and ':help
+" runtimepath' for more details about Vim plugins.
+"
+" From "man 1 gpg-agent":
+"
+" ...
+" You should always add the following lines to your .bashrc or whatever
+" initialization file is used for all shell invocations:
+"
+" GPG_TTY=`tty`
+" export GPG_TTY
+"
+" It is important that this environment variable always reflects the outâ€
+" put of the tty command. For W32 systems this option is not required.
+" ...
+"
+" Most distributions provide software to ease handling of gpg and gpg-agent.
+" Examples are keychain or seahorse.
+"
+" If there are specific actions that should take place when editing a
+" GnuPG-managed buffer, an autocmd for the User event and GnuPG pattern can
+" be defined. For example, the following will set 'textwidth' to 72 for all
+" GnuPG-encrypted buffers:
+"
+" autocmd User GnuPG setl textwidth=72
+"
+" This will be triggered before any BufRead or BufNewFile autocmds, and
+" therefore will not take precedence over settings specific to any filetype
+" that may get set.
+"
+" Commands: {{{2
+"
+" :GPGEditRecipients
+" Opens a scratch buffer to change the list of recipients. Recipients that
+" are unknown (not in your public key) are highlighted and have
+" a prepended "!". Closing the buffer makes the changes permanent.
+"
+" :GPGViewRecipients
+" Prints the list of recipients.
+"
+" :GPGEditOptions
+" Opens a scratch buffer to change the options for encryption (symmetric,
+" asymmetric, signing). Closing the buffer makes the changes permanent.
+" WARNING: There is no check of the entered options, so you need to know
+" what you are doing.
+"
+" :GPGViewOptions
+" Prints the list of options.
+"
+" Variables: {{{2
+"
+" g:GPGExecutable
+" If set used as gpg executable. If unset, defaults to
+" "gpg --trust-model always" if "gpg" is available, falling back to
+" "gpg2 --trust-model always" if not.
+"
+" g:GPGUseAgent
+" If set to 0 a possible available gpg-agent won't be used. Defaults to 1.
+"
+" g:GPGPreferSymmetric
+" If set to 1 symmetric encryption is preferred for new files. Defaults to 0.
+"
+" g:GPGPreferArmor
+" If set to 1 armored data is preferred for new files. Defaults to 0
+" unless a "*.asc" file is being edited.
+"
+" g:GPGPreferSign
+" If set to 1 signed data is preferred for new files. Defaults to 0.
+"
+" g:GPGDefaultRecipients
+" If set, these recipients are used as defaults when no other recipient is
+" defined. This variable is a Vim list. Default is unset.
+"
+" g:GPGPossibleRecipients
+" If set, these contents are loaded into the recipients dialog. This
+" allows to add commented lines with possible recipients to the list,
+" which can be uncommented to select the actual recipients. Default is
+" unset. Example:
+"
+" let g:GPGPossibleRecipients=[
+" \"Example User <example@example.com>",
+" \"Other User <otherexample@example.com>"
+" \]
+"
+"
+" g:GPGUsePipes
+" If set to 1, use pipes instead of temporary files when interacting with
+" gnupg. When set to 1, this can cause terminal-based gpg agents to not
+" display correctly when prompting for passwords. Defaults to 0.
+"
+" g:GPGHomedir
+" If set, specifies the directory that will be used for GPG's homedir.
+" This corresponds to gpg's --homedir option. This variable is a Vim
+" string. Default is unset.
+"
+" g:GPGFilePattern
+" If set, overrides the default set of file patterns that determine
+" whether this plugin will be activated. Defaults to
+" '*.\(gpg\|asc\|pgp\)'.
+"
+" Known Issues: {{{2
+"
+" In some cases gvim can't decrypt files
+
+" This is caused by the fact that a running gvim has no TTY and thus gpg is
+" not able to ask for the passphrase by itself. This is a problem for Windows
+" and Linux versions of gvim and could not be solved unless a "terminal
+" emulation" is implemented for gvim. To circumvent this you have to use any
+" combination of gpg-agent and a graphical pinentry program:
+"
+" - gpg-agent only:
+" you need to provide the passphrase for the needed key to gpg-agent
+" in a terminal before you open files with gvim which require this key.
+"
+" - pinentry only:
+" you will get a popup window every time you open a file that needs to
+" be decrypted.
+"
+" - gpgagent and pinentry:
+" you will get a popup window the first time you open a file that
+" needs to be decrypted.
+"
+" If you're using Vim <7.4.959, after the plugin runs any external command,
+" Vim will no longer be able to yank to/paste from the X clipboard or
+" primary selections. This is caused by a workaround for a different bug
+" where Vim no longer recognizes the key codes for keys such as the arrow
+" keys after running GnuPG. See the discussion at
+" https://github.com/jamessan/vim-gnupg/issues/36 for more details.
+"
+" Credits: {{{2
+"
+" - Mathieu Clabaut for inspirations through his vimspell.vim script.
+" - Richard Bronosky for patch to enable ".pgp" suffix.
+" - Erik Remmelzwaal for patch to enable windows support and patient beta
+" testing.
+" - Lars Becker for patch to make gpg2 working.
+" - Thomas Arendsen Hein for patch to convert encoding of gpg output.
+" - Karl-Heinz Ruskowski for patch to fix unknown recipients and trust model
+" and patient beta testing.
+" - Giel van Schijndel for patch to get GPG_TTY dynamically.
+" - Sebastian Luettich for patch to fix issue with symmetric encryption an set
+" recipients.
+" - Tim Swast for patch to generate signed files.
+" - James Vega for patches for better '*.asc' handling, better filename
+" escaping and better handling of multiple keyrings.
+"
+" Section: Plugin header {{{1
+
+" guard against multiple loads {{{2
+if (exists("g:loaded_gnupg") || &cp || exists("#GnuPG"))
+ finish
+endif
+let g:loaded_gnupg = '2.6'
+let s:GPGInitRun = 0
+
+" check for correct vim version {{{2
+if (v:version < 702)
+ echohl ErrorMsg | echo 'plugin gnupg.vim requires Vim version >= 7.2' | echohl None
+ finish
+endif
+
+" Section: Autocmd setup {{{1
+
+if (!exists("g:GPGFilePattern"))
+ let g:GPGFilePattern = '*.\(gpg\|asc\|pgp\)'
+endif
+
+augroup GnuPG
+ autocmd!
+
+ " do the decryption
+ exe "autocmd BufReadCmd " . g:GPGFilePattern . " call s:GPGInit(1) |" .
+ \ " call s:GPGDecrypt(1)"
+ exe "autocmd FileReadCmd " . g:GPGFilePattern . " call s:GPGInit(0) |" .
+ \ " call s:GPGDecrypt(0)"
+
+ " convert all text to encrypted text before writing
+ " We check for GPGCorrespondingTo to avoid triggering on writes in GPG Options/Recipient windows
+ exe "autocmd BufWriteCmd,FileWriteCmd " . g:GPGFilePattern . " if !exists('b:GPGCorrespondingTo') |" .
+ \ " call s:GPGInit(0) |" .
+ \ " call s:GPGEncrypt() |" .
+ \ " endif"
+
+ " cleanup on leaving vim
+ exe "autocmd VimLeave " . g:GPGFilePattern . " call s:GPGCleanup()"
+augroup END
+
+" Section: Constants {{{1
+
+let s:GPGMagicString = "\t \t"
+let s:keyPattern = '\%(0x\)\=[[:xdigit:]]\{8,16}'
+
+" Section: Highlight setup {{{1
+
+highlight default link GPGWarning WarningMsg
+highlight default link GPGError ErrorMsg
+highlight default link GPGHighlightUnknownRecipient ErrorMsg
+
+" Section: Functions {{{1
+
+" Function: s:shellescape(s[, special]) {{{2
+"
+" Calls shellescape(), also taking into account 'shellslash'
+" when on Windows and using $COMSPEC as the shell.
+"
+" Returns: shellescaped string
+"
+function s:shellescape(s, ...)
+ let special = a:0 ? a:1 : 0
+ if exists('+shellslash') && &shell == $COMSPEC
+ let ssl = &shellslash
+ set noshellslash
+
+ let escaped = shellescape(a:s, special)
+
+ let &shellslash = ssl
+ else
+ let escaped = shellescape(a:s, special)
+ endif
+
+ return escaped
+endfunction
+
+" Function: s:GPGInit(bufread) {{{2
+"
+" initialize the plugin
+" The bufread argument specifies whether this was called due to BufReadCmd
+"
+function s:GPGInit(bufread)
+ call s:GPGDebug(3, printf(">>>>>>>> Entering s:GPGInit(%d)", a:bufread))
+
+ " For FileReadCmd, we're reading the contents into another buffer. If that
+ " buffer is also destined to be encrypted, then these settings will have
+ " already been set, otherwise don't set them since it limits the
+ " functionality of the cleartext buffer.
+ if a:bufread
+ " we don't want a swap file, as it writes unencrypted data to disk
+ setl noswapfile
+
+ " if persistent undo is present, disable it for this buffer
+ if exists('+undofile')
+ setl noundofile
+ endif
+
+ " first make sure nothing is written to ~/.viminfo while editing
+ " an encrypted file.
+ set viminfo=
+ endif
+
+ " the rest only has to be run once
+ if s:GPGInitRun
+ return
+ endif
+
+ " check what gpg command to use
+ if (!exists("g:GPGExecutable"))
+ if executable("gpg")
+ let g:GPGExecutable = "gpg --trust-model always"
+ else
+ let g:GPGExecutable = "gpg2 --trust-model always"
+ endif
+ endif
+
+ " check if gpg-agent is allowed
+ if (!exists("g:GPGUseAgent"))
+ let g:GPGUseAgent = 1
+ endif
+
+ " check if symmetric encryption is preferred
+ if (!exists("g:GPGPreferSymmetric"))
+ let g:GPGPreferSymmetric = 0
+ endif
+
+ " check if signed files are preferred
+ if (!exists("g:GPGPreferSign"))
+ let g:GPGPreferSign = 0
+ endif
+
+ " start with empty default recipients if none is defined so far
+ if (!exists("g:GPGDefaultRecipients"))
+ let g:GPGDefaultRecipients = []
+ endif
+
+ if (!exists("g:GPGPossibleRecipients"))
+ let g:GPGPossibleRecipients = []
+ endif
+
+
+ " prefer not to use pipes since it can garble gpg agent display
+ if (!exists("g:GPGUsePipes"))
+ let g:GPGUsePipes = 0
+ endif
+
+ " allow alternate gnupg homedir
+ if (!exists('g:GPGHomedir'))
+ let g:GPGHomedir = ''
+ endif
+
+ " print version
+ call s:GPGDebug(1, "gnupg.vim ". g:loaded_gnupg)
+
+ let s:GPGCommand = g:GPGExecutable
+
+ " don't use tty in gvim except for windows: we get their a tty for free.
+ " FIXME find a better way to avoid an error.
+ " with this solution only --use-agent will work
+ if (has("gui_running") && !has("gui_win32"))
+ let s:GPGCommand .= " --no-tty"
+ endif
+
+ " setup shell environment for unix and windows
+ let s:shellredirsave = &shellredir
+ let s:shellsave = &shell
+ let s:shelltempsave = &shelltemp
+ " noshelltemp isn't currently supported on Windows, but it doesn't cause any
+ " errors and this future proofs us against requiring changes if Windows
+ " gains noshelltemp functionality
+ let s:shelltemp = !g:GPGUsePipes
+ if (has("unix"))
+ " unix specific settings
+ let s:shellredir = ">%s 2>&1"
+ let s:shell = '/bin/sh'
+ let s:stderrredirnull = '2>/dev/null'
+ else
+ " windows specific settings
+ let s:shellredir = '>%s'
+ let s:shell = &shell
+ let s:stderrredirnull = '2>nul'
+ endif
+
+ call s:GPGDebug(3, "shellredirsave: " . s:shellredirsave)
+ call s:GPGDebug(3, "shellsave: " . s:shellsave)
+ call s:GPGDebug(3, "shelltempsave: " . s:shelltempsave)
+
+ call s:GPGDebug(3, "shell: " . s:shell)
+ call s:GPGDebug(3, "shellcmdflag: " . &shellcmdflag)
+ call s:GPGDebug(3, "shellxquote: " . &shellxquote)
+ call s:GPGDebug(3, "shellredir: " . s:shellredir)
+ call s:GPGDebug(3, "stderrredirnull: " . s:stderrredirnull)
+
+ call s:GPGDebug(3, "shell implementation: " . resolve(s:shell))
+
+ " find the supported algorithms
+ let output = s:GPGSystem({ 'level': 2, 'args': '--version' })
+
+ let gpgversion = substitute(output, '^gpg (GnuPG) \([0-9]\+\.\d\+\).*', '\1', '')
+ let s:GPGPubkey = substitute(output, ".*Pubkey: \\(.\\{-}\\)\n.*", "\\1", "")
+ let s:GPGCipher = substitute(output, ".*Cipher: \\(.\\{-}\\)\n.*", "\\1", "")
+ let s:GPGHash = substitute(output, ".*Hash: \\(.\\{-}\\)\n.*", "\\1", "")
+ let s:GPGCompress = substitute(output, ".*Compress.\\{-}: \\(.\\{-}\\)\n.*", "\\1", "")
+
+ " determine if gnupg can use the gpg-agent
+ if (str2float(gpgversion) >= 2.1 || (exists("$GPG_AGENT_INFO") && g:GPGUseAgent == 1))
+ if (!exists("$GPG_TTY") && !has("gui_running"))
+ " Need to determine the associated tty by running a command in the
+ " shell. We can't use system() here because that doesn't run in a shell
+ " connected to a tty, so it's rather useless.
+ "
+ " Save/restore &modified so the buffer isn't incorrectly marked as
+ " modified just by detecting the correct tty value.
+ " Do the &undolevels dance so the :read and :delete don't get added into
+ " the undo tree, as the user needn't be aware of these.
+ let [mod, levels] = [&l:modified, &undolevels]
+ set undolevels=-1
+ silent read !tty
+ let $GPG_TTY = getline('.')
+ silent delete
+ let [&l:modified, &undolevels] = [mod, levels]
+ " redraw is needed since we're using silent to run !tty, c.f. :help :!
+ redraw!
+ if (v:shell_error)
+ let $GPG_TTY = ""
+ echohl GPGWarning
+ echom "$GPG_TTY is not set and the `tty` command failed! gpg-agent might not work."
+ echohl None
+ endif
+ endif
+ let s:GPGCommand .= " --use-agent"
+ else
+ let s:GPGCommand .= " --no-use-agent"
+ endif
+
+ call s:GPGDebug(2, "public key algorithms: " . s:GPGPubkey)
+ call s:GPGDebug(2, "cipher algorithms: " . s:GPGCipher)
+ call s:GPGDebug(2, "hashing algorithms: " . s:GPGHash)
+ call s:GPGDebug(2, "compression algorithms: " . s:GPGCompress)
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGInit()")
+ let s:GPGInitRun = 1
+endfunction
+
+" Function: s:GPGCleanup() {{{2
+"
+" cleanup on leaving vim
+"
+function s:GPGCleanup()
+ call s:GPGDebug(3, ">>>>>>>> Entering s:GPGCleanup()")
+
+ " wipe out screen
+ new +only
+ redraw!
+
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGCleanup()")
+endfunction
+
+" Function: s:GPGDecrypt(bufread) {{{2
+"
+" decrypt the buffer and find all recipients of the encrypted file
+" The bufread argument specifies whether this was called due to BufReadCmd
+"
+function s:GPGDecrypt(bufread)
+ call s:GPGDebug(3, printf(">>>>>>>> Entering s:GPGDecrypt(%d)", a:bufread))
+
+ " get the filename of the current buffer
+ let filename = expand("<afile>:p")
+
+ " clear GPGRecipients and GPGOptions
+ if type(g:GPGDefaultRecipients) == type([])
+ let b:GPGRecipients = copy(g:GPGDefaultRecipients)
+ else
+ let b:GPGRecipients = []
+ echohl GPGWarning
+ echom "g:GPGDefaultRecipients is not a Vim list, please correct this in your vimrc!"
+ echohl None
+ endif
+ let b:GPGOptions = []
+
+ " file name minus extension
+ let autocmd_filename = fnameescape(expand('<afile>:r'))
+
+ " File doesn't exist yet, so nothing to decrypt
+ if !filereadable(filename)
+ " Allow the user to define actions for GnuPG buffers
+ silent doautocmd User GnuPG
+ " call the autocommand for the file minus .gpg$
+ silent execute ':doautocmd BufNewFile ' . autocmd_filename
+ call s:GPGDebug(2, 'called BufNewFile autocommand for ' . autocmd_filename)
+
+ " This is a new file, so force the user to edit the recipient list if
+ " they open a new file and public keys are preferred
+ if (g:GPGPreferSymmetric == 0)
+ call s:GPGEditRecipients()
+ endif
+
+ return
+ endif
+
+ " Only let this if the file actually exists, otherwise GPG functionality
+ " will be disabled when editing a buffer that doesn't yet have a backing
+ " file
+ let b:GPGEncrypted = 0
+
+ " find the recipients of the file
+ let cmd = { 'level': 3 }
+ let cmd.args = '--verbose --decrypt --list-only --dry-run --no-use-agent --logger-fd 1 ' . s:shellescape(filename)
+ let output = s:GPGSystem(cmd)
+
+ " Suppress the "N more lines" message when editing a file, not when reading
+ " the contents of a file into a buffer
+ let silent = a:bufread ? 'silent ' : ''
+
+ let asymmPattern = 'gpg: public key is ' . s:keyPattern
+ " check if the file is symmetric/asymmetric encrypted
+ if (match(output, "gpg: encrypted with [[:digit:]]\\+ passphrase") >= 0)
+ " file is symmetric encrypted
+ let b:GPGEncrypted = 1
+ call s:GPGDebug(1, "this file is symmetric encrypted")
+
+ let b:GPGOptions += ["symmetric"]
+
+ " find the used cipher algorithm
+ let cipher = substitute(output, ".*gpg: \\([^ ]\\+\\) encrypted data.*", "\\1", "")
+ if (match(s:GPGCipher, "\\<" . cipher . "\\>") >= 0)
+ let b:GPGOptions += ["cipher-algo " . cipher]
+ call s:GPGDebug(1, "cipher-algo is " . cipher)
+ else
+ echohl GPGWarning
+ echom "The cipher " . cipher . " is not known by the local gpg command. Using default!"
+ echo
+ echohl None
+ endif
+ elseif (match(output, asymmPattern) >= 0)
+ " file is asymmetric encrypted
+ let b:GPGEncrypted = 1
+ call s:GPGDebug(1, "this file is asymmetric encrypted")
+
+ let b:GPGOptions += ["encrypt"]
+
+ " find the used public keys
+ let start = match(output, asymmPattern)
+ while (start >= 0)
+ let start = start + strlen("gpg: public key is ")
+ let recipient = matchstr(output, s:keyPattern, start)
+ call s:GPGDebug(1, "recipient is " . recipient)
+ " In order to support anonymous communication, GnuPG allows eliding
+ " information in the encryption metadata specifying what keys the file
+ " was encrypted to (c.f., --throw-keyids and --hidden-recipient). In
+ " that case, the recipient(s) will be listed as having used a key of all
+ " zeroes.
+ " Since this will obviously never actually be in a keyring, only try to
+ " convert to an ID or add to the recipients list if it's not a hidden
+ " recipient.
+ if recipient !~? '^0x0\+$'
+ let name = s:GPGNameToID(recipient)
+ if !empty(name)
+ let b:GPGRecipients += [name]
+ call s:GPGDebug(1, "name of recipient is " . name)
+ else
+ let b:GPGRecipients += [recipient]
+ echohl GPGWarning
+ echom "The recipient \"" . recipient . "\" is not in your public keyring!"
+ echohl None
+ end
+ end
+ let start = match(output, asymmPattern, start)
+ endwhile
+ else
+ " file is not encrypted
+ let b:GPGEncrypted = 0
+ call s:GPGDebug(1, "this file is not encrypted")
+ echohl GPGWarning
+ echom "File is not encrypted, all GPG functions disabled!"
+ echohl None
+ exe printf('%sr %s', silent, fnameescape(filename))
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGDecrypt()")
+ return
+ endif
+
+ if a:bufread
+ silent execute ':doautocmd BufReadPre ' . autocmd_filename
+ call s:GPGDebug(2, 'called BufReadPre autocommand for ' . autocmd_filename)
+ else
+ silent execute ':doautocmd FileReadPre ' . autocmd_filename
+ call s:GPGDebug(2, 'called FileReadPre autocommand for ' . autocmd_filename)
+ endif
+
+ " check if the message is armored
+ if (match(output, "gpg: armor header") >= 0)
+ call s:GPGDebug(1, "this file is armored")
+ let b:GPGOptions += ["armor"]
+ endif
+
+ " finally decrypt the buffer content
+ " since even with the --quiet option passphrase typos will be reported,
+ " we must redirect stderr (using shell temporarily)
+ call s:GPGDebug(1, "decrypting file")
+ let cmd = { 'level': 1, 'ex': silent . 'r !' }
+ let cmd.args = '--quiet --decrypt ' . s:shellescape(filename, 1)
+ call s:GPGExecute(cmd)
+
+ if (v:shell_error) " message could not be decrypted
+ echohl GPGError
+ let blackhole = input("Message could not be decrypted! (Press ENTER)")
+ echohl None
+ " Only wipeout the buffer if we were creating one to start with.
+ " FileReadCmd just reads the content into the existing buffer
+ if a:bufread
+ silent bwipeout!
+ endif
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGDecrypt()")
+ return
+ endif
+
+ if a:bufread
+ " In order to make :undo a no-op immediately after the buffer is read,
+ " we need to do this dance with 'undolevels'. Actually discarding the undo
+ " history requires performing a change after setting 'undolevels' to -1 and,
+ " luckily, we have one we need to do (delete the extra line from the :r
+ " command)
+ let levels = &undolevels
+ set undolevels=-1
+ " :lockmarks doesn't actually prevent '[,'] from being overwritten, so we
+ " need to manually set them ourselves instead
+ silent 1delete
+ 1mark [
+ $mark ]
+ let &undolevels = levels
+ let &readonly = filereadable(filename) && filewritable(filename) == 0
+ " call the autocommand for the file minus .gpg$
+ silent execute ':doautocmd BufReadPost ' . autocmd_filename
+ call s:GPGDebug(2, 'called BufReadPost autocommand for ' . autocmd_filename)
+ else
+ " call the autocommand for the file minus .gpg$
+ silent execute ':doautocmd FileReadPost ' . autocmd_filename
+ call s:GPGDebug(2, 'called FileReadPost autocommand for ' . autocmd_filename)
+ endif
+
+ " Allow the user to define actions for GnuPG buffers
+ silent doautocmd User GnuPG
+
+ " refresh screen
+ redraw!
+
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGDecrypt()")
+endfunction
+
+" Function: s:GPGEncrypt() {{{2
+"
+" encrypts the buffer to all previous recipients
+"
+function s:GPGEncrypt()
+ call s:GPGDebug(3, ">>>>>>>> Entering s:GPGEncrypt()")
+
+ " FileWriteCmd is only called when a portion of a buffer is being written to
+ " disk. Since Vim always sets the '[,'] marks to the part of a buffer that
+ " is being written, that can be used to determine whether BufWriteCmd or
+ " FileWriteCmd triggered us.
+ if [line("'["), line("']")] == [1, line('$')]
+ let auType = 'BufWrite'
+ else
+ let auType = 'FileWrite'
+ endif
+
+ " file name minus extension
+ let autocmd_filename = fnameescape(expand('<afile>:r'))
+
+ silent exe ':doautocmd '. auType .'Pre '. autocmd_filename
+ call s:GPGDebug(2, 'called '. auType .'Pre autocommand for ' . autocmd_filename)
+
+ " store encoding and switch to a safe one
+ if (&fileencoding != &encoding)
+ let s:GPGEncoding = &encoding
+ let &encoding = &fileencoding
+ call s:GPGDebug(2, "encoding was \"" . s:GPGEncoding . "\", switched to \"" . &encoding . "\"")
+ else
+ let s:GPGEncoding = ""
+ call s:GPGDebug(2, "encoding and fileencoding are the same (\"" . &encoding . "\"), not switching")
+ endif
+
+ " guard for unencrypted files
+ if (exists("b:GPGEncrypted") && b:GPGEncrypted == 0)
+ echohl GPGError
+ let blackhole = input("Message could not be encrypted! (Press ENTER)")
+ echohl None
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGEncrypt()")
+ return
+ endif
+
+ let filename = resolve(expand('<afile>'))
+ " initialize GPGOptions if not happened before
+ if (!exists("b:GPGOptions") || empty(b:GPGOptions))
+ let b:GPGOptions = []
+ if (exists("g:GPGPreferSymmetric") && g:GPGPreferSymmetric == 1)
+ let b:GPGOptions += ["symmetric"]
+ let b:GPGRecipients = []
+ else
+ let b:GPGOptions += ["encrypt"]
+ endif
+ " Fallback to preference by filename if the user didn't indicate
+ " their preference.
+ let preferArmor = get(g:, 'GPGPreferArmor', -1)
+ if (preferArmor >= 0 && preferArmor) || filename =~ '\.asc$'
+ let b:GPGOptions += ["armor"]
+ endif
+ if (exists("g:GPGPreferSign") && g:GPGPreferSign == 1)
+ let b:GPGOptions += ["sign"]
+ endif
+ call s:GPGDebug(1, "no options set, so using default options: " . string(b:GPGOptions))
+ endif
+
+ " built list of options
+ let options = ""
+ for option in b:GPGOptions
+ let options = options . " --" . option . " "
+ endfor
+
+ if (!exists('b:GPGRecipients'))
+ let b:GPGRecipients = []
+ endif
+
+ " check here again if all recipients are available in the keyring
+ let recipients = s:GPGCheckRecipients(b:GPGRecipients)
+
+ " check if there are unknown recipients and warn
+ if !empty(recipients.unknown)
+ echohl GPGWarning
+ echom "Please use GPGEditRecipients to correct!!"
+ echo
+ echohl None
+
+ " Let user know whats happend and copy known_recipients back to buffer
+ let dummy = input("Press ENTER to quit")
+ endif
+
+ " built list of recipients
+ let options .= ' ' . join(map(recipients.valid, '"-r ".v:val'), ' ')
+
+ " encrypt the buffer
+ let destfile = tempname()
+ let cmd = { 'level': 1, 'ex': "'[,']w !" }
+ let cmd.args = '--quiet --no-encrypt-to ' . options
+ let cmd.redirect = '>' . s:shellescape(destfile, 1)
+ silent call s:GPGExecute(cmd)
+
+ " restore encoding
+ if (s:GPGEncoding != "")
+ let &encoding = s:GPGEncoding
+ call s:GPGDebug(2, "restored encoding \"" . &encoding . "\"")
+ endif
+
+ if (v:shell_error) " message could not be encrypted
+ " Command failed, so clean up the tempfile
+ call delete(destfile)
+ echohl GPGError
+ let blackhole = input("Message could not be encrypted! (Press ENTER)")
+ echohl None
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGEncrypt()")
+ return
+ endif
+
+ if rename(destfile, filename)
+ " Rename failed, so clean up the tempfile
+ call delete(destfile)
+ echohl GPGError
+ echom printf("\"%s\" E212: Can't open file for writing", filename)
+ echohl None
+ return
+ endif
+
+ if auType == 'BufWrite'
+ setl nomodified
+ let &readonly = filereadable(filename) && filewritable(filename) == 0
+ endif
+
+ silent exe ':doautocmd '. auType .'Post '. autocmd_filename
+ call s:GPGDebug(2, 'called '. auType .'Post autocommand for ' . autocmd_filename)
+
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGEncrypt()")
+endfunction
+
+" Function: s:GPGViewRecipients() {{{2
+"
+" echo the recipients
+"
+function s:GPGViewRecipients()
+ call s:GPGDebug(3, ">>>>>>>> Entering s:GPGViewRecipients()")
+
+ " guard for unencrypted files
+ if (exists("b:GPGEncrypted") && b:GPGEncrypted == 0)
+ echohl GPGWarning
+ echom "File is not encrypted, all GPG functions disabled!"
+ echohl None
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGViewRecipients()")
+ return
+ endif
+
+ let recipients = s:GPGCheckRecipients(b:GPGRecipients)
+
+ echo 'This file has following recipients (Unknown recipients have a prepended "!"):'
+ " echo the recipients
+ for name in recipients.valid
+ let name = s:GPGIDToName(name)
+ echo name
+ endfor
+
+ " echo the unknown recipients
+ echohl GPGWarning
+ for name in recipients.unknown
+ let name = "!" . name
+ echo name
+ endfor
+ echohl None
+
+ " check if there is any known recipient
+ if empty(recipients.valid)
+ echohl GPGError
+ echom 'There are no known recipients!'
+ echohl None
+ endif
+
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGViewRecipients()")
+endfunction
+
+" Function: s:GPGEditRecipients() {{{2
+"
+" create a scratch buffer with all recipients to add/remove recipients
+"
+function s:GPGEditRecipients()
+ call s:GPGDebug(3, ">>>>>>>> Entering s:GPGEditRecipients()")
+
+ " guard for unencrypted files
+ if (exists("b:GPGEncrypted") && b:GPGEncrypted == 0)
+ echohl GPGWarning
+ echom "File is not encrypted, all GPG functions disabled!"
+ echohl None
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGEditRecipients()")
+ return
+ endif
+
+ " only do this if it isn't already a GPGRecipients_* buffer
+ if (match(bufname("%"), "^\\(GPGRecipients_\\|GPGOptions_\\)") != 0 && match(bufname("%"), "\.\\(gpg\\|asc\\|pgp\\)$") >= 0)
+
+ " save buffer name
+ let buffername = bufname("%")
+ let editbuffername = "GPGRecipients_" . buffername
+
+ " check if this buffer exists
+ if (!bufexists(editbuffername))
+ " create scratch buffer
+ execute 'silent! split ' . fnameescape(editbuffername)
+
+ " add a autocommand to regenerate the recipients after a write
+ autocmd BufHidden,BufUnload,BufWriteCmd <buffer> call s:GPGFinishRecipientsBuffer()
+ else
+ if (bufwinnr(editbuffername) >= 0)
+ " switch to scratch buffer window
+ execute 'silent! ' . bufwinnr(editbuffername) . "wincmd w"
+ else
+ " split scratch buffer window
+ execute 'silent! sbuffer ' . fnameescape(editbuffername)
+
+ " add a autocommand to regenerate the recipients after a write
+ autocmd BufHidden,BufUnload,BufWriteCmd <buffer> call s:GPGFinishRecipientsBuffer()
+ endif
+
+ " empty the buffer
+ silent %delete
+ endif
+
+ " Mark the buffer as a scratch buffer
+ setlocal buftype=acwrite
+ setlocal bufhidden=hide
+ setlocal noswapfile
+ setlocal nowrap
+ setlocal nobuflisted
+ setlocal nonumber
+
+ " so we know for which other buffer this edit buffer is
+ let b:GPGCorrespondingTo = buffername
+
+ " put some comments to the scratch buffer
+ silent put ='GPG: ----------------------------------------------------------------------'
+ silent put ='GPG: Please edit the list of recipients, one recipient per line.'
+ silent put ='GPG: Unknown recipients have a prepended \"!\".'
+ silent put ='GPG: Lines beginning with \"GPG:\" are removed automatically.'
+ silent put ='GPG: Data after recipients between and including \"(\" and \")\" is ignored.'
+ silent put ='GPG: Closing this buffer commits changes.'
+ silent put ='GPG: ----------------------------------------------------------------------'
+
+ " get the recipients
+ let recipients = s:GPGCheckRecipients(getbufvar(b:GPGCorrespondingTo, "GPGRecipients"))
+
+ " if there are no known or unknown recipients, use the default ones
+ if (empty(recipients.valid) && empty(recipients.unknown))
+ if (type(g:GPGDefaultRecipients) == type([]))
+ let recipients = s:GPGCheckRecipients(g:GPGDefaultRecipients)
+ else
+ echohl GPGWarning
+ echom "g:GPGDefaultRecipients is not a Vim list, please correct this in your vimrc!"
+ echohl None
+ endif
+ endif
+
+ " put the recipients in the scratch buffer
+ for name in recipients.valid
+ let name = s:GPGIDToName(name)
+ silent put =name
+ endfor
+
+ " put the unknown recipients in the scratch buffer
+ let syntaxPattern = ''
+ if !empty(recipients.unknown)
+ let flaggedNames = map(recipients.unknown, '"!".v:val')
+ call append('$', flaggedNames)
+ let syntaxPattern = '\(' . join(flaggedNames, '\|') . '\)'
+ endif
+
+ for line in g:GPGPossibleRecipients
+ silent put ='GPG: '.line
+ endfor
+
+ " define highlight
+ if (has("syntax") && exists("g:syntax_on"))
+ highlight clear GPGUnknownRecipient
+ if !empty(syntaxPattern)
+ execute 'syntax match GPGUnknownRecipient "' . syntaxPattern . '"'
+ highlight link GPGUnknownRecipient GPGHighlightUnknownRecipient
+ endif
+
+ syntax match GPGComment "^GPG:.*$"
+ execute 'syntax match GPGComment "' . s:GPGMagicString . '.*$"'
+ highlight clear GPGComment
+ highlight link GPGComment Comment
+ endif
+
+ " delete the empty first line
+ silent 1delete
+
+ " jump to the first recipient
+ silent $
+
+ endif
+
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGEditRecipients()")
+endfunction
+
+" Function: s:GPGFinishRecipientsBuffer() {{{2
+"
+" create a new recipient list from RecipientsBuffer
+"
+function s:GPGFinishRecipientsBuffer()
+ call s:GPGDebug(3, ">>>>>>>> Entering s:GPGFinishRecipientsBuffer()")
+
+ " guard for unencrypted files
+ if (exists("b:GPGEncrypted") && b:GPGEncrypted == 0)
+ echohl GPGWarning
+ echom "File is not encrypted, all GPG functions disabled!"
+ echohl None
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGFinishRecipientsBuffer()")
+ return
+ endif
+
+ " go to buffer before doing work
+ if (bufnr("%") != expand("<abuf>"))
+ " switch to scratch buffer window
+ execute 'silent! ' . bufwinnr(expand("<afile>")) . "wincmd w"
+ endif
+
+ " delete the autocommand
+ autocmd! * <buffer>
+
+ " get the recipients from the scratch buffer
+ let recipients = []
+ let lines = getline(1,"$")
+ for recipient in lines
+ let matches = matchlist(recipient, '^\(.\{-}\)\%(' . s:GPGMagicString . '(ID:\s\+\(' . s:keyPattern . '\)\s\+.*\)\=$')
+
+ let recipient = matches[2] ? matches[2] : matches[1]
+
+ " delete all spaces at beginning and end of the recipient
+ " also delete a '!' at the beginning of the recipient
+ let recipient = substitute(recipient, "^[[:space:]!]*\\(.\\{-}\\)[[:space:]]*$", "\\1", "")
+
+ " delete comment lines
+ let recipient = substitute(recipient, "^GPG:.*$", "", "")
+
+ " only do this if the line is not empty
+ if !empty(recipient)
+ let gpgid = s:GPGNameToID(recipient)
+ if !empty(gpgid)
+ if (match(recipients, gpgid) < 0)
+ let recipients += [gpgid]
+ endif
+ else
+ if (match(recipients, recipient) < 0)
+ let recipients += [recipient]
+ echohl GPGWarning
+ echom "The recipient \"" . recipient . "\" is not in your public keyring!"
+ echohl None
+ endif
+ endif
+ endif
+ endfor
+
+ " write back the new recipient list to the corresponding buffer and mark it
+ " as modified. Buffer is now for sure an encrypted buffer.
+ call setbufvar(b:GPGCorrespondingTo, "GPGRecipients", recipients)
+ call setbufvar(b:GPGCorrespondingTo, "&mod", 1)
+ call setbufvar(b:GPGCorrespondingTo, "GPGEncrypted", 1)
+
+ " check if there is any known recipient
+ if empty(recipients)
+ echohl GPGError
+ echom 'There are no known recipients!'
+ echohl None
+ endif
+
+ " reset modified flag
+ setl nomodified
+
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGFinishRecipientsBuffer()")
+endfunction
+
+" Function: s:GPGViewOptions() {{{2
+"
+" echo the recipients
+"
+function s:GPGViewOptions()
+ call s:GPGDebug(3, ">>>>>>>> Entering s:GPGViewOptions()")
+
+ " guard for unencrypted files
+ if (exists("b:GPGEncrypted") && b:GPGEncrypted == 0)
+ echohl GPGWarning
+ echom "File is not encrypted, all GPG functions disabled!"
+ echohl None
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGViewOptions()")
+ return
+ endif
+
+ if (exists("b:GPGOptions"))
+ echo 'This file has following options:'
+ " echo the options
+ for option in b:GPGOptions
+ echo option
+ endfor
+ endif
+
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGViewOptions()")
+endfunction
+
+" Function: s:GPGEditOptions() {{{2
+"
+" create a scratch buffer with all recipients to add/remove recipients
+"
+function s:GPGEditOptions()
+ call s:GPGDebug(3, ">>>>>>>> Entering s:GPGEditOptions()")
+
+ " guard for unencrypted files
+ if (exists("b:GPGEncrypted") && b:GPGEncrypted == 0)
+ echohl GPGWarning
+ echom "File is not encrypted, all GPG functions disabled!"
+ echohl None
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGEditOptions()")
+ return
+ endif
+
+ " only do this if it isn't already a GPGOptions_* buffer
+ if (match(bufname("%"), "^\\(GPGRecipients_\\|GPGOptions_\\)") != 0 && match(bufname("%"), "\.\\(gpg\\|asc\\|pgp\\)$") >= 0)
+
+ " save buffer name
+ let buffername = bufname("%")
+ let editbuffername = "GPGOptions_" . buffername
+
+ " check if this buffer exists
+ if (!bufexists(editbuffername))
+ " create scratch buffer
+ execute 'silent! split ' . fnameescape(editbuffername)
+
+ " add a autocommand to regenerate the options after a write
+ autocmd BufHidden,BufUnload,BufWriteCmd <buffer> call s:GPGFinishOptionsBuffer()
+ else
+ if (bufwinnr(editbuffername) >= 0)
+ " switch to scratch buffer window
+ execute 'silent! ' . bufwinnr(editbuffername) . "wincmd w"
+ else
+ " split scratch buffer window
+ execute 'silent! sbuffer ' . fnameescape(editbuffername)
+
+ " add a autocommand to regenerate the options after a write
+ autocmd BufHidden,BufUnload,BufWriteCmd <buffer> call s:GPGFinishOptionsBuffer()
+ endif
+
+ " empty the buffer
+ silent %delete
+ endif
+
+ " Mark the buffer as a scratch buffer
+ setlocal buftype=nofile
+ setlocal noswapfile
+ setlocal nowrap
+ setlocal nobuflisted
+ setlocal nonumber
+
+ " so we know for which other buffer this edit buffer is
+ let b:GPGCorrespondingTo = buffername
+
+ " put some comments to the scratch buffer
+ silent put ='GPG: ----------------------------------------------------------------------'
+ silent put ='GPG: THERE IS NO CHECK OF THE ENTERED OPTIONS!'
+ silent put ='GPG: YOU NEED TO KNOW WHAT YOU ARE DOING!'
+ silent put ='GPG: IF IN DOUBT, QUICKLY EXIT USING :x OR :bd.'
+ silent put ='GPG: Please edit the list of options, one option per line.'
+ silent put ='GPG: Please refer to the gpg documentation for valid options.'
+ silent put ='GPG: Lines beginning with \"GPG:\" are removed automatically.'
+ silent put ='GPG: Closing this buffer commits changes.'
+ silent put ='GPG: ----------------------------------------------------------------------'
+
+ " put the options in the scratch buffer
+ let options = getbufvar(b:GPGCorrespondingTo, "GPGOptions")
+
+ for option in options
+ silent put =option
+ endfor
+
+ " delete the empty first line
+ silent 1delete
+
+ " jump to the first option
+ silent $
+
+ " define highlight
+ if (has("syntax") && exists("g:syntax_on"))
+ syntax match GPGComment "^GPG:.*$"
+ highlight clear GPGComment
+ highlight link GPGComment Comment
+ endif
+ endif
+
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGEditOptions()")
+endfunction
+
+" Function: s:GPGFinishOptionsBuffer() {{{2
+"
+" create a new option list from OptionsBuffer
+"
+function s:GPGFinishOptionsBuffer()
+ call s:GPGDebug(3, ">>>>>>>> Entering s:GPGFinishOptionsBuffer()")
+
+ " guard for unencrypted files
+ if (exists("b:GPGEncrypted") && b:GPGEncrypted == 0)
+ echohl GPGWarning
+ echom "File is not encrypted, all GPG functions disabled!"
+ echohl None
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGFinishOptionsBuffer()")
+ return
+ endif
+
+ " go to buffer before doing work
+ if (bufnr("%") != expand("<abuf>"))
+ " switch to scratch buffer window
+ execute 'silent! ' . bufwinnr(expand("<afile>")) . "wincmd w"
+ endif
+
+ " clear options and unknownOptions
+ let options = []
+ let unknownOptions = []
+
+ " delete the autocommand
+ autocmd! * <buffer>
+
+ " get the options from the scratch buffer
+ let lines = getline(1, "$")
+ for option in lines
+ " delete all spaces at beginning and end of the option
+ " also delete a '!' at the beginning of the option
+ let option = substitute(option, "^[[:space:]!]*\\(.\\{-}\\)[[:space:]]*$", "\\1", "")
+ " delete comment lines
+ let option = substitute(option, "^GPG:.*$", "", "")
+
+ " only do this if the line is not empty
+ if (!empty(option) && match(options, option) < 0)
+ let options += [option]
+ endif
+ endfor
+
+ " write back the new option list to the corresponding buffer and mark it
+ " as modified
+ call setbufvar(b:GPGCorrespondingTo, "GPGOptions", options)
+ call setbufvar(b:GPGCorrespondingTo, "&mod", 1)
+
+ " reset modified flag
+ setl nomodified
+
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGFinishOptionsBuffer()")
+endfunction
+
+" Function: s:GPGCheckRecipients(tocheck) {{{2
+"
+" check if recipients are known
+" Returns: dictionary of recipients, {'valid': [], 'unknown': []}
+"
+function s:GPGCheckRecipients(tocheck)
+ call s:GPGDebug(3, ">>>>>>>> Entering s:GPGCheckRecipients()")
+
+ let recipients = {'valid': [], 'unknown': []}
+
+ if (type(a:tocheck) == type([]))
+ for recipient in a:tocheck
+ let gpgid = s:GPGNameToID(recipient)
+ if !empty(gpgid)
+ if (match(recipients.valid, gpgid) < 0)
+ call add(recipients.valid, gpgid)
+ endif
+ else
+ if (match(recipients.unknown, recipient) < 0)
+ call add(recipients.unknown, recipient)
+ echohl GPGWarning
+ echom "The recipient \"" . recipient . "\" is not in your public keyring!"
+ echohl None
+ endif
+ end
+ endfor
+ endif
+
+ call s:GPGDebug(2, "recipients are: " . string(recipients.valid))
+ call s:GPGDebug(2, "unknown recipients are: " . string(recipients.unknown))
+
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGCheckRecipients()")
+ return recipients
+endfunction
+
+" Function: s:GPGNameToID(name) {{{2
+"
+" find GPG key ID corresponding to a name
+" Returns: ID for the given name
+"
+function s:GPGNameToID(name)
+ call s:GPGDebug(3, ">>>>>>>> Entering s:GPGNameToID()")
+
+ " ask gpg for the id for a name
+ let cmd = { 'level': 2 }
+ let cmd.args = '--quiet --with-colons --fixed-list-mode --list-keys ' . s:shellescape(a:name)
+ let output = s:GPGSystem(cmd)
+
+ " when called with "--with-colons" gpg encodes its output _ALWAYS_ as UTF-8,
+ " so convert it, if necessary
+ if (&encoding != "utf-8")
+ let output = iconv(output, "utf-8", &encoding)
+ endif
+ let lines = split(output, "\n")
+
+ " parse the output of gpg
+ let pubseen = 0
+ let counter = 0
+ let gpgids = []
+ let seen_keys = {}
+ let skip_key = 0
+ let has_strftime = exists('*strftime')
+ let choices = "The name \"" . a:name . "\" is ambiguous. Please select the correct key:\n"
+ for line in lines
+
+ let fields = split(line, ":")
+
+ " search for the next pub
+ if (fields[0] == "pub")
+ " check if this key has already been processed
+ if has_key(seen_keys, fields[4])
+ let skip_key = 1
+ continue
+ endif
+ let skip_key = 0
+ let seen_keys[fields[4]] = 1
+
+ " Ignore keys which are not usable for encryption
+ if fields[11] !~? 'e'
+ continue
+ endif
+
+ let identity = fields[4]
+ let gpgids += [identity]
+ if has_strftime
+ let choices = choices . counter . ": ID: 0x" . identity . " created at " . strftime("%c", fields[5]) . "\n"
+ else
+ let choices = choices . counter . ": ID: 0x" . identity . "\n"
+ endif
+ let counter = counter+1
+ let pubseen = 1
+ " search for the next uid
+ elseif (!skip_key && fields[0] == "uid")
+ let choices = choices . " " . fields[9] . "\n"
+ endif
+
+ endfor
+
+ " counter > 1 means we have more than one results
+ let answer = 0
+ if (counter > 1)
+ let choices = choices . "Enter number: "
+ let answer = input(choices, "0")
+ while (answer == "")
+ let answer = input("Enter number: ", "0")
+ endwhile
+ endif
+
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGNameToID()")
+ return get(gpgids, answer, "")
+endfunction
+
+" Function: s:GPGIDToName(identity) {{{2
+"
+" find name corresponding to a GPG key ID
+" Returns: Name for the given ID
+"
+function s:GPGIDToName(identity)
+ call s:GPGDebug(3, ">>>>>>>> Entering s:GPGIDToName()")
+
+ " TODO is the encryption subkey really unique?
+
+ " ask gpg for the id for a name
+ let cmd = { 'level': 2 }
+ let cmd.args = '--quiet --with-colons --fixed-list-mode --list-keys ' . a:identity
+ let output = s:GPGSystem(cmd)
+
+ " when called with "--with-colons" gpg encodes its output _ALWAYS_ as UTF-8,
+ " so convert it, if necessary
+ if (&encoding != "utf-8")
+ let output = iconv(output, "utf-8", &encoding)
+ endif
+ let lines = split(output, "\n")
+
+ " parse the output of gpg
+ let pubseen = 0
+ let uid = ""
+ for line in lines
+ let fields = split(line, ":")
+
+ if !pubseen " search for the next pub
+ if (fields[0] == "pub")
+ " Ignore keys which are not usable for encryption
+ if fields[11] !~? 'e'
+ continue
+ endif
+
+ let pubseen = 1
+ endif
+ else " search for the next uid
+ if (fields[0] == "uid")
+ let pubseen = 0
+ if exists("*strftime")
+ let uid = fields[9] . s:GPGMagicString . "(ID: 0x" . a:identity . " created at " . strftime("%c", fields[5]) . ")"
+ else
+ let uid = fields[9] . s:GPGMagicString . "(ID: 0x" . a:identity . ")"
+ endif
+ break
+ endif
+ endif
+ endfor
+
+ call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGIDToName()")
+ return uid
+endfunction
+
+" Function: s:GPGPreCmd() {{{2
+"
+" Setup the environment for running the gpg command
+"
+function s:GPGPreCmd()
+ let &shellredir = s:shellredir
+ let &shell = s:shell
+ let &shelltemp = s:shelltemp
+ " Force C locale so GPG output is consistent
+ let s:messages = v:lang
+ language messages C
+endfunction
+
+
+" Function: s:GPGPostCmd() {{{2
+"
+" Restore the user's environment after running the gpg command
+"
+function s:GPGPostCmd()
+ let &shellredir = s:shellredirsave
+ let &shell = s:shellsave
+ let &shelltemp = s:shelltempsave
+ execute 'language messages' s:messages
+ " Workaround a bug in the interaction between console vim and
+ " pinentry-curses by forcing Vim to re-detect and setup its terminal
+ " settings
+ let &term = &term
+ silent doautocmd TermChanged
+endfunction
+
+" Function: s:GPGSystem(dict) {{{2
+"
+" run g:GPGCommand using system(), logging the commandline and output. This
+" uses temp files (regardless of how 'shelltemp' is set) to hold the output of
+" the command, so it must not be used for sensitive commands.
+" Recognized keys are:
+" level - Debug level at which the commandline and output will be logged
+" args - Arguments to be given to g:GPGCommand
+"
+" Returns: command output
+"
+function s:GPGSystem(dict)
+ let commandline = s:GPGCommand
+ if (!empty(g:GPGHomedir))
+ let commandline .= ' --homedir ' . s:shellescape(g:GPGHomedir)
+ endif
+ let commandline .= ' ' . a:dict.args
+ let commandline .= ' ' . s:stderrredirnull
+ call s:GPGDebug(a:dict.level, "command: ". commandline)
+
+ call s:GPGPreCmd()
+ let output = system(commandline)
+ call s:GPGPostCmd()
+
+ call s:GPGDebug(a:dict.level, "rc: ". v:shell_error)
+ call s:GPGDebug(a:dict.level, "output: ". output)
+ return output
+endfunction
+
+" Function: s:GPGExecute(dict) {{{2
+"
+" run g:GPGCommand using :execute, logging the commandline
+" Recognized keys are:
+" level - Debug level at which the commandline will be logged
+" args - Arguments to be given to g:GPGCommand
+" ex - Ex command which will be :executed
+" redirect - Shell redirect to use, if needed
+"
+function s:GPGExecute(dict)
+ let commandline = printf('%s%s', a:dict.ex, s:GPGCommand)
+ if (!empty(g:GPGHomedir))
+ let commandline .= ' --homedir ' . s:shellescape(g:GPGHomedir, 1)
+ endif
+ let commandline .= ' ' . a:dict.args
+ if (has_key(a:dict, 'redirect'))
+ let commandline .= ' ' . a:dict.redirect
+ endif
+ let commandline .= ' ' . s:stderrredirnull
+ call s:GPGDebug(a:dict.level, "command: " . commandline)
+
+ call s:GPGPreCmd()
+ execute commandline
+ call s:GPGPostCmd()
+
+ call s:GPGDebug(a:dict.level, "rc: ". v:shell_error)
+endfunction
+
+" Function: s:GPGDebug(level, text) {{{2
+"
+" output debug message, if this message has high enough importance
+" only define function if GPGDebugLevel set at all
+"
+function s:GPGDebug(level, text)
+ if exists("g:GPGDebugLevel") && g:GPGDebugLevel >= a:level
+ if exists("g:GPGDebugLog")
+ execute "redir >> " . g:GPGDebugLog
+ silent echom "GnuPG: " . a:text
+ redir END
+ else
+ echom "GnuPG: " . a:text
+ endif
+ endif
+endfunction
+
+" Section: Commands {{{1
+
+command! GPGViewRecipients call s:GPGViewRecipients()
+command! GPGEditRecipients call s:GPGEditRecipients()
+command! GPGViewOptions call s:GPGViewOptions()
+command! GPGEditOptions call s:GPGEditOptions()
+
+" Section: Menu {{{1
+
+if (has("menu"))
+ amenu <silent> Plugin.GnuPG.View\ Recipients :GPGViewRecipients<CR>
+ amenu <silent> Plugin.GnuPG.Edit\ Recipients :GPGEditRecipients<CR>
+ amenu <silent> Plugin.GnuPG.View\ Options :GPGViewOptions<CR>
+ amenu <silent> Plugin.GnuPG.Edit\ Options :GPGEditOptions<CR>
+endif
+
+" vim600: set foldmethod=marker foldlevel=0 :
diff --git a/base/vim/plugin/scratch.vim b/base/vim/plugin/scratch.vim
new file mode 100644
index 0000000..9b83ea4
--- /dev/null
+++ b/base/vim/plugin/scratch.vim
@@ -0,0 +1,134 @@
+" scratch.vim
+" Author: Abhilash Koneri (abhilash_koneri at hotmail dot com)
+" Improved By: Hari Krishna Dara (hari_vim at yahoo dot com)
+" Last Change: 25-Feb-2004 @ 09:48
+" Created: 17-Aug-2002
+" Version: 1.0.0
+" Download From:
+" http://www.vim.org/script.php?script_id=389
+"----------------------------------------------------------------------
+" This is a simple plugin that creates a scratch buffer for your
+" vim session and helps to access it when you need it.
+"
+" If you like the custom mappings provided in the script - hitting
+" <F8> should create a new scratch buffer. You can do your scribes
+" here and if you want to get rid of it, hit <F8> again inside scratch buffer
+" window. If you want to get back to the scratch buffer repeat <F8>. Use
+" <Plug>ShowScratchBuffer and <Plug>InsShowScratchBuffer to customize these
+" mappings.
+"
+" If you want to designate a file into which the scratch buffer contents
+" should automatically be dumped to, when Vim exits, set its path to
+" g:scratchBackupFile global variable. This file can be accessed just in case
+" you happen to have some important information in the scratch buffer and quit
+" Vim (or shutdown the m/c) forgetting to copy it over. The target file is
+" force overwritten using the :write! command so make sure you set a file name
+" that can accidentally be used for other purposes (especially when you use
+" relative paths). I recommend a value of '/tmp/scratch.txt'.
+" CAUTION: This feature works only when Vim generates VimLeavePre autocommad.
+"
+" Custom mappings
+" ---------------
+" The ones defined below are not very ergonomic!
+"----------------------------------------------------------------------
+"Standard Inteface: <F8> to make a new ScratchBuffer, <F8>-again to hide one
+
+if exists('loaded_scratch')
+ finish
+endif
+let loaded_scratch = 1
+
+" Make sure line-continuations won't cause any problem. This will be restored
+" at the end
+let s:save_cpo = &cpo
+set cpo&vim
+
+if (! exists("no_plugin_maps") || ! no_plugin_maps) &&
+ \ (! exists("no_scratch_maps") || ! no_scratch_maps)
+ if !hasmapto('<Plug>ShowScratchBuffer',"n")
+ nmap <unique> <silent> <F8> <Plug>ShowScratchBuffer
+ endif
+ if !hasmapto('<Plug>InsShowScratchBuffer',"i")
+ imap <unique> <silent> <F8> <Plug>InsShowScratchBuffer
+ endif
+endif
+
+" User Overrideable Plugin Interface
+nmap <script> <silent> <Plug>ShowScratchBuffer
+ \ :silent call <SID>ShowScratchBuffer()<cr>
+imap <script> <silent> <Plug>InsShowScratchBuffer
+ \ <c-o>:silent call <SID>ShowScratchBuffer()<cr>
+
+command! -nargs=0 Scratch :call <SID>ShowScratchBuffer()
+
+if !exists('g:scratchBackupFile')
+ let g:scratchBackupFile = '' " So that users can easily find this var.
+endif
+aug ScratchBackup
+ au!
+ au VimLeavePre * :call <SID>BackupScratchBuffer()
+aug END
+
+let s:SCRATCH_BUFFER_NAME="[Scratch]"
+if !exists('s:buffer_number') " Supports reloading.
+ let s:buffer_number = -1
+endif
+
+"----------------------------------------------------------------------
+" Diplays the scratch buffer. Creates one if it is an already
+" present
+"----------------------------------------------------------------------
+function! <SID>ShowScratchBuffer()
+ if(s:buffer_number == -1 || bufexists(s:buffer_number) == 0)
+ " Temporarily modify isfname to avoid treating the name as a pattern.
+ let _isf = &isfname
+ set isfname-=\
+ set isfname-=[
+ if exists('+shellslash')
+ exec "sp \\\\". s:SCRATCH_BUFFER_NAME
+ else
+ exec "sp \\". s:SCRATCH_BUFFER_NAME
+ endif
+ let &isfname = _isf
+ let s:buffer_number = bufnr('%')
+ else
+ let buffer_win=bufwinnr(s:buffer_number)
+ if(buffer_win == -1)
+ exec 'sb '. s:buffer_number
+ else
+ exec buffer_win.'wincmd w'
+ endif
+ endif
+ " Do setup always, just in case.
+ setlocal buftype=nofile
+ setlocal bufhidden=hide
+ setlocal nobuflisted
+ setlocal noswapfile
+ setlocal noro
+ nmap <buffer> <silent> <Plug>ShowScratchBuffer :hide<cr>
+ imap <buffer> <silent> <Plug>InsShowScratchBuffer <c-o>:hide<cr>
+ command! -buffer -nargs=0 Scratch :hide
+endfunction
+
+function! s:BackupScratchBuffer()
+ if s:buffer_number != -1 && exists('g:scratchBackupFile') &&
+ \ g:scratchBackupFile != ''
+ exec 'split #' . s:buffer_number
+ " Avoid writing empty scratch buffers.
+ if line('$') > 1 || getline(1) !~ '^\s*$'
+ let _cpo=&cpo
+ try
+ set cpo-=A
+ exec 'write!' g:scratchBackupFile
+ finally
+ let &cpo=_cpo
+ endtry
+ endif
+ endif
+endfunction
+
+" Restore cpo.
+let &cpo = s:save_cpo
+unlet s:save_cpo
+
+" vim6: sw=2 et
diff --git a/base/vim/syntax/git-diff.vim b/base/vim/syntax/git-diff.vim
new file mode 100644
index 0000000..54a97ec
--- /dev/null
+++ b/base/vim/syntax/git-diff.vim
@@ -0,0 +1,8 @@
+runtime syntax/diff.vim
+
+syntax match gitDiffStatLine /^ .\{-}\zs[+-]\+$/ contains=gitDiffStatAdd,gitDiffStatDelete
+syntax match gitDiffStatAdd /+/ contained
+syntax match gitDiffStatDelete /-/ contained
+
+highlight gitDiffStatAdd ctermfg=2
+highlight gitDiffStatDelete ctermfg=5
diff --git a/base/vim/syntax/git-log.vim b/base/vim/syntax/git-log.vim
new file mode 100644
index 0000000..571b28a
--- /dev/null
+++ b/base/vim/syntax/git-log.vim
@@ -0,0 +1,3 @@
+syntax match gitLogCommit +^commit \x\{40}+
+
+highlight link gitLogCommit Statement
diff --git a/base/vim/syntax/git-status.vim b/base/vim/syntax/git-status.vim
new file mode 100644
index 0000000..4cf05e5
--- /dev/null
+++ b/base/vim/syntax/git-status.vim
@@ -0,0 +1,18 @@
+runtime syntax/diff.vim
+setlocal filetype=
+
+syntax match gitStatusComment +^#.*+ contains=ALL
+
+syntax match gitStatusBranch +On branch .\++
+
+syntax match gitStatusUndracked +\t\zs.\++
+syntax match gitStatusNewFile +\t\zsnew file: .\++
+syntax match gitStatusModified +\t\zsmodified: .\++
+
+highlight link gitStatusComment Comment
+
+highlight link gitStatusBranch Title
+
+highlight link gitStatusUndracked diffOnly
+highlight link gitStatusNewFile diffAdded
+highlight link gitStatusModified diffChanged
diff --git a/base/vim/syntax/markdown.vim b/base/vim/syntax/markdown.vim
new file mode 100644
index 0000000..5700d4b
--- /dev/null
+++ b/base/vim/syntax/markdown.vim
@@ -0,0 +1,130 @@
+" Vim syntax file
+" Language: Markdown
+" Maintainer: Tim Pope <vimNOSPAM@tpope.org>
+" Filenames: *.markdown
+
+if exists("b:current_syntax")
+ finish
+endif
+
+if !exists('main_syntax')
+ let main_syntax = 'markdown'
+endif
+
+runtime! syntax/html.vim
+unlet! b:current_syntax
+
+if !exists('g:markdown_fenced_languages')
+ let g:markdown_fenced_languages = []
+endif
+for s:type in map(copy(g:markdown_fenced_languages),'matchstr(v:val,"[^=]*$")')
+ if s:type =~ '\.'
+ let b:{matchstr(s:type,'[^.]*')}_subtype = matchstr(s:type,'\.\zs.*')
+ endif
+ exe 'syn include @markdownHighlight'.substitute(s:type,'\.','','g').' syntax/'.matchstr(s:type,'[^.]*').'.vim'
+ unlet! b:current_syntax
+endfor
+unlet! s:type
+
+syn sync minlines=10
+syn case ignore
+
+syn match markdownValid '[<>]\c[a-z/$!]\@!'
+syn match markdownValid '&\%(#\=\w*;\)\@!'
+
+syn match markdownLineStart "^[<@]\@!" nextgroup=@markdownBlock
+
+syn cluster markdownBlock contains=markdownH1,markdownH2,markdownH3,markdownH4,markdownH5,markdownH6,markdownBlockquote,markdownListMarker,markdownOrderedListMarker,markdownCodeBlock,markdownRule
+syn cluster markdownInline contains=markdownLineBreak,markdownLinkText,markdownItalic,markdownBold,markdownCode,markdownEscape,@htmlTop,markdownError
+
+syn match markdownH1 "^.\+\n=\+$" contained contains=@markdownInline,markdownHeadingRule
+syn match markdownH2 "^.\+\n-\+$" contained contains=@markdownInline,markdownHeadingRule
+
+syn match markdownHeadingRule "^[=-]\+$" contained
+
+syn region markdownH1 matchgroup=markdownHeadingDelimiter start="##\@!" end="#*\s*$" keepend oneline contains=@markdownInline contained
+syn region markdownH2 matchgroup=markdownHeadingDelimiter start="###\@!" end="#*\s*$" keepend oneline contains=@markdownInline contained
+syn region markdownH3 matchgroup=markdownHeadingDelimiter start="####\@!" end="#*\s*$" keepend oneline contains=@markdownInline contained
+syn region markdownH4 matchgroup=markdownHeadingDelimiter start="#####\@!" end="#*\s*$" keepend oneline contains=@markdownInline contained
+syn region markdownH5 matchgroup=markdownHeadingDelimiter start="######\@!" end="#*\s*$" keepend oneline contains=@markdownInline contained
+syn region markdownH6 matchgroup=markdownHeadingDelimiter start="#######\@!" end="#*\s*$" keepend oneline contains=@markdownInline contained
+
+syn match markdownBlockquote ">\s" contained nextgroup=@markdownBlock
+
+syn region markdownCodeBlock start=" \|\t" end="$" contained
+
+" TODO: real nesting
+syn match markdownListMarker "\%(\t\| \{0,4\}\)[-*+]\%(\s\+\S\)\@=" contained
+syn match markdownOrderedListMarker "\%(\t\| \{0,4}\)\<\d\+\.\%(\s*\S\)\@=" contained
+
+syn match markdownRule "\* *\* *\*[ *]*$" contained
+syn match markdownRule "- *- *-[ -]*$" contained
+
+syn match markdownLineBreak " \{2,\}$"
+
+syn region markdownIdDeclaration matchgroup=markdownLinkDelimiter start="^ \{0,3\}!\=\[" end="\]:" oneline keepend nextgroup=markdownUrl skipwhite
+syn match markdownUrl "\S\+" nextgroup=markdownUrlTitle skipwhite contained
+syn region markdownUrl matchgroup=markdownUrlDelimiter start="<" end=">" oneline keepend nextgroup=markdownUrlTitle skipwhite contained
+syn region markdownUrlTitle matchgroup=markdownUrlTitleDelimiter start=+"+ end=+"+ keepend contained
+syn region markdownUrlTitle matchgroup=markdownUrlTitleDelimiter start=+'+ end=+'+ keepend contained
+syn region markdownUrlTitle matchgroup=markdownUrlTitleDelimiter start=+(+ end=+)+ keepend contained
+
+syn region markdownLinkText matchgroup=markdownLinkTextDelimiter start="!\=\[\%(\_[^]]*]\%( \=[[(]\)\)\@=" end="\]\%( \=[[(]\)\@=" keepend nextgroup=markdownLink,markdownId skipwhite contains=@markdownInline,markdownLineStart
+syn region markdownLink matchgroup=markdownLinkDelimiter start="(" end=")" contains=markdownUrl keepend contained
+syn region markdownId matchgroup=markdownIdDelimiter start="\[" end="\]" keepend contained
+syn region markdownAutomaticLink matchgroup=markdownUrlDelimiter start="<\%(\w\+:\|[[:alnum:]_+-]\+@\)\@=" end=">" keepend oneline
+
+syn region markdownItalic start="\S\@<=\*\|\*\S\@=" end="\S\@<=\*\|\*\S\@=" keepend contains=markdownLineStart
+syn region markdownItalic start="\S\@<=_\|_\S\@=" end="\S\@<=_\|_\S\@=" keepend contains=markdownLineStart
+syn region markdownBold start="\S\@<=\*\*\|\*\*\S\@=" end="\S\@<=\*\*\|\*\*\S\@=" keepend contains=markdownLineStart
+syn region markdownBold start="\S\@<=__\|__\S\@=" end="\S\@<=__\|__\S\@=" keepend contains=markdownLineStart
+syn region markdownBoldItalic start="\S\@<=\*\*\*\|\*\*\*\S\@=" end="\S\@<=\*\*\*\|\*\*\*\S\@=" keepend contains=markdownLineStart
+syn region markdownBoldItalic start="\S\@<=___\|___\S\@=" end="\S\@<=___\|___\S\@=" keepend contains=markdownLineStart
+syn region markdownCode matchgroup=markdownCodeDelimiter start="`" end="`" keepend contains=markdownLineStart
+syn region markdownCode matchgroup=markdownCodeDelimiter start="`` \=" end=" \=``" keepend contains=markdownLineStart
+syn region markdownCode matchgroup=markdownCodeDelimiter start="^\s*\zs```.*$" end="^```\ze\s*$" keepend
+
+if main_syntax ==# 'markdown'
+ for s:type in g:markdown_fenced_languages
+ exe 'syn region markdownHighlight'.substitute(matchstr(s:type,'[^=]*$'),'\..*','','').' matchgroup=markdownCodeDelimiter start="^\s*\zs```'.matchstr(s:type,'[^=]*').'\>.*$" end="^```\ze\s*$" keepend contains=@markdownHighlight'.substitute(matchstr(s:type,'[^=]*$'),'\.','','g')
+ endfor
+ unlet! s:type
+endif
+
+syn match markdownEscape "\\[][\\`*_{}()#+.!-]"
+syn match markdownError "\w\@<=_\w\@="
+
+hi def link markdownH1 htmlH1
+hi def link markdownH2 htmlH2
+hi def link markdownH3 htmlH3
+hi def link markdownH4 htmlH4
+hi def link markdownH5 htmlH5
+hi def link markdownH6 htmlH6
+hi def link markdownHeadingRule markdownRule
+hi def link markdownHeadingDelimiter Delimiter
+hi def link markdownOrderedListMarker markdownListMarker
+hi def link markdownListMarker htmlTagName
+hi def link markdownBlockquote Comment
+hi def link markdownRule PreProc
+
+hi def link markdownLinkText htmlLink
+hi def link markdownIdDeclaration Typedef
+hi def link markdownId Type
+hi def link markdownAutomaticLink markdownUrl
+hi def link markdownUrl Float
+hi def link markdownUrlTitle String
+hi def link markdownIdDelimiter markdownLinkDelimiter
+hi def link markdownUrlDelimiter htmlTag
+hi def link markdownUrlTitleDelimiter Delimiter
+
+hi def link markdownItalic htmlItalic
+hi def link markdownBold htmlBold
+hi def link markdownBoldItalic htmlBoldItalic
+hi def link markdownCodeDelimiter Delimiter
+
+hi def link markdownEscape Special
+hi def link markdownError Error
+
+let b:current_syntax = "markdown"
+
+" vim:set sw=2:
diff --git a/base/vim/syntax/mediawiki.vim b/base/vim/syntax/mediawiki.vim
new file mode 100644
index 0000000..5551dc5
--- /dev/null
+++ b/base/vim/syntax/mediawiki.vim
@@ -0,0 +1,293 @@
+" mediawiki.vim (formerly named Wikipedia.vim)
+"
+" Vim syntax file
+" Language: MediaWiki, http://www.mediawiki.org/
+" Maintainer: This syntax file needs a maintainer in order to ship
+" with Vim. Please contact [[User:Unforgettableid]] if you want
+" to volunteer.
+" Home: http://en.wikipedia.org/wiki/Wikipedia:Text_editor_support#Vim
+" Last Change: 2011 Sep 19
+" Credits: [[User:Aepd87]], [[User:Danny373]], [[User:Ingo Karkat]], et al.
+"
+" Published on Wikipedia in 2003-04 and declared authorless.
+"
+" Based on the HTML syntax file. Probably too closely based, in fact.
+" There may well be name collisions everywhere, but ignorance is bliss,
+" so they say.
+"
+" To do: plug-in support for downloading and uploading to the server.
+
+if !exists("main_syntax")
+ if version < 600
+ syntax clear
+ elseif exists("b:current_syntax")
+ finish
+ endif
+ let main_syntax = "html"
+endif
+
+syntax case ignore
+if v:version >= 700
+ syntax spell toplevel
+endif
+
+" Mark illegal characters
+sy match htmlError "[<>&]"
+
+" Tags
+sy region htmlString contained start=+"+ end=+"+ contains=htmlSpecialChar,@htmlPreproc
+sy region htmlString contained start=+'+ end=+'+ contains=htmlSpecialChar,@htmlPreproc
+sy match htmlValue contained "=[\t ]*[^'" \t>][^ \t>]*"hs=s+1 contains=@htmlPreproc
+sy region htmlEndTag start=+</+ end=+>+ contains=htmlTagN,htmlTagError
+sy region htmlTag start=+<[^/]+ end=+>+ contains=htmlTagN,htmlString,htmlArg,htmlValue,htmlTagError,htmlEvent,htmlCssDefinition,@htmlPreproc,@htmlArgCluster
+sy match htmlTagN contained +<\s*[-a-zA-Z0-9]\++hs=s+1 contains=htmlTagName,htmlSpecialTagName,@htmlTagNameCluster
+sy match htmlTagN contained +</\s*[-a-zA-Z0-9]\++hs=s+2 contains=htmlTagName,htmlSpecialTagName,@htmlTagNameCluster
+sy match htmlTagError contained "[^>]<"ms=s+1
+
+" Allowed HTML tag names
+sy keyword htmlTagName contained big blockquote br caption center cite code
+sy keyword htmlTagName contained dd del div dl dt font hr ins li
+sy keyword htmlTagName contained ol p pre rb rp rt ruby s small span strike sub
+sy keyword htmlTagName contained sup table td th tr tt ul var
+sy match htmlTagName contained "\<\(b\|i\|u\|h[1-6]\|em\|strong\)\>"
+" Allowed Wiki tag names
+sy keyword htmlTagName contained math nowiki references source syntaxhighlight
+
+" Allowed arg names
+sy keyword htmlArg contained align lang dir width height nowrap bgcolor clear
+sy keyword htmlArg contained noshade cite datetime size face color type start
+sy keyword htmlArg contained value compact summary border frame rules
+sy keyword htmlArg contained cellspacing cellpadding valign char charoff
+sy keyword htmlArg contained colgroup col span abbr axis headers scope rowspan
+sy keyword htmlArg contained colspan id class name style title
+
+" Special characters
+sy match htmlSpecialChar "&#\=[0-9A-Za-z]\{1,8};"
+
+" Comments
+sy region htmlComment start=+<!+ end=+>+ contains=htmlCommentPart,htmlCommentError
+sy match htmlCommentError contained "[^><!]"
+sy region htmlCommentPart contained start=+--+ end=+--\s*+ contains=@htmlPreProc
+sy region htmlComment start=+<!DOCTYPE+ keepend end=+>+
+
+if !exists("html_no_rendering")
+ sy cluster htmlTop contains=@Spell,htmlTag,htmlEndTag,htmlSpecialChar,htmlPreProc,htmlComment,htmlLink,@htmlPreproc
+
+ sy region htmlBold start="<b\>" end="</b>"me=e-4 contains=@htmlTop,htmlBoldUnderline,htmlBoldItalic
+ sy region htmlBold start="<strong\>" end="</strong>"me=e-9 contains=@htmlTop,htmlBoldUnderline,htmlBoldItalic
+ sy region htmlBoldUnderline contained start="<u\>" end="</u>"me=e-4 contains=@htmlTop,htmlBoldUnderlineItalic
+ sy region htmlBoldItalic contained start="<i\>" end="</i>"me=e-4 contains=@htmlTop,htmlBoldItalicUnderline
+ sy region htmlBoldItalic contained start="<em\>" end="</em>"me=e-5 contains=@htmlTop,htmlBoldItalicUnderline
+ sy region htmlBoldUnderlineItalic contained start="<i\>" end="</i>"me=e-4 contains=@htmlTop
+ sy region htmlBoldUnderlineItalic contained start="<em\>" end="</em>"me=e-5 contains=@htmlTop
+ sy region htmlBoldItalicUnderline contained start="<u\>" end="</u>"me=e-4 contains=@htmlTop,htmlBoldUnderlineItalic
+
+ sy region htmlUnderline start="<u\>" end="</u>"me=e-4 contains=@htmlTop,htmlUnderlineBold,htmlUnderlineItalic
+ sy region htmlUnderlineBold contained start="<b\>" end="</b>"me=e-4 contains=@htmlTop,htmlUnderlineBoldItalic
+ sy region htmlUnderlineBold contained start="<strong\>" end="</strong>"me=e-9 contains=@htmlTop,htmlUnderlineBoldItalic
+ sy region htmlUnderlineItalic contained start="<i\>" end="</i>"me=e-4 contains=@htmlTop,htmlUnderlineItalicBold
+ sy region htmlUnderlineItalic contained start="<em\>" end="</em>"me=e-5 contains=@htmlTop,htmlUnderlineItalicBold
+ sy region htmlUnderlineItalicBold contained start="<b\>" end="</b>"me=e-4 contains=@htmlTop
+ sy region htmlUnderlineItalicBold contained start="<strong\>" end="</strong>"me=e-9 contains=@htmlTop
+ sy region htmlUnderlineBoldItalic contained start="<i\>" end="</i>"me=e-4 contains=@htmlTop
+ sy region htmlUnderlineBoldItalic contained start="<em\>" end="</em>"me=e-5 contains=@htmlTop
+
+ sy region htmlItalic start="<i\>" end="</i>"me=e-4 contains=@htmlTop,htmlItalicBold,htmlItalicUnderline
+ sy region htmlItalic start="<em\>" end="</em>"me=e-5 contains=@htmlTop
+ sy region htmlItalicBold contained start="<b\>" end="</b>"me=e-4 contains=@htmlTop,htmlItalicBoldUnderline
+ sy region htmlItalicBold contained start="<strong\>" end="</strong>"me=e-9 contains=@htmlTop,htmlItalicBoldUnderline
+ sy region htmlItalicBoldUnderline contained start="<u\>" end="</u>"me=e-4 contains=@htmlTop
+ sy region htmlItalicUnderline contained start="<u\>" end="</u>"me=e-4 contains=@htmlTop,htmlItalicUnderlineBold
+ sy region htmlItalicUnderlineBold contained start="<b\>" end="</b>"me=e-4 contains=@htmlTop
+ sy region htmlItalicUnderlineBold contained start="<strong\>" end="</strong>"me=e-9 contains=@htmlTop
+
+ sy region htmlH1 start="<h1\>" end="</h1>"me=e-5 contains=@htmlTop
+ sy region htmlH2 start="<h2\>" end="</h2>"me=e-5 contains=@htmlTop
+ sy region htmlH3 start="<h3\>" end="</h3>"me=e-5 contains=@htmlTop
+ sy region htmlH4 start="<h4\>" end="</h4>"me=e-5 contains=@htmlTop
+ sy region htmlH5 start="<h5\>" end="</h5>"me=e-5 contains=@htmlTop
+ sy region htmlH6 start="<h6\>" end="</h6>"me=e-5 contains=@htmlTop
+endif
+
+
+" No htmlTop and wikiPre inside HTML preformatted areas, because
+" MediaWiki renders everything in there literally (HTML tags and
+" entities, too): <pre> tags work as the combination of <nowiki> and
+" the standard HTML <pre> tag: the content will preformatted, and it
+" will not be parsed, but shown as in the wikitext source.
+"
+" With wikiPre, indented lines would be rendered differently from
+" unindented lines.
+sy match htmlPreTag /<pre>/ contains=htmlTag
+sy match htmlPreEndTag /<\/pre>/ contains=htmlEndTag
+sy match wikiNowikiTag /<nowiki>/ contains=htmlTag
+sy match wikiNowikiEndTag /<\/nowiki>/ contains=htmlEndTag
+sy match wikiSourceTag /<source\s\+[^>]\+>/ contains=htmlTag
+sy match wikiSourceEndTag /<\/source>/ contains=htmlEndTag
+sy match wikiSyntaxHLTag /<syntaxhighlight\s\+[^>]\+>/ contains=htmlTag
+sy match wikiSyntaxHLEndTag /<\/syntaxhighlight>/ contains=htmlEndTag
+
+" Note: Cannot use 'start="<pre>"rs=e', so still have the <pre> tag
+" highlighted correctly via separate sy-match. Unfortunately, this will
+" also highlight <pre> tags inside the preformatted region.
+sy region htmlPre start="<pre>" end="<\/pre>"me=e-6 contains=htmlPreTag
+sy region wikiNowiki start="<nowiki>" end="<\/nowiki>"me=e-9 contains=wikiNowikiTag
+sy region wikiSource start="<source\s\+[^>]\+>" keepend end="<\/source>"me=e-9 contains=wikiSourceTag
+sy region wikiSyntaxHL start="<syntaxhighlight\s\+[^>]\+>" keepend end="<\/syntaxhighlight>"me=e-18 contains=wikiSyntaxHLTag
+
+sy include @TeX syntax/tex.vim
+sy region wikiTeX matchgroup=htmlTag start="<math>" end="<\/math>" contains=@texMathZoneGroup,wikiNowiki,wikiNowikiEndTag
+sy region wikiRef matchgroup=htmlTag start="<ref>" end="<\/ref>" contains=wikiNowiki,wikiNowikiEndTag
+
+sy cluster wikiTop contains=@Spell,wikiLink,wikiNowiki,wikiNowikiEndTag
+
+sy region wikiItalic start=+'\@<!'''\@!+ end=+''+ oneline contains=@wikiTop,wikiItalicBold
+sy region wikiBold start=+'''+ end=+'''+ oneline contains=@wikiTop,wikiBoldItalic
+sy region wikiBoldAndItalic start=+'''''+ end=+'''''+ oneline contains=@wikiTop
+
+sy region wikiBoldItalic contained start=+'\@<!'''\@!+ end=+''+ oneline contains=@wikiTop
+sy region wikiItalicBold contained start=+'''+ end=+'''+ oneline contains=@wikiTop
+
+sy region wikiH1 start="^=" end="=" oneline contains=@wikiTop
+sy region wikiH2 start="^==" end="==" oneline contains=@wikiTop
+sy region wikiH3 start="^===" end="===" oneline contains=@wikiTop
+sy region wikiH4 start="^====" end="====" oneline contains=@wikiTop
+sy region wikiH5 start="^=====" end="=====" oneline contains=@wikiTop
+sy region wikiH6 start="^======" end="======" oneline contains=@wikiTop
+
+sy region wikiLink start="\[\[" end="\]\]\(s\|'s\|es\|ing\|\)" oneline contains=wikiLink,wikiNowiki,wikiNowikiEndTag
+
+sy region wikiLink start="\[http:" end="\]" oneline contains=wikiNowiki,wikiNowikiEndTag
+sy region wikiLink start="\[https:" end="\]" oneline contains=wikiNowiki,wikiNowikiEndTag
+sy region wikiLink start="\[ftp:" end="\]" oneline contains=wikiNowiki,wikiNowikiEndTag
+sy region wikiLink start="\[gopher:" end="\]" oneline contains=wikiNowiki,wikiNowikiEndTag
+sy region wikiLink start="\[news:" end="\]" oneline contains=wikiNowiki,wikiNowikiEndTag
+sy region wikiLink start="\[mailto:" end="\]" oneline contains=wikiNowiki,wikiNowikiEndTag
+
+sy region wikiTemplate start="{{" end="}}" contains=wikiNowiki,wikiNowikiEndTag
+
+sy match wikiParaFormatChar /^[\:|\*|;|#]\+/
+sy match wikiParaFormatChar /^-----*/
+sy match wikiPre /^\ .*$/ contains=wikiNowiki,wikiNowikiEndTag
+
+
+" HTML highlighting
+
+if version < 508
+ command! -nargs=+ HtmlHiLink hi link <args>
+else
+ command! -nargs=+ HtmlHiLink hi def link <args>
+endif
+
+if version >= 508 || !exists("did_html_syn_inits")
+ HtmlHiLink htmlTag Function
+ HtmlHiLink htmlEndTag Identifier
+ HtmlHiLink htmlArg Type
+ HtmlHiLink htmlTagName htmlStatement
+ HtmlHiLink htmlSpecialTagName Exception
+ HtmlHiLink htmlValue String
+ HtmlHiLink htmlSpecialChar Special
+
+ if !exists("html_no_rendering")
+ HtmlHiLink htmlTitle Title
+ HtmlHiLink htmlH1 htmlTitle
+ HtmlHiLink htmlH2 htmlTitle
+ HtmlHiLink htmlH3 htmlTitle
+ HtmlHiLink htmlH4 htmlTitle
+ HtmlHiLink htmlH5 htmlTitle
+ HtmlHiLink htmlH6 htmlTitle
+
+ HtmlHiLink htmlPreProc PreProc
+ HtmlHiLink htmlHead htmlPreProc
+ HtmlHiLink htmlPreProcAttrName htmlPreProc
+ HtmlHiLink htmlPreStmt htmlPreProc
+
+ HtmlHiLink htmlSpecial Special
+ HtmlHiLink htmlCssDefinition htmlSpecial
+ HtmlHiLink htmlEvent htmlSpecial
+ HtmlHiLink htmlSpecialChar htmlSpecial
+
+ HtmlHiLink htmlComment Comment
+ HtmlHiLink htmlCommentPart htmlComment
+ HtmlHiLink htmlCssStyleComment htmlComment
+
+ HtmlHiLink htmlString String
+ HtmlHiLink htmlPreAttr htmlString
+ HtmlHiLink htmlValue htmlString
+
+ HtmlHiLink htmlError Error
+ HtmlHiLink htmlBadArg htmlError
+ HtmlHiLink htmlBadTag htmlError
+ HtmlHiLink htmlCommentError htmlError
+ HtmlHiLink htmlPreError htmlError
+ HtmlHiLink htmlPreProcAttrError htmlError
+ HtmlHiLink htmlTagError htmlError
+
+ HtmlHiLink htmlStatement Statement
+
+ HtmlHiLink htmlConstant Constant
+
+ HtmlHiLink htmlBoldItalicUnderline htmlBoldUnderlineItalic
+ HtmlHiLink htmlUnderlineItalicBold htmlBoldUnderlineItalic
+ HtmlHiLink htmlUnderlineBoldItalic htmlBoldUnderlineItalic
+ HtmlHiLink htmlItalicBoldUnderline htmlBoldUnderlineItalic
+ HtmlHiLink htmlItalicUnderlineBold htmlBoldUnderlineItalic
+
+ HtmlHiLink htmlItalicBold htmlBoldItalic
+ HtmlHiLink htmlItalicUnderline htmlUnderlineItalic
+ HtmlHiLink htmlUnderlineBold htmlBoldUnderline
+
+ HtmlHiLink htmlLink Underlined
+
+ if !exists("html_my_rendering")
+ hi def htmlBold term=bold cterm=bold gui=bold
+ hi def htmlBoldUnderline term=bold,underline cterm=bold,underline gui=bold,underline
+ hi def htmlBoldItalic term=bold,italic cterm=bold,italic gui=bold,italic
+ hi def htmlBoldUnderlineItalic term=bold,italic,underline cterm=bold,italic,underline gui=bold,italic,underline
+ hi def htmlUnderline term=underline cterm=underline gui=underline
+ hi def htmlUnderlineItalic term=italic,underline cterm=italic,underline gui=italic,underline
+ hi def htmlItalic term=italic cterm=italic gui=italic
+ endif
+
+ endif " !exists("html_no_rendering")
+
+ if version < 508
+ let did_html_syn_inits = 1
+ endif
+
+endif " version >= 508 || !exists("did_html_syn_inits")
+
+" Wiki highlighting
+
+HtmlHiLink wikiItalic htmlItalic
+HtmlHiLink wikiBold htmlBold
+HtmlHiLink wikiBoldItalic htmlBoldItalic
+HtmlHiLink wikiItalicBold htmlBoldItalic
+HtmlHiLink wikiBoldAndItalic htmlBoldItalic
+
+HtmlHiLink wikiH1 htmlTitle
+HtmlHiLink wikiH2 htmlTitle
+HtmlHiLink wikiH3 htmlTitle
+HtmlHiLink wikiH4 htmlTitle
+HtmlHiLink wikiH5 htmlTitle
+HtmlHiLink wikiH6 htmlTitle
+
+HtmlHiLink wikiLink htmlLink
+HtmlHiLink wikiTemplate htmlSpecial
+HtmlHiLink wikiParaFormatChar htmlSpecial
+HtmlHiLink wikiPre htmlConstant
+HtmlHiLink wikiRef htmlComment
+
+HtmlHiLink wikiSource wikiPre
+HtmlHiLink wikiSyntaxHL wikiPre
+
+
+let b:current_syntax = "html"
+
+delcommand HtmlHiLink
+
+if main_syntax == "html"
+ unlet main_syntax
+endif
+
+" vim: set et sts=2 sw=2:
diff --git a/base/vim/syntax/php.vim b/base/vim/syntax/php.vim
new file mode 100644
index 0000000..981ba43
--- /dev/null
+++ b/base/vim/syntax/php.vim
@@ -0,0 +1 @@
+setl ts=4 sw=4 sts=4
diff --git a/base/vimrc b/base/vimrc
new file mode 100644
index 0000000..8897bff
--- /dev/null
+++ b/base/vimrc
@@ -0,0 +1,151 @@
+" Setting editor defaults
+set nu
+syntax on
+set bg=dark
+set diffopt=filler,iwhite " keep files synced and ignore whitespace
+set expandtab " Get rid of tabs altogether and replace with spaces
+"set guioptions-=m " Remove menu from the gui
+set guioptions-=T " Remove toolbar
+set hidden " hide buffers instead of closing
+set history=50 " keep 50 lines of command line history
+set ignorecase " Do case insensitive matching
+set smartcase " Ignore case only when doing a lowercase search
+set incsearch " Incremental search
+set linebreak " This displays long lines as wrapped at word boundries
+set matchtime=10 " Time to flash the brack with showmatch
+set nobackup " Don't keep a backup file
+set nocompatible " Use Vim defaults (much better!)
+set hlsearch " Use highlighted search (I am blind)
+set t_Co=256 " Set terminal to use 256 colors
+set backspace=indent,eol,start
+set clipboard=unnamed " Use system clipboard for all cut/copy operations
+set ruler
+set foldmethod=indent
+set nofen
+set mouse=a
+set spell spelllang=en
+set wildmode=list:longest,full
+set nomousehide
+set sessionoptions+=resize,winpos
+set listchars=tab:>-,trail:.
+set list
+set colorcolumn=100,+0
+set guifont=Inconsolata\ Medium\ 12
+
+" Status Line Settings
+set laststatus=2 " always have status bar
+set statusline=
+set statusline+=%n\
+set statusline+=%<%f\ %h%m%r
+set statusline+=%=
+set statusline+=%-14.(%l,%c%V%)\ %P
+
+" Emacs-like bindings in the command line
+cnoremap <C-a> <Home>
+cnoremap <C-e> <End>
+cnoremap <C-p> <Up>
+cnoremap <C-n> <Down>
+cnoremap <C-b> <Left>
+cnoremap <C-f> <Right>
+cnoremap <M-b> <S-Left>
+cnoremap <M-f> <S-Right>
+
+" filetypes
+filetype plugin on
+filetype indent on
+filetype on
+
+"Set colorscheme.
+colorscheme slate
+highlight ColorColumn ctermbg=DarkGrey guibg=DarkGrey
+
+" Turn off highlighting after search
+map ,, :nohl<CR>
+
+" Remove all trailing whitespaces
+map <Leader>s :%s/\s\+$//g <cr>
+
+" toggle line number with F11 or Ctrl-L
+map <F11> : set number! <cr>
+map <C-L> : set number! <cr>
+
+" Move between split windows
+map O5B <C-W>j
+map O5A <C-W>k
+map O5D <C-W>h
+map O5C <C-W>l
+imap O5B <C-W>j
+imap O5A <C-W>k
+imap O5D <C-W>h
+imap O5C <C-W>l
+
+"Set tab to 4 spaces
+set shiftwidth=4
+set softtabstop=4
+set tabstop=4
+set smarttab
+
+" Paste from GUI into vim and not lose indendation
+" F7 to toggle paste mode
+map <F7> :set invpaste<CR>
+set pastetoggle=<F7>
+
+"normal mode maps
+
+" Switch to/from header file
+map <F4> :e %:p:s,.h$,.X123X,:s,.cpp$,.h,:s,.X123X$,.cpp,<CR>
+
+"Map \e to edit a file from the directory of the current buffer
+if has("unix")
+ nmap <Leader>e :e <C-R>=expand("%:p:h") . "/"<CR>
+else
+ nmap <Leader>,e :e <C-R>=expand("%:p:h") . "\\"<CR>
+endif
+
+cabbr <expr> %% expand('%:p:h')
+
+"When editing a file, make screen display the name of the file you are editing
+function! SetTitle()
+ if $TERM =~ "^screen"
+ let l:title = 'vi: ' . expand('%:t')
+
+ if (l:title != 'vi: __Tag_List__')
+ let l:truncTitle = strpart(l:title, 0, 15)
+ silent exe '!echo -e -n "\033k' . l:truncTitle . '\033\\"'
+ endif
+ endif
+endfunction
+
+" Run it every time we change buffers
+autocmd BufEnter,BufFilePost * call SetTitle()
+
+" Scratch Plugin
+let g:scratchBackupFile="/tmp/scratch.txt"
+
+" Quicker compile & check
+map <C-x> :silent make<Enter>:copen<Enter><C-w><C-w>:redraw<Enter>
+
+let g:GPGFilePattern = '*.\(gpg\|pgp\)'
+
+execute pathogen#infect()
+
+" Spell Check colors
+highlight clear SpellBad
+highlight SpellBad term=standout ctermfg=1 term=underline cterm=underline
+highlight clear SpellCap
+highlight SpellCap term=underline cterm=underline
+highlight clear SpellRare
+highlight SpellRare term=underline cterm=underline
+highlight clear SpellLocal
+highlight SpellLocal term=underline cterm=underline
+
+" ctrlp
+let g:ctrlp_extensions = ['tag', 'buffertag']
+map <C-r> :CtrlPTag<Enter>
+map <C-y> :redo<Enter>
+set scroll=2
+
+" Load host-specific settings
+set runtimepath^=~/.host-specific/vim,
+set runtimepath+=~/.host-specific/vim/after
+source ~/.host-specific/vimrc
diff --git a/base/weatherrc b/base/weatherrc
new file mode 100644
index 0000000..d14c702
--- /dev/null
+++ b/base/weatherrc
@@ -0,0 +1,6 @@
+[default]
+alert = False
+city = Seattle
+id = KSEA
+metric = True
+zones = WAZ508,WAZ505,WAZ509
diff --git a/base/xsession b/base/xsession
new file mode 100644
index 0000000..fbb69ff
--- /dev/null
+++ b/base/xsession
@@ -0,0 +1,55 @@
+# Set my editor and terminal
+export TERMINAL=urxvtc
+export EDITOR=vim
+export LANG=en_US.utf8
+export LC_ALL=en_US.utf8
+
+systemctl --user import-environment DISPLAY PATH
+
+if [ -f ~/.shell_pathes ]; then
+ source ~/.shell_pathes
+fi
+
+# Host Specific Settings
+if [ -f ~/.host-specific/shell_pathes ]; then
+ source ~/.host-specific/shell_pathes
+fi
+
+# Some X Stuff
+xsetroot -solid "#111"
+ulimit -c unlimited
+setxkbmap -option compose:menu
+
+# Startup applications
+xrandr --dpi 96
+xrdb -merge $HOME/.Xresources
+#/usr/lib/deja-dup/deja-dup/deja-dup-monitor &
+#/usr/lib/gnome-settings-daemon/gnome-settings-daemon &
+/usr/lib/notification-daemon/notification-daemon &
+(sleep 8; start-pulseaudio-x11) &
+#(sleep 60; update-notifier) &
+#(sleep 5; gnome-sound-applet) &
+(sleep 5; nm-applet) &
+(sleep 5; system-config-printer-applet) &
+#(sleep 5; nitrogen --restore) &
+#gtk-theme-switch2 -i Ambiance
+(sleep 10; redshift-gtk) &
+#(sleep 10; dropbox start) &
+
+# Disable bell
+xset -b
+
+# Host Specific Settings
+if [ -f ~/.host-specific/xsession ]; then
+ source ~/.host-specific/xsession
+fi
+
+# Start terminal daemon
+urxvtd -q -f -o
+
+# Make the i3 config
+
+make_i3_config > $HOME/.config/i3/config
+
+
+exec i3
diff --git a/base/zshrc b/base/zshrc
new file mode 100644
index 0000000..e69dec8
--- /dev/null
+++ b/base/zshrc
@@ -0,0 +1,53 @@
+#
+# .zshrc is sourced in interactive shells.
+# It should contain commands to set up aliases,
+# functions, options, key bindings, etc.
+#
+
+# Load this first or the work desktop cries
+source ~/.host-specific/zshrc
+
+autoload -U compinit
+compinit
+
+#allow tab completion in the middle of a word
+setopt COMPLETE_IN_WORD
+
+## keep background processes at full speed
+#setopt NOBGNICE
+## restart running processes on exit
+#setopt HUP
+
+## history
+#setopt APPEND_HISTORY
+## for sharing history between zsh processes
+#setopt INC_APPEND_HISTORY
+#setopt SHARE_HISTORY
+
+## never ever beep ever
+#setopt NO_BEEP
+
+## automatically decide when to page a list of completions
+#LISTMAX=0
+
+## disable mail checking
+#MAILCHECK=0
+
+# autoload -U colors
+colors
+
+# Fix keys
+bindkey 'OH' beginning-of-line
+bindkey '[1~' beginning-of-line
+bindkey 'OF' end-of-line
+bindkey '[4~' end-of-line
+bindkey '[3~' delete-char
+
+zstyle ':completion:*' completer _complete _ignored _files
+
+source ~/.bash_aliases
+source ~/.host-specific/bash_aliases
+
+[[ -a ~/.shell_pathes ]] && source ~/.shell_pathes
+[[ -a ~/.host-specific/shell_pathes ]] && source ~/.host-specific/shell_pathes
+
diff --git a/host-overrides/bismuth.jesterpm.net/config/.nolink b/host-overrides/bismuth.jesterpm.net/config/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/host-overrides/bismuth.jesterpm.net/config/.nolink
diff --git a/host-overrides/bismuth.jesterpm.net/config/i3/.nolink b/host-overrides/bismuth.jesterpm.net/config/i3/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/host-overrides/bismuth.jesterpm.net/config/i3/.nolink
diff --git a/host-overrides/bismuth.jesterpm.net/config/i3/autostart.d/.nolink b/host-overrides/bismuth.jesterpm.net/config/i3/autostart.d/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/host-overrides/bismuth.jesterpm.net/config/i3/autostart.d/.nolink
diff --git a/host-overrides/bismuth.jesterpm.net/config/i3/autostart.d/10_xautolock b/host-overrides/bismuth.jesterpm.net/config/i3/autostart.d/10_xautolock
new file mode 100644
index 0000000..ef9dc0c
--- /dev/null
+++ b/host-overrides/bismuth.jesterpm.net/config/i3/autostart.d/10_xautolock
@@ -0,0 +1,2 @@
+exec --no-startup-id xautolock -time 20 -locker lock -corners 0-00
+
diff --git a/host-overrides/bismuth.jesterpm.net/config/i3/config.d/.nolink b/host-overrides/bismuth.jesterpm.net/config/i3/config.d/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/host-overrides/bismuth.jesterpm.net/config/i3/config.d/.nolink
diff --git a/host-overrides/bismuth.jesterpm.net/config/i3/config.d/50_bismuth.jesterpm.net b/host-overrides/bismuth.jesterpm.net/config/i3/config.d/50_bismuth.jesterpm.net
new file mode 100644
index 0000000..bd87162
--- /dev/null
+++ b/host-overrides/bismuth.jesterpm.net/config/i3/config.d/50_bismuth.jesterpm.net
@@ -0,0 +1,7 @@
+## This config is specific to Bismuth
+
+workspace "1: irssi" output VGA-0
+workspace "2: web" output VGA-0
+workspace "3: dev" output VGA-0
+workspace "10: April" output VGA-0
+
diff --git a/host-overrides/cadmium.jesterpm.net/config/.nolink b/host-overrides/cadmium.jesterpm.net/config/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/host-overrides/cadmium.jesterpm.net/config/.nolink
diff --git a/host-overrides/cadmium.jesterpm.net/config/grobi.conf b/host-overrides/cadmium.jesterpm.net/config/grobi.conf
new file mode 100644
index 0000000..c52ad07
--- /dev/null
+++ b/host-overrides/cadmium.jesterpm.net/config/grobi.conf
@@ -0,0 +1,11 @@
+# vim:ft=yaml
+
+rules:
+ - name: Desk
+ outputs_connected:
+ - DP-1-DEL-41183-810956108-DELL U3417W-FR3PK8CI0V5L
+ configure_command: xrandr --output eDP-1 --mode 1920x1080 --pos 768x1440 --output DP-1 --auto --pos 0x0 --primary
+
+ - name: Fallback
+ primary: eDP-1
+ configure_single: eDP-1@1920x1080
diff --git a/host-overrides/cadmium.jesterpm.net/config/i3/.nolink b/host-overrides/cadmium.jesterpm.net/config/i3/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/host-overrides/cadmium.jesterpm.net/config/i3/.nolink
diff --git a/host-overrides/cadmium.jesterpm.net/config/i3/autostart.d/.nolink b/host-overrides/cadmium.jesterpm.net/config/i3/autostart.d/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/host-overrides/cadmium.jesterpm.net/config/i3/autostart.d/.nolink
diff --git a/host-overrides/cadmium.jesterpm.net/config/i3/config.d/.nolink b/host-overrides/cadmium.jesterpm.net/config/i3/config.d/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/host-overrides/cadmium.jesterpm.net/config/i3/config.d/.nolink
diff --git a/host-overrides/cadmium.jesterpm.net/config/systemd/.nolink b/host-overrides/cadmium.jesterpm.net/config/systemd/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/host-overrides/cadmium.jesterpm.net/config/systemd/.nolink
diff --git a/host-overrides/cadmium.jesterpm.net/config/systemd/user/.nolink b/host-overrides/cadmium.jesterpm.net/config/systemd/user/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/host-overrides/cadmium.jesterpm.net/config/systemd/user/.nolink
diff --git a/host-overrides/cadmium.jesterpm.net/config/systemd/user/grobi.service b/host-overrides/cadmium.jesterpm.net/config/systemd/user/grobi.service
new file mode 100644
index 0000000..38f8543
--- /dev/null
+++ b/host-overrides/cadmium.jesterpm.net/config/systemd/user/grobi.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=grobi display auto config daemon
+
+[Service]
+Type=simple
+ExecStart=/bin/sh -c "grobi watch -v"
+Restart=always
+RestartSec=2s
+
+[Install]
+WantedBy=default.target
diff --git a/host-overrides/jesterpm.net/config/.nolink b/host-overrides/jesterpm.net/config/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/host-overrides/jesterpm.net/config/.nolink
diff --git a/host-overrides/jesterpm.net/config/i3/.nolink b/host-overrides/jesterpm.net/config/i3/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/host-overrides/jesterpm.net/config/i3/.nolink
diff --git a/host-overrides/jesterpm.net/config/i3/autostart.d/.nolink b/host-overrides/jesterpm.net/config/i3/autostart.d/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/host-overrides/jesterpm.net/config/i3/autostart.d/.nolink
diff --git a/host-overrides/jesterpm.net/config/i3/config.d/.nolink b/host-overrides/jesterpm.net/config/i3/config.d/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/host-overrides/jesterpm.net/config/i3/config.d/.nolink
diff --git a/host-overrides/jesterpm.net/config/systemd/.nolink b/host-overrides/jesterpm.net/config/systemd/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/host-overrides/jesterpm.net/config/systemd/.nolink
diff --git a/host-overrides/jesterpm.net/config/systemd/user/.nolink b/host-overrides/jesterpm.net/config/systemd/user/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/host-overrides/jesterpm.net/config/systemd/user/.nolink
diff --git a/host-overrides/jesterpm.net/config/systemd/user/dyndns.service b/host-overrides/jesterpm.net/config/systemd/user/dyndns.service
new file mode 100644
index 0000000..574ae0f
--- /dev/null
+++ b/host-overrides/jesterpm.net/config/systemd/user/dyndns.service
@@ -0,0 +1,7 @@
+[Unit]
+Description=DynDNS Service
+After=network.target network-online.target dbus.socket
+
+[Service]
+Type=oneshot
+ExecStart=/home/jesterpm/bin/update-dyndns
diff --git a/host-overrides/jesterpm.net/config/systemd/user/dyndns.timer b/host-overrides/jesterpm.net/config/systemd/user/dyndns.timer
new file mode 100644
index 0000000..53aab7f
--- /dev/null
+++ b/host-overrides/jesterpm.net/config/systemd/user/dyndns.timer
@@ -0,0 +1,10 @@
+[Unit]
+Description=DynDNS Service
+
+[Timer]
+OnCalendar=*-*-* *:00/5:00
+Persistent=true
+Unit=dyndns.service
+
+[Install]
+WantedBy=timers.target
diff --git a/host-overrides/jesterpm.net/config/systemd/user/mbsync.service b/host-overrides/jesterpm.net/config/systemd/user/mbsync.service
new file mode 100644
index 0000000..e35116f
--- /dev/null
+++ b/host-overrides/jesterpm.net/config/systemd/user/mbsync.service
@@ -0,0 +1,7 @@
+[Unit]
+Description=Mailbox synchronization service
+
+[Service]
+Type=oneshot
+ExecStart=/usr/bin/mbsync -Va
+ExecStartPost=/usr/bin/notmuch new
diff --git a/host-overrides/jesterpm.net/config/systemd/user/mbsync.timer b/host-overrides/jesterpm.net/config/systemd/user/mbsync.timer
new file mode 100644
index 0000000..35a0ada
--- /dev/null
+++ b/host-overrides/jesterpm.net/config/systemd/user/mbsync.timer
@@ -0,0 +1,10 @@
+[Unit]
+Description=Mailbox synchronization timer
+
+[Timer]
+OnBootSec=2m
+OnUnitActiveSec=1m
+Unit=mbsync.service
+
+[Install]
+WantedBy=timers.target
diff --git a/host-overrides/jesterpm.net/gitconfig b/host-overrides/jesterpm.net/gitconfig
new file mode 100644
index 0000000..c31848e
--- /dev/null
+++ b/host-overrides/jesterpm.net/gitconfig
@@ -0,0 +1,14 @@
+[user]
+ name = Jesse Morgan
+ email = jesse@jesterpm.net
+ signkey = 08311F3C
+[color]
+ ui = true
+[alias]
+ lg = log --decorate --graph
+[push]
+ default = simple
+[commit]
+ gpgsign = true
+[init]
+ defaultBranch = master
diff --git a/host-overrides/jesterpm.net/host-specific/bash_aliases b/host-overrides/jesterpm.net/host-specific/bash_aliases
new file mode 100644
index 0000000..0afc370
--- /dev/null
+++ b/host-overrides/jesterpm.net/host-specific/bash_aliases
@@ -0,0 +1 @@
+alias open=xdg-open
diff --git a/host-overrides/jesterpm.net/host-specific/bashrc b/host-overrides/jesterpm.net/host-specific/bashrc
new file mode 100644
index 0000000..295e969
--- /dev/null
+++ b/host-overrides/jesterpm.net/host-specific/bashrc
@@ -0,0 +1,10 @@
+#
+# Settings specific to my personal machines
+#
+
+# Mail Stuff
+export MAILUSER="jesse"
+export MAILNAME="Jesse Morgan"
+export MAILHOST="jesterpm.net"
+export MAIL=$HOME/.maildir/INBOX
+export MAILDIR=$MAIL
diff --git a/host-overrides/jesterpm.net/host-specific/muttrc b/host-overrides/jesterpm.net/host-specific/muttrc
new file mode 100644
index 0000000..637ce85
--- /dev/null
+++ b/host-overrides/jesterpm.net/host-specific/muttrc
@@ -0,0 +1,11 @@
+# Mutt settings specific to my computers.
+
+set from="Jesse Morgan <jesse@jesterpm.net>"
+set sendmail="msmtp"
+set use_from=yes
+set envelope_from=yes
+
+set postponed="+drafts"
+set record="+sent"
+
+mailboxes =INBOX =archive =spam =trash =sent
diff --git a/host-overrides/jesterpm.net/host-specific/shell_pathes b/host-overrides/jesterpm.net/host-specific/shell_pathes
new file mode 100644
index 0000000..ff593a7
--- /dev/null
+++ b/host-overrides/jesterpm.net/host-specific/shell_pathes
@@ -0,0 +1,2 @@
+# PATH changes
+export PATH="$HOME/.cargo/bin:$PATH:$HOME/.linuxbrew/bin"
diff --git a/host-overrides/jesterpm.net/host-specific/vim/ftplugin/java.vim b/host-overrides/jesterpm.net/host-specific/vim/ftplugin/java.vim
new file mode 100644
index 0000000..f0934a2
--- /dev/null
+++ b/host-overrides/jesterpm.net/host-specific/vim/ftplugin/java.vim
@@ -0,0 +1,3 @@
+" Set the makeprg
+set makeprg=javac\ -sourcepath\ src\ -d\ ~/development/classes\ %
+
diff --git a/host-overrides/jesterpm.net/host-specific/vimrc b/host-overrides/jesterpm.net/host-specific/vimrc
new file mode 100644
index 0000000..a499745
--- /dev/null
+++ b/host-overrides/jesterpm.net/host-specific/vimrc
@@ -0,0 +1,23 @@
+set tags+=~/development/lib/*/tags
+set tags+=../tags
+
+function! InsertJavaPackage()
+ let filename = expand("%")
+ let filename = substitute(filename, "\.java$", "", "")
+ let dir = getcwd() . "/" . filename
+ let dir = substitute(dir, "^.*\/src\/", "", "")
+ let dir = substitute(dir, "\/[^\/]*$", "", "")
+ let dir = substitute(dir, "\/", ".", "g")
+ let filename = substitute(filename, "^.*\/", "", "")
+ let dir = "package " . dir . ";"
+ let result = append(0, "\/*")
+ let result = append(1, " * Copyright " . strftime("%Y") ." Jesse Morgan")
+ let result = append(2, " */")
+ let result = append(4, [dir, "", "\/**", " * ", " * @author Jesse Morgan <jesse@jesterpm.net>", " */", "class " . filename . " {", " ", "}"])
+ exe "normal 12G"
+endfunction
+
+autocmd BufNewFile *.java call InsertJavaPackage()
+"autocmd filetype java setlocal makeprg=ant
+"autocmd filetype java setlocal efm=\ %#[javac]\ %#%f:%l:%c:%*\\d:%*\\d:\ %t%[%^:]%#:%m,\%A\ %#[javac]\ %f:%l:\ %m,%-Z\ %#[javac]\ %p^,%-C%.%#
+
diff --git a/host-overrides/jesterpm.net/local/.nolink b/host-overrides/jesterpm.net/local/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/host-overrides/jesterpm.net/local/.nolink
diff --git a/host-overrides/jesterpm.net/local/share/.nolink b/host-overrides/jesterpm.net/local/share/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/host-overrides/jesterpm.net/local/share/.nolink
diff --git a/host-overrides/jesterpm.net/local/share/applications/.nolink b/host-overrides/jesterpm.net/local/share/applications/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/host-overrides/jesterpm.net/local/share/applications/.nolink
diff --git a/host-overrides/jesterpm.net/local/share/applications/gnome-terminal.desktop b/host-overrides/jesterpm.net/local/share/applications/gnome-terminal.desktop
new file mode 100755
index 0000000..267922b
--- /dev/null
+++ b/host-overrides/jesterpm.net/local/share/applications/gnome-terminal.desktop
@@ -0,0 +1,28 @@
+[Desktop Entry]
+Name=Terminal
+Comment=Use the command line
+TryExec=gnome-terminal
+Exec=gnome-terminal
+Icon=utilities-terminal
+Type=Application
+X-GNOME-DocPath=gnome-terminal/index.html
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=gnome-terminal
+X-GNOME-Bugzilla-Component=BugBuddyBugs
+X-GNOME-Bugzilla-Version=3.4.1.1
+Categories=GNOME;GTK;Utility;TerminalEmulator;
+StartupNotify=true
+OnlyShowIn=GNOME;Unity;
+Keywords=Run;
+Actions=New;VIM
+X-Ubuntu-Gettext-Domain=gnome-terminal
+
+[Desktop Action New]
+Name=New Terminal
+Exec=gnome-terminal
+OnlyShowIn=Unity
+
+[Desktop Action VIM]
+Name=VIM Terminal
+Exec=gnome-terminal -e vim --window-with-profile=BlackWhite
+OnlyShowIn=Unity
diff --git a/host-overrides/jesterpm.net/mbsyncrc b/host-overrides/jesterpm.net/mbsyncrc
new file mode 100644
index 0000000..6c72578
--- /dev/null
+++ b/host-overrides/jesterpm.net/mbsyncrc
@@ -0,0 +1,58 @@
+IMAPAccount gmail
+Host imap.gmail.com
+User jesterpm@gmail.com
+PassCmd "imap-pass -g jesterpm@gmail.com"
+SSLType IMAPS
+CertificateFile ~/.cert/imap.gmail.com.pem
+
+IMAPStore gmail-remote
+Account gmail
+
+MaildirStore gmail-local
+Path ~/.maildir/
+Inbox ~/.maildir/INBOX
+
+Channel sync-gmail-default
+Master :gmail-remote:
+Slave :gmail-local:
+Patterns "INBOX"
+Expunge Both
+Create Slave
+
+Channel sync-gmail-sent
+Master :gmail-remote:"[Gmail]/Sent Mail"
+Slave :gmail-local:sent
+Expunge Both
+Create Slave
+
+Channel sync-gmail-trash
+Master :gmail-remote:"[Gmail]/Bin"
+Slave :gmail-local:trash
+Expunge Both
+Create Slave
+
+Channel sync-gmail-drafts
+Master :gmail-remote:"[Gmail]/Drafts"
+Slave :gmail-local:drafts
+Expunge Both
+Create Slave
+
+Channel sync-gmail-archive
+Master :gmail-remote:"[Gmail]/All Mail"
+Slave :gmail-local:archive
+Expunge Both
+Create Slave
+
+Channel sync-gmail-spam
+Master :gmail-remote:"[Gmail]/Spam"
+Slave :gmail-local:spam
+Expunge Both
+Create Slave
+
+Group gmail
+Channel sync-gmail-default
+Channel sync-gmail-sent
+Channel sync-gmail-trash
+Channel sync-gmail-drafts
+Channel sync-gmail-archive
+Channel sync-gmail-spam
diff --git a/host-overrides/jesterpm.net/msmtprc b/host-overrides/jesterpm.net/msmtprc
new file mode 100644
index 0000000..809766e
--- /dev/null
+++ b/host-overrides/jesterpm.net/msmtprc
@@ -0,0 +1,9 @@
+account default
+from jesse@jesterpm.net
+host smtp.jesterpm.net
+auth on
+user jesse@jesterpm.net
+tls
+tls_starttls off
+tls_trust_file /etc/ssl/certs/ca-certificates.crt
+logfile ~/.msmtp.log
diff --git a/host-overrides/jesterpm.net/ssh/.nolink b/host-overrides/jesterpm.net/ssh/.nolink
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/host-overrides/jesterpm.net/ssh/.nolink
diff --git a/host-overrides/jesterpm.net/ssh/authorized_keys2 b/host-overrides/jesterpm.net/ssh/authorized_keys2
new file mode 100644
index 0000000..a4ac6af
--- /dev/null
+++ b/host-overrides/jesterpm.net/ssh/authorized_keys2
@@ -0,0 +1,2 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDNZyZWqPoyifRiamnpQCmOV5tWz36A90cLiRbMazEJrumfJVMg+pNraqalYM3bKx5DrSL8bqc9HlzPtQHqWevokkMDhT6SEKrdBD+b5Vc1ASVaeRRRRWyORA4XHTUlPVBli5UtP1vwuom0JL6vmhbOISkpPznJNqU11WHB27eb8ptw3ilLDPjZz0f1OBfU1CJ4aAiw7z0lO7GDpsdzZFScg2DN1m+9joBVxooyKjl3OhKg9/1Mg1olb3I+yCCc/WSqXqFv4Zryi6x7rKMRTaP4BZj2Ur72FOq+59FFKC8czUrgfw4k2slPezN1u4uIMjBT5oqKgT5PY3BwkuRWZyi9
+
diff --git a/host-overrides/jesterpm.net/ssh/config b/host-overrides/jesterpm.net/ssh/config
new file mode 100644
index 0000000..748be38
--- /dev/null
+++ b/host-overrides/jesterpm.net/ssh/config
@@ -0,0 +1,3 @@
+User jesterpm
+IdentityFile ~/.ssh/id_ecdsa_sk_nano
+IdentityFile ~/.ssh/id_ecdsa_sk_yubinfc