diff options
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 |