├── img ├── vim.png └── haskell.png ├── .gitignore ├── git-hscope ├── Dockerfile ├── LICENSE ├── .travis.yml ├── scripts ├── neovim.sh ├── func.sh ├── setup.sh └── setup_haskell.hs ├── vimrc.haskell ├── install.sh ├── README.md └── .vimrc /img/vim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/begriffs/haskell-vim-now/HEAD/img/vim.png -------------------------------------------------------------------------------- /img/haskell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/begriffs/haskell-vim-now/HEAD/img/haskell.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .vim 3 | *.vim 4 | *.local 5 | *.local.* 6 | .stack 7 | .stack-* 8 | backup 9 | -------------------------------------------------------------------------------- /git-hscope: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | pushd $(git rev-parse --show-toplevel) >/dev/null 4 | git ls-files | grep '\.l\?hs$' | xargs hscope -b "$@" 5 | popd >/dev/null 6 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM haskell:7.8 2 | 3 | # install vim tooling 4 | RUN apt-get update \ 5 | && apt-get install -y git vim curl wget build-essential \ 6 | # for vim extensions 7 | exuberant-ctags libcurl4-openssl-dev \ 8 | && apt-get clean 9 | 10 | # install stack 11 | RUN wget -q -O- https://s3.amazonaws.com/download.fpcomplete.com/debian/fpco.key | apt-key add - 12 | RUN echo 'deb http://download.fpcomplete.com/debian/jessie stable main'| tee /etc/apt/sources.list.d/fpco.list 13 | RUN apt-get update \ 14 | && apt-get install -y stack \ 15 | && apt-get install -y sudo \ 16 | && apt-get clean 17 | 18 | # Haskell Vim setup 19 | ADD https://raw.githubusercontent.com/begriffs/haskell-vim-now/master/install.sh /install.sh 20 | RUN /bin/bash /install.sh 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2014 Joe Nelson. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | language: generic 3 | 4 | # Vim plugin installation hangs on Travis OS X. 5 | # Disabling mac builds for now. 6 | # matrix: 7 | # include: 8 | # - os: osx 9 | # osx_image: xcode8 10 | 11 | before_install: 12 | - mkdir -p $HOME/.local/bin 13 | - mkdir -p $HOME/.config/haskell-vim-now 14 | - export PATH=$HOME/.local/bin:$PATH 15 | - git config --global user.email "me@test.com" 16 | - git config --global user.name "Testy McTesterton" 17 | - | 18 | if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then 19 | brew update 20 | brew install haskell-stack ctags vim 21 | fi 22 | - | 23 | if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then 24 | curl -sSL https://get.haskellstack.org/ | sh 25 | stack --version 26 | sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 575159689BEFB442 27 | sudo add-apt-repository ppa:nmi/vim-snapshots -y 28 | sudo apt-get -qq update -y 29 | sudo apt-get install vim ctags -y 30 | fi 31 | # emulating upgrade procedure (due to install.sh cloning from begriffs/master) 32 | - cp -r $TRAVIS_BUILD_DIR $XDG_CONFIG_HOME/ 33 | 34 | env: 35 | - XDG_CONFIG_HOME=$HOME/.config 36 | 37 | script: 38 | - bash install.sh --no-hoogle --dry-run 39 | 40 | # Caching so the next build will be fast too. 41 | cache: 42 | directories: 43 | - $HOME/.stack 44 | - $HOME/.local/bin 45 | 46 | -------------------------------------------------------------------------------- /scripts/neovim.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 4 | . ${SCRIPT_DIR}/func.sh 5 | 6 | setup() { 7 | # $1 - optional - path to haskell-vim-now installation 8 | # If not set, script will use default location. 9 | unset HVN_DEST 10 | if [ -z $1 ] ; then 11 | # No argument provided, using default path 12 | HVN_DEST="$(config_home)/haskell-vim-now" 13 | else 14 | HVN_DEST=$1 15 | fi 16 | [ ! -e ${HVN_DEST} ] && exit_err "${HVN_DEST} doesn't exist! Install Haskell-Vim-Now first!" 17 | 18 | 19 | ## Neovim configuration steps 20 | 21 | today=`date +%Y%m%d_%H%M%S` 22 | msg "Backing up current nvim config using timestamp ${today}..." 23 | [ ! -e ${HVN_DEST}/backup ] && mkdir ${HVN_DEST}/backup 24 | 25 | [ -e ${HOME}/.config/nvim ] && mv ${HOME}/.config/nvim ${HVN_DEST}/backup/nvim.${today} && detail "${HVN_DEST}/backup/nvim.${today}" 26 | 27 | msg "Creating folder for Neovim" 28 | mkdir -p ${HOME}/.config/nvim 29 | 30 | msg "Creating symlinks" 31 | detail "~/.config/nvim/init.vim -> ${HVN_DEST}/.vimrc" 32 | ln -sf ${HVN_DEST}/.vimrc ${HOME}/.config/nvim/init.vim 33 | detail "~/.config/nvim/bundle -> ${HVN_DEST}/.vim/bundle" 34 | ln -sf ${HVN_DEST}/.vim/bundle ${HOME}/.config/nvim/bundle 35 | detail "~/.config/nvim/autoload -> ${HVN_DEST}/.vim/autoload" 36 | ln -sf ${HVN_DEST}/.vim/autoload ${HOME}/.config/nvim/autoload 37 | 38 | echo -e "\n" 39 | msg "<---- HASKELL VIM NOW Neovim setup successfully finished ---->" 40 | echo -e "\n" 41 | } 42 | 43 | main() { 44 | setup ${HVN_DEST} 45 | } 46 | 47 | main 48 | -------------------------------------------------------------------------------- /vimrc.haskell: -------------------------------------------------------------------------------- 1 | " HVN paths {{{ 2 | " stack bin path symlink 3 | let hvn_stack_bin = expand(resolve(hvn_config_dir . "/.stack-bin")) 4 | " Find custom built hasktags, codex etc 5 | let $PATH = expand(hvn_stack_bin) . ':' . $PATH 6 | 7 | " }}} 8 | 9 | " Conceal {{{ 10 | " Use same color behind concealed unicode characters 11 | hi clear Conceal 12 | 13 | " Pretty unicode haskell symbols 14 | let g:haskell_conceal_wide = 1 15 | let g:haskell_conceal_enumerations = 1 16 | let hscoptions="𝐒𝐓𝐄𝐌xRtB𝔻w" 17 | 18 | " }}} 19 | 20 | " Hoogle {{{ 21 | " Hoogle the word under the cursor 22 | nnoremap hh :Hoogle 23 | 24 | " Hoogle and prompt for input 25 | nnoremap hH :Hoogle 26 | 27 | " Hoogle for detailed documentation (e.g. "Functor") 28 | nnoremap hi :HoogleInfo 29 | 30 | " Hoogle for detailed documentation and prompt for input 31 | nnoremap hI :HoogleInfo 32 | 33 | " Hoogle, close the Hoogle window 34 | nnoremap hz :HoogleClose 35 | 36 | " }}} 37 | 38 | " Formatting {{{ 39 | " Use hindent instead of par for haskell buffers 40 | autocmd FileType haskell let &formatprg="hindent --tab-size 2 -XQuasiQuotes" 41 | 42 | " Enable some tabular presets for Haskell 43 | let g:haskell_tabular = 1 44 | 45 | " Delete trailing white space on save 46 | augroup whitespace 47 | autocmd! 48 | autocmd BufWrite *.hs :call DeleteTrailingWS() 49 | augroup END 50 | 51 | " }}} 52 | 53 | "Completion, Syntax check, Lint & Refactor {{{ 54 | 55 | " Disable hlint-refactor-vim's default keybindings 56 | let g:hlintRefactor#disableDefaultKeybindings = 1 57 | 58 | " hlint-refactor-vim keybindings 59 | map hr :call ApplyOneSuggestion() 60 | map hR :call ApplyAllSuggestions() 61 | 62 | " Fix path issues from vim.wikia.com/wiki/Set_working_directory_to_the_current_file 63 | let s:default_path = escape(&path, '\ ') " store default value of 'path' 64 | " Always add the current file's directory to the path and tags list if not 65 | " already there. Add it to the beginning to speed up searches. 66 | autocmd BufRead * 67 | \ let s:tempPath=escape(escape(expand("%:p:h"), ' '), '\ ') | 68 | \ exec "set path-=".s:tempPath | 69 | \ exec "set path-=".s:default_path | 70 | \ exec "set path^=".s:tempPath | 71 | \ exec "set path^=".s:default_path 72 | 73 | " Haskell Lint 74 | nmap hl :Neomake hlint 75 | 76 | " GHC errors and warnings 77 | noremap hc :make:copen 78 | 79 | " }}} 80 | -------------------------------------------------------------------------------- /scripts/func.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Text color 4 | if which tput >/dev/null 2>&1; then 5 | ncolors=$(tput colors) 6 | fi 7 | 8 | if [ -t 1 ] && [ -n "${ncolors}" ] && [ "${ncolors}" -ge 8 ]; then 9 | RED="$(tput setaf 1)" 10 | GREEN="$(tput setaf 2)" 11 | YELLOW="$(tput setaf 3)" 12 | BOLD="$(tput bold)" 13 | NORMAL="$(tput sgr0)" 14 | else 15 | RED="" 16 | GREEN="" 17 | YELLOW="" 18 | BOLD="" 19 | NORMAL="" 20 | fi 21 | 22 | msg() { echo -e "${GREEN}--- $@${NORMAL}" 1>&2; } 23 | warn() { echo -e "${YELLOW}${BOLD}--> $@${NORMAL}" 1>&2; } 24 | err() { echo -e "${RED}${BOLD}*** $@${NORMAL}" 1>&2; } 25 | detail() { echo -e " $@" 1>&2; } 26 | verlte() { 27 | [ "$1" = `echo -e "$1\n$2" | sort -t '.' -k 1,1n -k 2,2n -k 3,3n -k 4,4n | head -n1` ] 28 | } 29 | 30 | system_type() { 31 | local platform 32 | case ${OSTYPE} in 33 | linux* ) platform="LINUX" ;; 34 | darwin* ) platform="OSX" ;; 35 | cygwin* ) platform="CYGWIN" ;; 36 | * ) platform="OTHER" ;; 37 | esac 38 | 39 | echo ${platform} 40 | return 0 41 | } 42 | 43 | config_home() { 44 | local cfg_home 45 | if [ -z ${XDG_CONFIG_HOME+x} ]; then 46 | cfg_home="${HOME}/.config" 47 | else 48 | cfg_home=${XDG_CONFIG_HOME} 49 | fi 50 | 51 | echo ${cfg_home} 52 | return 0 53 | } 54 | 55 | package_manager() { 56 | local package_manager 57 | 58 | if command -v brew >/dev/null 2>&1 ; then 59 | package_manager="BREW" 60 | elif command -v dnf >/dev/null 2>&1 ; then 61 | package_manager="DNF" 62 | elif command -v yum >/dev/null 2>&1 ; then 63 | package_manager="YUM" 64 | elif command -v apt-get >/dev/null 2>&1 ; then 65 | package_manager="APT" 66 | elif command -v port >/dev/null 2>&1 ; then 67 | package_manager="PORT" 68 | else 69 | package_manager="OTHER" 70 | fi 71 | 72 | echo ${package_manager} 73 | return 0 74 | } 75 | 76 | # $1: package manager 77 | # $2-: list of packages 78 | package_install() { 79 | local pkgmgr=$1; shift 80 | if [ $# -eq 0 ]; then 81 | msg "No new packages needed for install..." 82 | return 83 | fi 84 | msg "Installing system packages [$*] using [$pkgmgr]..." 85 | case ${pkgmgr} in 86 | BREW ) 87 | msg "Installing with homebrew..." 88 | brew install $* 89 | ;; 90 | PORT ) 91 | msg "Installing with port..." 92 | port install $* 93 | ;; 94 | APT ) 95 | msg "Installing with apt-get..." 96 | sudo apt-get install --no-upgrade -y $* 97 | ;; 98 | DNF ) 99 | msg "Installing with DNF..." 100 | sudo dnf install -yq $* # yum and dnf use same repos 101 | ;; 102 | YUM ) 103 | msg "Installing with YUM..." 104 | sudo yum install -yq $* 105 | ;; 106 | OTHER ) 107 | warn "No package manager detected. You may need to install required packages manually." 108 | ;; 109 | * ) 110 | exit_err_report "setup.sh is not configured to handle ${pkgmgr} manager." 111 | esac 112 | } 113 | 114 | fix_path() { 115 | # $1 - path 116 | local return_path 117 | case $(system_type) in 118 | CYGWIN ) 119 | return_path=$(cygpath -u "${1}" | tr -d '\r') 120 | ;; 121 | * ) 122 | return_path=${1} 123 | esac 124 | 125 | echo ${return_path} 126 | return 0 127 | } 128 | 129 | check_exist() { 130 | local not_exist=() 131 | for prg; do 132 | if ! command -v ${prg} >/dev/null 2>&1; then 133 | not_exist+=("${prg}") 134 | fi 135 | done 136 | echo ${not_exist[@]} 137 | return ${#not_exist[@]} 138 | } 139 | 140 | exit_err() { 141 | err ${1} 142 | err "Aborting..." 143 | exit 1 144 | } 145 | 146 | exit_err_report() { 147 | err ${1} 148 | err "Please report at https://github.com/begriffs/haskell-vim-now/issues" 149 | err "Aborting..." 150 | exit 1 151 | } 152 | 153 | -------------------------------------------------------------------------------- /scripts/setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 4 | . ${SCRIPT_DIR}/func.sh 5 | 6 | stack_resolver() { 7 | local DEFAULT_RESOLVER=lts 8 | local CONFIGURED 9 | 10 | CONFIGURED=$(awk '{if ($1 == "resolver:") {print $2}}' "$1") \ 11 | || err "Failed to determine stack resolver" 12 | if [ -z $CONFIGURED ]; then 13 | echo $DEFAULT_RESOLVER 14 | else 15 | echo $CONFIGURED 16 | fi 17 | return 0 18 | } 19 | 20 | # Print package name to install if command is not found 21 | # $1: command name 22 | # $2: package name 23 | cmdpkg() { 24 | test -n "$(which $1)" || echo "$2" 25 | } 26 | 27 | # $1: package manager 28 | package_list() { 29 | cmdpkg git git 30 | cmdpkg vim vim 31 | cmdpkg par par 32 | 33 | case $1 in 34 | BREW) 35 | cmdpkg make homebrew/dupes/make 36 | cmdpkg ctags ctags ;; 37 | PORT) 38 | cmdpkg make gmake 39 | cmdpkg ctags ctags ;; 40 | APT) 41 | cmdpkg make make 42 | cmdpkg ctags exuberant-ctags 43 | cmdpkg curl curl 44 | echo libcurl4-openssl-dev ;; 45 | YUM|DNF) 46 | cmdpkg make make 47 | cmdpkg ctags ctags 48 | echo "libcurl-devel zlib-devel powerline" ;; 49 | esac 50 | } 51 | 52 | setup_tools() { 53 | # Installs _only if_ the command is not available 54 | local PACKAGE_MGR=$(package_manager) 55 | package_install ${PACKAGE_MGR} $(package_list ${PACKAGE_MGR}) 56 | 57 | local NOT_INSTALLED=$(check_exist ctags curl curl-config git make vim par) 58 | [ ! -z "${NOT_INSTALLED}" ] && exit_err "Installer requires '${NOT_INSTALLED}'. Please install and try again." 59 | 60 | msg "Checking ctags' exuberance..." 61 | local RETCODE 62 | ctags --version | grep -q Exuberant ; RETCODE=$? 63 | [ ${RETCODE} -ne 0 ] && exit_err "Requires exuberant-ctags, not just ctags. Please install and put it in your PATH." 64 | 65 | msg "Setting git to use fully-pathed vim for messages..." 66 | git config --global core.editor $(which vim) 67 | } 68 | 69 | vim_check_version() { 70 | local VIM_VER=$(vim --version | sed -n 's/^.*IMproved \([^ ]*\).*$/\1/p') 71 | if ! verlte '7.4' ${VIM_VER} ; then 72 | exit_err "Detected vim version \"${VIM_VER}\", however version 7.4 or later is required." 73 | fi 74 | 75 | if vim --version | grep -q +ruby 2>&1 ; then 76 | msg "Testing for broken Ruby interface in vim..." 77 | vim -T dumb --cmd "ruby puts RUBY_VERSION" --cmd qa! 1>/dev/null 2>/dev/null 78 | if [ $? -eq 0 ] ; then 79 | msg "Test passed. Ruby interface is OK." 80 | else 81 | err "The Ruby interface is broken on your installation of vim." 82 | err "Reinstall or recompile vim." 83 | msg "If you're on OS X, try the following:" 84 | detail "rvm use system" 85 | detail "brew reinstall vim" 86 | warn "If nothing helped, please report at https://github.com/begriffs/haskell-vim-now/issues" 87 | exit 1 88 | fi 89 | fi 90 | } 91 | 92 | vim_backup () { 93 | local HVN_DEST=$1 94 | 95 | if [ -e ~/.vim/colors ]; then 96 | msg "Preserving color scheme files..." 97 | cp -R ~/.vim/colors ${HVN_DEST}/colors 98 | fi 99 | 100 | today=`date +%Y%m%d_%H%M%S` 101 | msg "Backing up current vim config using timestamp ${today}..." 102 | [ ! -e ${HVN_DEST}/backup ] && mkdir ${HVN_DEST}/backup 103 | 104 | for i in .vim .vimrc .gvimrc; do [ -e ${HOME}/${i} ] && mv ${HOME}/${i} ${HVN_DEST}/backup/${i}.${today} && detail "${HVN_DEST}/backup/${i}.${today}"; done 105 | } 106 | 107 | vim_setup_links() { 108 | local HVN_DEST=$1 109 | 110 | msg "Creating vim config symlinks" 111 | detail "~/.vimrc -> ${HVN_DEST}/.vimrc" 112 | ln -sf ${HVN_DEST}/.vimrc ${HOME}/.vimrc 113 | 114 | detail "~/.vim -> ${HVN_DEST}/.vim" 115 | ln -sf ${HVN_DEST}/.vim ${HOME}/.vim 116 | } 117 | 118 | vim_install_plug() { 119 | local HVN_DEST=$1 120 | 121 | if [ ! -e ${HVN_DEST}/.vim/autoload/plug.vim ]; then 122 | msg "Installing vim-plug" 123 | curl -fLo ${HVN_DEST}/.vim/autoload/plug.vim --create-dirs \ 124 | https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim \ 125 | || exit_err_report "Failed to install vim-plug." 126 | fi 127 | } 128 | 129 | vim_install_plugins() { 130 | msg "Installing plugins using vim-plug..." 131 | vim -E -u ${HVN_DEST}/.vimrc +PlugUpgrade +PlugUpdate +PlugClean! +qall 132 | } 133 | 134 | setup_vim() { 135 | local HVN_DEST=$1 136 | 137 | vim_check_version 138 | vim_install_plug $HVN_DEST 139 | 140 | # Point of no return; we cannot fail after this. 141 | # Backup old config and switch to new config 142 | vim_backup $HVN_DEST 143 | vim_setup_links $HVN_DEST 144 | vim_install_plugins $HVN_DEST 145 | } 146 | 147 | setup_done() { 148 | local HVN_DEST=$1 149 | 150 | echo -e "\n" 151 | msg "<---- HASKELL VIM NOW installation successfully finished ---->" 152 | echo -e "\n" 153 | 154 | warn "If you are using NeoVim" 155 | detail "Run ${HVN_DEST}/scripts/neovim.sh to backup your existing" 156 | detail "configuration and symlink the new one." 157 | 158 | warn "Note for a good-looking vim experience:" 159 | detail "Configure your terminal to use a font with Powerline symbols." 160 | detail "https://powerline.readthedocs.org/en/master/installation.html#fonts-installation" 161 | } 162 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | PROGNAME=$(basename $0) 4 | DEFAULT_REPO="https://github.com/begriffs/haskell-vim-now.git" 5 | DEFAULT_BRANCH="master" 6 | DEFAULT_GENERATE_HOOGLE_DB=true 7 | DEFAULT_HVN_FULL_INSTALL=true 8 | DEFAULT_DRY_RUN=false 9 | DEFAULT_NIX=false 10 | 11 | if which tput >/dev/null 2>&1; then 12 | ncolors=$(tput colors) 13 | fi 14 | 15 | if [ -t 1 ] && [ -n "${ncolors}" ] && [ "${ncolors}" -ge 8 ]; then 16 | RED="$(tput setaf 1)" 17 | GREEN="$(tput setaf 2)" 18 | YELLOW="$(tput setaf 3)" 19 | BOLD="$(tput bold)" 20 | NORMAL="$(tput sgr0)" 21 | else 22 | RED="" 23 | GREEN="" 24 | YELLOW="" 25 | BOLD="" 26 | NORMAL="" 27 | fi 28 | 29 | msg() { echo -e "${GREEN}--- $@${NORMAL}" 1>&2; } 30 | warn() { echo -e "${YELLOW}${BOLD}--> $@${NORMAL}" 1>&2; } 31 | err() { echo -e "${RED}${BOLD}*** $@${NORMAL}" 1>&2; } 32 | 33 | config_home() { 34 | local cfg_home 35 | if [ -z ${XDG_CONFIG_HOME+x} ]; then 36 | cfg_home="${HOME}/.config" 37 | else 38 | cfg_home=${XDG_CONFIG_HOME} 39 | fi 40 | echo ${cfg_home} 41 | return 0 42 | } 43 | 44 | update_pull() { 45 | local repo_path=$1 46 | cd ${repo_path} 47 | if [ "$(git status -s)" ]; then 48 | ## Local repo has changes, prompt before overwriting them: 49 | read -p "Would you like to force a sync? THIS WILL REMOVE ANY LOCAL CHANGES! [y/N]: " response 50 | case $response in 51 | [yY][eE][sS]|[yY]) 52 | git reset --hard 53 | ;; 54 | esac 55 | fi 56 | git pull --rebase 57 | return $? 58 | } 59 | 60 | check_repo_change() { 61 | local REPO_PATH=$1 62 | local HVN_DEST=$2 63 | local orig_repo 64 | 65 | orig_repo=$(cd $HVN_DEST && git config --get remote.origin.url) || exit 1 66 | if test -z "$orig_repo" -o "$orig_repo" != $REPO_PATH 67 | then 68 | err "The source repository path [$REPO_PATH] does not match the" 69 | err "origin repository of the existing installation [$orig_repo]." 70 | err "Please remove the existing installation [$HVN_DEST] and try again." 71 | exit 1 72 | fi 73 | } 74 | 75 | install() { 76 | local REPO_PATH=$1 77 | local REPO_BRANCH=$2 78 | local HVN_DEST=$3 79 | 80 | if [ -e ${HVN_DEST} ]; then 81 | warn "Existing Haskell-Vim-Now installation detected at ${HVN_DEST}." 82 | elif [ -e ${HOME}/.haskell-vim-now ]; then 83 | warn "Old Haskell-Vim-Now installation detected." 84 | msg "Migrating existing installation to ${HVN_DEST}..." 85 | mv -f ${HOME}/.haskell-vim-now ${HVN_DEST} 86 | mv -f ${HOME}/.vimrc.local ${HVN_DEST}/vimrc.local 87 | mv -f ${HOME}/.vimrc.local.pre ${HVN_DEST}/vimrc.local.pre 88 | sed -i.bak "s/Plugin '/Plug '/g" ${HOME}/.vim.local/bundles.vim 89 | mv -f ${HOME}/.vim.local/bundles.vim ${HVN_DEST}/plugins.vim 90 | rm -f ${HOME}/.vim.local/bundles.vim.bak 91 | rmdir ${HOME}/.vim.local >/dev/null 92 | else 93 | warn "No previous installations detected." 94 | msg "Installing Haskell-Vim-Now from ${REPO_PATH} ..." 95 | mkdir -p $(config_home) 96 | git clone -b ${REPO_BRANCH} ${REPO_PATH} ${HVN_DEST} || exit 1 97 | 98 | return 0 99 | fi 100 | 101 | check_repo_change ${REPO_PATH} ${HVN_DEST} 102 | # Quick update to make sure we execute correct update procedure 103 | msg "Syncing Haskell-Vim-Now with upstream..." 104 | if ! update_pull ${HVN_DEST} ; then 105 | err "Sync (git pull) failed. Aborting..." 106 | exit 1; 107 | fi 108 | } 109 | 110 | do_setup() { 111 | local HVN_DEST=$1 112 | local FULL_INSTALL=$2 113 | local GENERATE_HOOGLE_DB=$3 114 | local DRY_RUN=$4 115 | local NIX=$5 116 | local setup_path=${HVN_DEST}/scripts/setup.sh 117 | local setup_haskell_path=${HVN_DEST}/scripts/setup_haskell.hs 118 | 119 | . $setup_path || { \ 120 | err "Failed to source ${setup_path}." 121 | err "Have you cloned from the correct repository?" 122 | exit 1 123 | } 124 | 125 | setup_tools 126 | setup_vim $HVN_DEST 127 | 128 | if [ "$FULL_INSTALL" == true ] 129 | then 130 | local ARG_NO_HOOGLE_DB="--no-hoogle" 131 | local ARG_NO_HELPER_BINS="--no-helper-bins" 132 | local ARG_NIX= 133 | 134 | if [ "$GENERATE_HOOGLE_DB" == true ] 135 | then 136 | ARG_NO_HOOGLE_DB= 137 | fi 138 | 139 | if [ "$DRY_RUN" == false ] 140 | then 141 | ARG_NO_HELPER_BINS= 142 | fi 143 | 144 | if ! check_exist stack >/dev/null ; then 145 | err "Installer requires Stack." 146 | msg "Installation instructions: http://docs.haskellstack.org/en/stable/README/#how-to-install" 147 | exit 1 148 | fi 149 | 150 | local STACK_VER=$(stack --version | sed 's/^Version \([0-9]*\.[0-9]*\.[0-9]*\).*$/\1/') 151 | if ! verlte '1.4.0' ${STACK_VER} ; then 152 | exit_err "Detected stack version \"${STACK_VER}\", however version 1.4.0 or later is required." 153 | fi 154 | 155 | if [ "$NIX" == true ] 156 | then 157 | ARG_NIX="--nix" 158 | fi 159 | 160 | stack $ARG_NIX $setup_haskell_path $ARG_NO_HOOGLE_DB $ARG_NO_HELPER_BINS ; RETCODE=$? 161 | [ ${RETCODE} -ne 0 ] && exit_err "setup_haskell.hs failed with error ${RETCODE}." 162 | fi 163 | 164 | setup_done $HVN_DEST 165 | } 166 | 167 | main() { 168 | local REPO_PATH=$1 169 | local REPO_BRANCH=$2 170 | local FULL_INSTALL=$3 171 | local GENERATE_HOOGLE_DB=$4 172 | local DRY_RUN=$5 173 | local NIX=$6 174 | local HVN_DEST="$(config_home)/haskell-vim-now" 175 | local HVN_DEPENDENCIES_DEST="$(config_home)/haskell-vim-now" 176 | 177 | install $REPO_PATH $REPO_BRANCH $HVN_DEST 178 | do_setup $HVN_DEST $FULL_INSTALL $GENERATE_HOOGLE_DB $DRY_RUN $NIX 179 | } 180 | 181 | function usage() { 182 | echo "Usage: $PROGNAME [--basic] [--repo ] [--no-hoogle]" 183 | echo "" 184 | echo "OPTIONS" 185 | echo " --basic" 186 | echo " Install only vim and plugins without haskell components." 187 | echo " --repo " 188 | echo " Git repository to install from. The default is $DEFAULT_REPO." 189 | echo " --no-hoogle" 190 | echo " Disable Hoogle database generation. The default is $DEFAULT_GENERATE_HOOGLE_DB." 191 | echo " --dry-run" 192 | echo " Perform a dry run for the stack installs. Primarily intended for testing." 193 | echo " --nix" 194 | echo " Perform stack build in a nix-shell. For more information see https://docs.haskellstack.org/en/stable/nix_integration" 195 | exit 1 196 | } 197 | 198 | # command line args override env vars 199 | HVN_REPO=${HVN_REPO:=$DEFAULT_REPO} 200 | HVN_BRANCH=${HVN_BRANCH:=$DEFAULT_BRANCH} 201 | HVN_GENERATE_HOOGLE_DB=${HVN_GENERATE_HOOGLE_DB:=$DEFAULT_GENERATE_HOOGLE_DB} 202 | HVN_FULL_INSTALL=${HVN_FULL_INSTALL:=$DEFAULT_HVN_FULL_INSTALL} 203 | HVN_DRY_RUN=${HVN_DRY_RUN:=$DEFAULT_DRY_RUN} 204 | HVN_NIX=${HVN_NIX:=$DEFAULT_NIX} 205 | 206 | while test -n "$1" 207 | do 208 | case $1 in 209 | --basic) shift; HVN_FULL_INSTALL=false; continue;; 210 | --repo) shift; HVN_REPO=$1; shift; continue;; 211 | --branch) shift; HVN_BRANCH=$1; shift; continue;; 212 | --no-hoogle) shift; HVN_GENERATE_HOOGLE_DB=false; continue;; 213 | --dry-run) shift; HVN_DRY_RUN=true; continue;; 214 | --nix) shift; HVN_NIX=true; continue;; 215 | *) usage;; 216 | esac 217 | done 218 | 219 | test -n "$HVN_REPO" || usage 220 | main $HVN_REPO $HVN_BRANCH $HVN_FULL_INSTALL $HVN_GENERATE_HOOGLE_DB $HVN_DRY_RUN $HVN_NIX 221 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/begriffs/haskell-vim-now.svg?branch=master)](https://travis-ci.org/begriffs/haskell-vim-now) 2 | 3 | 4 | 5 |

Haskell Vim IDE

6 | 7 |
8 | 9 | In less than **ten minutes** your Vim will transform into a beautiful 10 | Haskell paradise. (Don't worry, it backs up your original 11 | configuration to `~/.config/haskell-vim-now/backup/.vimrc.yearmonthdate_time`.) It also builds all necessary support binaries 12 | including `ghcide`, `hlint`, `hoogle` and more. 13 | 14 | No more wading through plugins trying to make them all work together. 15 | In ten minutes you will have a fully functional Vim that looks great 16 | and lets you 17 | 18 | * inspect types 19 | * evaluate Haskell 20 | * lint and check 21 | * manipulate tags 22 | * hoogle lookup 23 | * pointfree refactor 24 | * tab complete 25 | * unicode symbols 26 | * highlight DSLs 27 | * work with git 28 | 29 | 30 | ## Installation 31 | 32 | Just download and run the installer: 33 | 34 | ```sh 35 | curl -L https://git.io/haskell-vim-now > /tmp/haskell-vim-now.sh 36 | bash /tmp/haskell-vim-now.sh 37 | ``` 38 | **WARNING**: this command, once successful, will _make backups and **remove**_ your existing VIM configurations (`.vim`, plugins etc). You can later [customize](#customizing) HVN configurations. 39 | 40 | ## Keybindings and commands 41 | 42 | The commands are organized into logical groups to help you remember 43 | them. 44 | 45 | ### Types, autocomplete, refactoring, and linting 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 |
<Tab>Autocomplete with words in file
<C-space>Autocomplete with symbols in your Cabal sandbox
,hrApply one refactoring hint at cursor position
,hRApply all refactoring suggestions in the file
,hlRun Haskell linter on file
,hcRun Haskell compile check on file
,<cr>Clear type selection
72 | 73 | ### Hoogle 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 |
,hhRun Hoogle on the word under the cursor
,hHRun Hoogle and prompt for input
,hiRun Hoogle for detailed information on word under cursor
,hIRun Hoogle for detailed information and prompt for input
,hzClose the Hoogle search window
94 | 95 | ### GHCI repl 96 | 97 | If you open a tmux terminal alongside MacVim then you can send Vim 98 | selections to it. This works well for evaluating things in GHCI. 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 |
,rsSend selected text to tmux
,rvChange tmux session, window, and pane attachment
110 | 111 | ### Git 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 |
,g?Last-committed files (Monday morning key)
,gsGit status (fugitive)
,ggGit grep
,glGit log (extradition)
,gdGit diff
,gbGit blame
135 | 136 | ### Commenting 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 |
gcComment / Uncomment selection
145 | 146 | ### Aligning 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 |
,a=Align on equal signs
,a,Align on commas
,a|Align on vertical bar
,apAlign on character of your choice
164 | 165 | ### Splits and find file 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 |
,<space>Fuzzy file find (CtrlP)
,fToggle file browser, find file
,FToggle file browser
,sjOpen split below
,skOpen split above
,shOpen split leftward
,slOpen split rightward
192 | 193 | ### Buffers 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 |
,bpPrevious buffer
,bnNext buffer
,b<space>Buffer fuzzy finder
,bdDelete buffer, keep window open (bbye)
,boClose all buffers except the current one
214 | 215 | ### Misc 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 |
,maEnable mouse mode (default)
,moDisable mouse mode
,igToggle indentation guides
,uInteractive undo tree
,ssEnable spell checking
,eOpen file prompt with current path
,<cr>Clear search highlights
,rRedraw screen
C-hMove cursor to leftward pane
C-kMove cursor to upward pane
C-jMove cursor to downward pane
C-lMove cursor to rightward pane (redraw is `,r` instead)
gqFormat selection using `hindent` for haskell buffers (`par` for others)
,yYank to OS clipboard
,dDelete to OS clipboard
,pPaste from OS clipboard
269 | 270 | (If you prefer to restore the default screen redraw action of `C-l` 271 | then add `unmap ` to your vimrc.local) 272 | 273 | ## Customizing 274 | 275 | After installing this configuration, your `.vimrc` and `.vim` will 276 | be under version control. Don't alter these files. Instead, add 277 | your own settings to `~/.config/haskell-vim-now/vimrc.local.pre`, 278 | `~/.config/haskell-vim-now/vimrc.local`. 279 | 280 | ## Adding Vim Plugins 281 | 282 | Haskell-Vim-Now uses [vim-plug](https://github.com/junegunn/vim-plug) 283 | to install plugins. It uses the following vim configuration structure 284 | to determine what to install: 285 | 286 | ```viml 287 | call plug#begin('~/.vim/plugged') 288 | 289 | " The plugins are named in github short form, for example: 290 | Plug 'junegunn/vim-easy-align' 291 | 292 | " All plug statements must be between plug#begin and plug#end 293 | call plug#end() 294 | ``` 295 | 296 | However the `.vimrc` file in Haskell-Vim-Now is under version control 297 | so you shouldn't edit it directly. To add a plugin what you should 298 | do is add `Plug` statements to `~/.config/haskell-vim-now/plugins.vim`. 299 | When ready reload `.vimrc` and run `:PlugInstall` to install plugins. 300 | 301 | ## Neovim support 302 | 303 | The `.vimrc` configuration is fully compatible with Neovim, and adds a few 304 | Neovim specific mappings for the terminal mode (terminal emulation is activated 305 | with `:terminal`). The mappings make `Esc` and `c-[hjkl]` function as one would 306 | expect them to from normal mode. 307 | 308 | The Neovim configuration is found at `.config/nvim`, and is symlinked just like 309 | regular vim, which means you should only add your own settings to 310 | `~/.config/haskell-vim-now/vimrc.local.pre`, `~/.config/haskell-vim-now/vimrc.local` 311 | and `~/.config/haskell-vim-now/plugins.vim`. 312 | 313 | You can quickly backup and replace your Neovim setup by running the `scripts/neovim.sh` 314 | script. 315 | 316 | ## Docker image 317 | 318 | If you are into developing with Docker, you can use the image. 319 | 320 | docker pull haskell:7.8 321 | docker build -t haskell-vim . 322 | docker run --rm -i -t haskell-vim /bin/bash 323 | 324 | If instead you want to extract the vim setup from the image that is easy enough 325 | 326 | docker build -t haskell-vim . 327 | mkdir ~/.haskell-vim-now 328 | cd ~/.haskell-vim-now 329 | docker run --rm haskell-vim tar -cz -C /root/.haskell-vim-now . > haskell-vim-now.tgz 330 | tar -xzf haskell-vim-now.tgz 331 | 332 | However, some things (for example the hoogle database) use absolute paths and don't work correctly. 333 | 334 | ## Advanced install methods 335 | 336 | ### Basic install 337 | In case you want to skip the haskell specific components and want to install 338 | just the common vim config you can use: 339 | ```sh 340 | bash <(curl -sL https://git.io/haskell-vim-now) --basic 341 | ``` 342 | 343 | ### Installing from a fork or clone 344 | If you have a modified fork you can use the `--repo` option to tell the install 345 | script the location of your repository: 346 | ```sh 347 | bash <(curl -sL INSTALL-SCRIPT-URL) --repo FORK-URL 348 | ``` 349 | 350 | For example: 351 | 352 | ```sh 353 | bash <(curl -sL https://raw.githubusercontent.com/begriffs/haskell-vim-now/master/install.sh) --repo https://github.com/begriffs/haskell-vim-now.git 354 | ``` 355 | 356 | Additionally the `--branch` argument can override the default of `master` used when cloning the repo. 357 | 358 | If you have a local git clone you can use `install.sh` directly 359 | to install from your clone: 360 | ```sh 361 | install.sh --repo CLONE-PATH 362 | ``` 363 | 364 | ### Installing in Nix build environment 365 | 366 | Use `--nix` parameter, in case you want to execute [stack installation in nix-shell](https://docs.haskellstack.org/en/stable/nix_integration/#use-stack-as-normal) 367 | ```sh 368 | bash <(curl -sL https://git.io/haskell-vim-now) --nix 369 | ``` 370 | 371 | ## Troubleshooting 372 | 373 | See this [wiki](https://github.com/begriffs/haskell-vim-now/wiki/Installation-Troubleshooting) 374 | page for tips on fixing installation problems. 375 | 376 | ## Thank you! 377 | 378 | Big thanks to [contributors](https://github.com/begriffs/haskell-vim-now/graphs/contributors). I'd especially like to thank [@SX91](https://github.com/SX91) for rewriting the installer and for other major improvements. 379 | -------------------------------------------------------------------------------- /.vimrc: -------------------------------------------------------------------------------- 1 | " HVN paths {{{ 2 | " Set XDG_CONFIG_HOME/haskell-vim-now to load user's config files 3 | if exists($XDG_CONFIG_HOME) 4 | let hvn_config_dir = $XDG_CONFIG_HOME . "/haskell-vim-now" 5 | else 6 | let hvn_config_dir = $HOME . "/.config/haskell-vim-now" 7 | endif 8 | 9 | " Haskell Vim Now paths 10 | " haskell config path 11 | let hvn_config_haskell = expand(resolve(hvn_config_dir . "/vimrc.haskell")) 12 | " pre config path 13 | let hvn_config_pre = expand(resolve(hvn_config_dir . "/vimrc.local.pre")) 14 | " post config path 15 | let hvn_config_post = expand(resolve(hvn_config_dir . "/vimrc.local")) 16 | " user plugins config path 17 | let hvn_user_plugins = expand(resolve(hvn_config_dir . "/plugins.vim")) 18 | " }}} 19 | 20 | " Precustomization {{{ 21 | if filereadable(hvn_config_pre) 22 | execute 'source '. hvn_config_pre 23 | endif 24 | " }}} 25 | 26 | " General {{{ 27 | " Use indentation for folds 28 | set foldmethod=indent 29 | set foldnestmax=5 30 | set foldlevelstart=99 31 | set foldcolumn=0 32 | 33 | augroup vimrcFold 34 | " fold vimrc itself by categories 35 | autocmd! 36 | autocmd FileType vim set foldmethod=marker 37 | autocmd FileType vim set foldlevel=0 38 | augroup END 39 | 40 | " Sets how many lines of history VIM has to remember 41 | set history=700 42 | 43 | " Set to auto read when a file is changed from the outside 44 | set autoread 45 | 46 | " With a map leader it's possible to do extra key combinations 47 | " like w saves the current file 48 | if ! exists("mapleader") 49 | let mapleader = "," 50 | endif 51 | 52 | if ! exists("g:mapleader") 53 | let g:mapleader = "," 54 | endif 55 | 56 | " Leader key timeout 57 | set tm=2000 58 | 59 | " Allow the normal use of "," by pressing it twice 60 | noremap ,, , 61 | 62 | " Use par for prettier line formatting 63 | set formatprg=par 64 | let $PARINIT = 'rTbgqR B=.,?_A_a Q=_s>|' 65 | 66 | " Kill the damned Ex mode. 67 | nnoremap Q 68 | 69 | " Make work like again (this is a problem with libterm) 70 | if has('nvim') 71 | nnoremap h 72 | endif 73 | 74 | " }}} 75 | 76 | " vim-plug {{{ 77 | 78 | set nocompatible 79 | 80 | if has('nvim') 81 | call plug#begin('~/.config/nvim/bundle') 82 | else 83 | call plug#begin('~/.vim/bundle') 84 | endif 85 | 86 | " Support bundles 87 | Plug 'jgdavey/tslime.vim' 88 | Plug 'Shougo/vimproc.vim', { 'do': 'make' } 89 | Plug 'ervandew/supertab' 90 | Plug 'benekastah/neomake' 91 | Plug 'moll/vim-bbye' 92 | Plug 'nathanaelkane/vim-indent-guides' 93 | Plug 'vim-scripts/gitignore' 94 | 95 | " Git 96 | Plug 'tpope/vim-fugitive' 97 | Plug 'int3/vim-extradite' 98 | 99 | " Bars, panels, and files 100 | Plug 'scrooloose/nerdtree' 101 | Plug 'vim-airline/vim-airline' 102 | Plug 'ctrlpvim/ctrlp.vim' 103 | Plug 'majutsushi/tagbar' 104 | 105 | " Text manipulation 106 | Plug 'vim-scripts/Align' 107 | Plug 'simnalamburt/vim-mundo' 108 | Plug 'tpope/vim-commentary' 109 | Plug 'godlygeek/tabular' 110 | Plug 'michaeljsmith/vim-indent-object' 111 | Plug 'easymotion/vim-easymotion' 112 | Plug 'ConradIrwin/vim-bracketed-paste' 113 | 114 | " Allow pane movement to jump out of vim into tmux 115 | Plug 'christoomey/vim-tmux-navigator' 116 | 117 | " Haskell 118 | Plug 'neovimhaskell/haskell-vim', { 'for': 'haskell' } 119 | Plug 'enomsg/vim-haskellConcealPlus', { 'for': 'haskell' } 120 | Plug 'neoclide/coc.nvim', { 'do': { -> coc#util#install() } } 121 | Plug 'Twinside/vim-hoogle', { 'for': 'haskell' } 122 | Plug 'mpickering/hlint-refactor-vim', { 'for': 'haskell' } 123 | 124 | " Colorscheme 125 | Plug 'vim-scripts/wombat256.vim' 126 | 127 | " Custom bundles 128 | 129 | if filereadable(hvn_user_plugins) 130 | execute 'source '. hvn_user_plugins 131 | endif 132 | 133 | call plug#end() 134 | 135 | " }}} 136 | 137 | " VIM user interface {{{ 138 | 139 | " Set 7 lines to the cursor - when moving vertically using j/k 140 | set so=7 141 | 142 | " Turn on the WiLd menu 143 | set wildmenu 144 | " Tab-complete files up to longest unambiguous prefix 145 | set wildmode=list:longest,full 146 | 147 | " Always show current position 148 | set ruler 149 | set number 150 | 151 | " Show trailing whitespace 152 | set list 153 | " But only interesting whitespace 154 | if &listchars ==# 'eol:$' 155 | set listchars=tab:>\ ,trail:-,extends:>,precedes:<,nbsp:+ 156 | endif 157 | 158 | " Height of the command bar 159 | set cmdheight=1 160 | 161 | " Configure backspace so it acts as it should act 162 | set backspace=eol,start,indent 163 | set whichwrap+=<,>,h,l 164 | 165 | " Ignore case when searching 166 | set ignorecase 167 | 168 | " When searching try to be smart about cases 169 | set smartcase 170 | 171 | " Highlight search results 172 | set hlsearch 173 | 174 | " Makes search act like search in modern browsers 175 | set incsearch 176 | 177 | " Don't redraw while executing macros (good performance config) 178 | set lazyredraw 179 | 180 | " For regular expressions turn magic on 181 | set magic 182 | 183 | " Show matching brackets when text indicator is over them 184 | set showmatch 185 | " How many tenths of a second to blink when matching brackets 186 | set mat=2 187 | 188 | " No annoying sound on errors 189 | set noerrorbells 190 | set vb t_vb= 191 | 192 | if &term =~ '256color' 193 | " disable Background Color Erase (BCE) so that color schemes 194 | " render properly when inside 256-color tmux and GNU screen. 195 | " see also http://snk.tuxfamily.org/log/vim-256color-bce.html 196 | set t_ut= 197 | endif 198 | 199 | " Force redraw 200 | map r :redraw! 201 | 202 | " Turn mouse mode on 203 | nnoremap ma :set mouse=a 204 | 205 | " Turn mouse mode off 206 | nnoremap mo :set mouse= 207 | 208 | " Default to mouse mode on 209 | set mouse=a 210 | 211 | " Change cursor shape between insert and normal mode in iTerm2.app 212 | if $TERM_PROGRAM =~ "iTerm" 213 | let &t_SI = "\]50;CursorShape=1\x7" " Vertical bar in insert mode 214 | let &t_EI = "\]50;CursorShape=0\x7" " Block in normal mode 215 | endif 216 | " }}} 217 | 218 | " Colors and Fonts {{{ 219 | 220 | try 221 | colorscheme wombat256mod 222 | catch 223 | endtry 224 | 225 | " Adjust signscolumn to match wombat 226 | hi! link SignColumn LineNr 227 | 228 | " Use pleasant but very visible search hilighting 229 | hi Search ctermfg=white ctermbg=173 cterm=none guifg=#ffffff guibg=#e5786d gui=none 230 | hi! link Visual Search 231 | 232 | " Match wombat colors in nerd tree 233 | hi Directory guifg=#8ac6f2 234 | 235 | " Searing red very visible cursor 236 | hi Cursor guibg=red 237 | 238 | " Don't blink normal mode cursor 239 | set guicursor=n-v-c:block-Cursor 240 | set guicursor+=n-v-c:blinkon0 241 | 242 | " Set extra options when running in GUI mode 243 | if has("gui_running") 244 | set guioptions-=T 245 | set guioptions-=e 246 | set guitablabel=%M\ %t 247 | endif 248 | set t_Co=256 249 | 250 | " Set utf8 as standard encoding and en_US as the standard language 251 | if !has('nvim') 252 | " Only set this for vim, since neovim is utf8 as default and setting it 253 | " causes problems when reloading the .vimrc configuration 254 | set encoding=utf8 255 | endif 256 | 257 | " Use Unix as the standard file type 258 | set ffs=unix,dos,mac 259 | 260 | " Use large font by default in MacVim 261 | set gfn=Monaco:h19 262 | 263 | " Use powerline fonts for airline 264 | if !exists('g:airline_symbols') 265 | let g:airline_symbols = {} 266 | endif 267 | 268 | let g:airline_powerline_fonts = 1 269 | let g:airline_symbols.space = "\ua0" 270 | " }}} 271 | 272 | " Files, backups and undo {{{ 273 | 274 | " Turn backup off, since most stuff is in Git anyway... 275 | set nobackup 276 | set nowb 277 | set noswapfile 278 | 279 | " Source the vimrc file after saving it 280 | augroup sourcing 281 | autocmd! 282 | if has('nvim') 283 | autocmd bufwritepost init.vim source $MYVIMRC 284 | else 285 | autocmd bufwritepost .vimrc source $MYVIMRC 286 | endif 287 | augroup END 288 | 289 | " Open file prompt with current path 290 | nmap e :e =expand("%:p:h") . '/' 291 | 292 | " Show undo tree 293 | nmap u :MundoToggle 294 | 295 | " Fuzzy find files 296 | nnoremap :CtrlP 297 | let g:ctrlp_max_files=0 298 | let g:ctrlp_show_hidden=1 299 | let g:ctrlp_custom_ignore = { 'dir': '\v[\/](.git|.cabal-sandbox|.stack-work)$' } 300 | 301 | " }}} 302 | 303 | " Text, tab and indent related {{{ 304 | 305 | " Use spaces instead of tabs 306 | set expandtab 307 | 308 | " 1 tab == 2 spaces, unless the file is already 309 | " using tabs, in which case tabs will be inserted. 310 | set shiftwidth=2 311 | set softtabstop=2 312 | set tabstop=2 313 | 314 | " Linebreak on 500 characters 315 | set lbr 316 | set tw=500 317 | 318 | set ai "Auto indent 319 | set si "Smart indent 320 | set wrap "Wrap lines 321 | 322 | " Copy and paste to os clipboard 323 | nmap y "*y 324 | vmap y "*y 325 | nmap d "*d 326 | vmap d "*d 327 | nmap p "*p 328 | vmap p "*p 329 | 330 | " }}} 331 | 332 | " Visual mode related {{{ 333 | 334 | " Visual mode pressing * or # searches for the current selection 335 | " Super useful! From an idea by Michael Naumann 336 | vnoremap * :call VisualSelection('f', '') 337 | vnoremap # :call VisualSelection('b', '') 338 | 339 | " }}} 340 | 341 | " Moving around, tabs, windows and buffers {{{ 342 | 343 | " Treat long lines as break lines (useful when moving around in them) 344 | nnoremap j gj 345 | nnoremap k gk 346 | 347 | noremap h 348 | noremap k 349 | noremap j 350 | noremap l 351 | 352 | " Disable highlight when is pressed 353 | " but preserve cursor coloring 354 | nmap :noh\|hi Cursor guibg=red 355 | 356 | " Return to last edit position when opening files (You want this!) 357 | augroup last_edit 358 | autocmd! 359 | autocmd BufReadPost * 360 | \ if line("'\"") > 0 && line("'\"") <= line("$") | 361 | \ exe "normal! g`\"" | 362 | \ endif 363 | augroup END 364 | " Remember info about open buffers on close 365 | set viminfo^=% 366 | 367 | " Open window splits in various places 368 | nmap sh :leftabove vnew 369 | nmap sl :rightbelow vnew 370 | nmap sk :leftabove new 371 | nmap sj :rightbelow new 372 | 373 | " Manually create key mappings (to avoid rebinding C-\) 374 | let g:tmux_navigator_no_mappings = 1 375 | 376 | nnoremap :TmuxNavigateLeft 377 | nnoremap :TmuxNavigateDown 378 | nnoremap :TmuxNavigateUp 379 | nnoremap :TmuxNavigateRight 380 | 381 | " don't close buffers when you aren't displaying them 382 | set hidden 383 | 384 | " previous buffer, next buffer 385 | nnoremap bp :bp 386 | nnoremap bn :bn 387 | 388 | " close every window in current tabview but the current 389 | nnoremap bo o 390 | 391 | " delete buffer without closing pane 392 | noremap bd :Bd 393 | 394 | " fuzzy find buffers 395 | noremap b :CtrlPBuffer 396 | 397 | " Neovim terminal configurations 398 | if has('nvim') 399 | " Use to escape terminal insert mode 400 | tnoremap 401 | " Make terminal split moving behave like normal neovim 402 | tnoremap h 403 | tnoremap j 404 | tnoremap k 405 | tnoremap l 406 | endif 407 | 408 | 409 | " }}} 410 | 411 | " Status line {{{ 412 | 413 | " Always show the status line 414 | set laststatus=2 415 | 416 | " }}} 417 | 418 | " Editing mappings {{{ 419 | 420 | " Utility function to delete trailing white space 421 | func! DeleteTrailingWS() 422 | exe "normal mz" 423 | %s/\s\+$//ge 424 | exe "normal `z" 425 | endfunc 426 | 427 | " }}} 428 | 429 | " Spell checking {{{ 430 | 431 | " Pressing ,ss will toggle and untoggle spell checking 432 | map ss :setlocal spell! 433 | 434 | " }}} 435 | 436 | " Helper functions {{{ 437 | 438 | function! CmdLine(str) 439 | exe "menu Foo.Bar :" . a:str 440 | emenu Foo.Bar 441 | unmenu Foo 442 | endfunction 443 | 444 | function! VisualSelection(direction, extra_filter) range 445 | let l:saved_reg = @" 446 | execute "normal! vgvy" 447 | 448 | let l:pattern = escape(@", '\\/.*$^~[]') 449 | let l:pattern = substitute(l:pattern, "\n$", "", "") 450 | 451 | if a:direction == 'b' 452 | execute "normal ?" . l:pattern . "^M" 453 | elseif a:direction == 'gv' 454 | call CmdLine("vimgrep " . '/'. l:pattern . '/' . ' **/*.' . a:extra_filter) 455 | elseif a:direction == 'replace' 456 | call CmdLine("%s" . '/'. l:pattern . '/') 457 | elseif a:direction == 'f' 458 | execute "normal /" . l:pattern . "^M" 459 | endif 460 | 461 | let @/ = l:pattern 462 | let @" = l:saved_reg 463 | endfunction 464 | 465 | " }}} 466 | 467 | " Slime {{{ 468 | 469 | vmap rs SendSelectionToTmux 470 | nmap rs NormalModeSendToTmux 471 | nmap rv SetTmuxVars 472 | 473 | " }}} 474 | 475 | " NERDTree {{{ 476 | 477 | " Close nerdtree after a file is selected 478 | let NERDTreeQuitOnOpen = 1 479 | 480 | function! IsNERDTreeOpen() 481 | return exists("t:NERDTreeBufName") && (bufwinnr(t:NERDTreeBufName) != -1) 482 | endfunction 483 | 484 | function! ToggleFindNerd() 485 | if IsNERDTreeOpen() 486 | exec ':NERDTreeToggle' 487 | else 488 | exec ':NERDTreeFind' 489 | endif 490 | endfunction 491 | 492 | " If nerd tree is closed, find current file, if open, close it 493 | nmap f :call ToggleFindNerd() 494 | nmap F :NERDTreeToggle 495 | 496 | " }}} 497 | 498 | " Alignment {{{ 499 | 500 | " Stop Align plugin from forcing its mappings on us 501 | let g:loaded_AlignMapsPlugin=1 502 | " Align on equal signs 503 | map a= :Align = 504 | " Align on commas 505 | map a, :Align , 506 | " Align on pipes 507 | map a :Align 508 | " Prompt for align character 509 | map ap :Align 510 | " }}} 511 | 512 | " Git {{{ 513 | 514 | let g:extradite_width = 60 515 | " Hide messy Ggrep output and copen automatically 516 | function! NonintrusiveGitGrep(term) 517 | execute "copen" 518 | " Map 't' to open selected item in new tab 519 | execute "nnoremap t T" 520 | execute "silent! Ggrep " . a:term 521 | execute "redraw!" 522 | endfunction 523 | 524 | command! -nargs=1 GGrep call NonintrusiveGitGrep() 525 | nmap gs :Gstatus 526 | nmap gg :copen:GGrep 527 | nmap gl :Extradite! 528 | nmap gd :Gdiff 529 | nmap gb :Gblame 530 | 531 | function! CommittedFiles() 532 | " Clear quickfix list 533 | let qf_list = [] 534 | " Find files committed in HEAD 535 | let git_output = system("git diff-tree --no-commit-id --name-only -r HEAD\n") 536 | for committed_file in split(git_output, "\n") 537 | let qf_item = {'filename': committed_file} 538 | call add(qf_list, qf_item) 539 | endfor 540 | " Fill quickfix list with them 541 | call setqflist(qf_list) 542 | endfunction 543 | 544 | " Show list of last-committed files 545 | nnoremap g? :call CommittedFiles():copen 546 | 547 | " }}} 548 | 549 | " Completion {{{ 550 | set completeopt+=longest 551 | 552 | " Use buffer words as default tab completion 553 | let g:SuperTabDefaultCompletionType = '' 554 | 555 | " }}} 556 | 557 | " Customization {{{ 558 | execute 'source '. hvn_config_haskell 559 | if filereadable(hvn_config_post) 560 | execute 'source '. hvn_config_post 561 | endif 562 | 563 | " }}} 564 | -------------------------------------------------------------------------------- /scripts/setup_haskell.hs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stack 2 | {- stack 3 | script 4 | --resolver lts-14.12 5 | --package aeson 6 | --package ansi-terminal 7 | --package directory 8 | --package foldl 9 | --package mtl 10 | --package raw-strings-qq 11 | --package stache 12 | --package system-filepath 13 | --package text 14 | --package transformers 15 | --package turtle 16 | -} 17 | 18 | {-# LANGUAGE FlexibleContexts #-} 19 | {-# LANGUAGE LambdaCase #-} 20 | {-# LANGUAGE NamedFieldPuns #-} 21 | {-# LANGUAGE OverloadedStrings #-} 22 | {-# LANGUAGE QuasiQuotes #-} 23 | 24 | import Control.Applicative ((<|>), empty) 25 | import Control.Exception (bracket_) 26 | import qualified Control.Foldl as Foldl 27 | import Control.Monad (mfilter, unless, when) 28 | import Control.Monad.IO.Class (MonadIO, liftIO) 29 | import Control.Monad.Reader (MonadReader, ask, asks, runReaderT) 30 | import Data.Aeson ((.=), object) 31 | import Data.Foldable (forM_) 32 | import Data.Maybe (listToMaybe) 33 | import Data.Monoid ((<>)) 34 | import qualified Data.Text as Text 35 | import Data.Text (Text) 36 | import qualified Data.Text.IO as Text.IO 37 | import Data.Text.Lazy (toStrict) 38 | import Filesystem.Path.CurrentOS (FilePath, (), decodeString) 39 | import qualified Filesystem.Path.CurrentOS as FS 40 | import Prelude hiding (FilePath) 41 | import qualified System.Console.ANSI as ANSI 42 | import qualified System.IO 43 | import System.Info (os) 44 | import System.Directory (getTemporaryDirectory) 45 | import Text.Mustache (Template, renderMustache) 46 | import Text.Mustache.Compile.TH (mustache) 47 | import Text.RawString.QQ (r) 48 | import qualified Turtle 49 | 50 | data HvnConfig = HvnConfig 51 | { hvnCfgHome :: FilePath 52 | , hvnCfgDest :: FilePath 53 | , hvnCfgHoogleDb :: Bool 54 | , hvnCfgHelperBinaries :: Bool 55 | } deriving (Show) 56 | 57 | data HvnArgs = HvnArgs 58 | { hvnArgsNoHoogleDb :: Bool 59 | , hvnArgsNoHelperBinaries :: Bool 60 | } deriving (Show) 61 | 62 | main :: IO () 63 | main = do 64 | HvnArgs {hvnArgsNoHoogleDb, hvnArgsNoHelperBinaries} <- 65 | Turtle.options "Haskell Vim Now - setup Haskell specifics" cliParser 66 | print HvnArgs {hvnArgsNoHoogleDb, hvnArgsNoHelperBinaries} 67 | hvnCfgHome <- hvnHomeDir 68 | let hvnCfgDest = hvnCfgHome textToFilePath hvn 69 | hvnCfgHoogleDb = not hvnArgsNoHoogleDb 70 | hvnCfgHelperBinaries = not hvnArgsNoHelperBinaries 71 | runReaderT setup HvnConfig { hvnCfgHome 72 | , hvnCfgDest 73 | , hvnCfgHoogleDb 74 | , hvnCfgHelperBinaries 75 | } 76 | Turtle.exit Turtle.ExitSuccess 77 | 78 | cliParser :: Turtle.Parser HvnArgs 79 | cliParser = HvnArgs 80 | <$> Turtle.switch "no-hoogle" 'g' 81 | "Disable Hoogle database generation." 82 | <*> Turtle.switch "no-helper-bins" 'b' 83 | "Disable install of helper binaries (mainly for CI)." 84 | 85 | hvnHomeDir :: (MonadIO m) => m FilePath 86 | hvnHomeDir = do 87 | mXdgConfigHome <- Turtle.need "XDG_CONFIG_HOME" 88 | maybe 89 | (fmap ( ".config") Turtle.home) 90 | (pure . textToFilePath) 91 | mXdgConfigHome 92 | 93 | setup :: (MonadIO m, MonadReader HvnConfig m) => m () 94 | setup = do 95 | setupHaskell 96 | msg "HASKELL VIM NOW install of Haskell specifics successfully finished" 97 | 98 | setupHaskell :: (MonadIO m, MonadReader HvnConfig m) => m () 99 | setupHaskell = do 100 | HvnConfig {hvnCfgDest, hvnCfgHoogleDb, hvnCfgHelperBinaries} <- ask 101 | msg "Setting up GHC if needed..." 102 | (exitCode, stdout, stderr) <- Turtle.procStrictWithErr "stack" ["setup", "--verbosity", "warning"] empty 103 | case exitCode of 104 | (Turtle.ExitFailure retCode) -> do 105 | err $ "Stack setup failed with exit code: " <> (Text.pack . show $ retCode) 106 | err $ "stderr: " <> stderr 107 | err $ "stdout: " <> stdout 108 | Turtle.exit (Turtle.ExitFailure 1) 109 | Turtle.ExitSuccess -> do 110 | stackBinPath <- 111 | commandSubstitution "stack --verbosity 0 path --local-bin" 112 | stackGlobalDir <- 113 | commandSubstitution "stack --verbosity 0 path --stack-root" 114 | stackGlobalConfig <- 115 | commandSubstitution "stack --verbosity 0 path --config-location" 116 | stackResolver <- stackResolverText . textToFilePath $ stackGlobalConfig 117 | detail $ "Stack bin path: " <> stackBinPath 118 | detail $ "Stack global path: " <> stackGlobalDir 119 | detail $ "Stack global config location: " <> stackGlobalConfig 120 | detail $ "Stack resolver: " <> stackResolver 121 | let emptyStackPath = any Text.null 122 | [ stackBinPath 123 | , stackGlobalDir 124 | , stackGlobalConfig 125 | ] 126 | when emptyStackPath $ do 127 | err "Incorrect stack paths." 128 | Turtle.exit (Turtle.ExitFailure 1) 129 | let stackBinUnderCfgDest = hvnCfgDest ".stack-bin" 130 | mkDirLink (textToFilePath stackBinPath) stackBinUnderCfgDest 131 | when hvnCfgHelperBinaries $ do 132 | msg "Installing helper binaries..." 133 | let hvnHelperBinDir = hvnCfgDest "hvn-helper-binaries" 134 | Turtle.mktree hvnHelperBinDir 135 | Turtle.cd hvnHelperBinDir 136 | stackYamlExists <- Turtle.testfile (hvnHelperBinDir "stack.yaml") 137 | unless stackYamlExists $ do 138 | -- Install ghcide from source for maximum 139 | -- out-of-the-box compatibility. 140 | installGhcide 141 | -- Stack dependency solving requires cabal to be on the PATH. 142 | stackInstall stackResolver "cabal-install" True 143 | -- Install hindent via default LTS 144 | stackInstall stackResolver "hindent" True 145 | let helperDependenciesCabalText = 146 | renderMustache helperDependenciesCabalTemplate $ 147 | object ["dependencies" .= helperDependencies] 148 | liftIO $ 149 | Turtle.writeTextFile 150 | "dependencies.cabal" 151 | (toStrict helperDependenciesCabalText) 152 | Turtle.stdout (Turtle.input "dependencies.cabal") 153 | let solverCommand = "stack init --resolver " <> stackResolver 154 | <> " --install-ghc" 155 | -- XXX for best results we should solve and install each one of them 156 | -- independently rather than solving them together. It becomes more 157 | -- difficult for the solver to find a workable build plan when we 158 | -- solve them together. 159 | -- Solve the versions of all helper binaries listed in 160 | -- dependencies.cabal. 161 | solverResult <- Turtle.shell solverCommand empty 162 | case solverResult of 163 | (Turtle.ExitFailure retCode) -> do 164 | err $ 165 | "\"" <> solverCommand <> "\" failed with error " <> 166 | (Text.pack . show $ retCode) 167 | Turtle.exit (Turtle.ExitFailure 1) 168 | Turtle.ExitSuccess -> do 169 | Turtle.cp "stack.yaml" "stack.yaml.bak" 170 | Turtle.output 171 | "stack.yaml" 172 | (mfilter 173 | ((> 1) . Text.length . Turtle.lineToText) 174 | (Turtle.sed 175 | (Turtle.begins ("#" <|> "user-message" <|> " ") *> 176 | pure "") 177 | (Turtle.input "stack.yaml.bak"))) 178 | -- XXX I could not figure out how to keep the ">" sign unescaped in 179 | -- mustache, so had to treat this especially. If we can do that then 180 | -- we can push this as well in helperDependencies. 181 | forM_ (map (head . Text.words) helperDependencies) $ 182 | \dep -> stackInstall stackResolver dep True 183 | -- XXX we should remove the temporary dir after installing to reclaim 184 | -- unnecessary space. 185 | when hvnCfgHoogleDb $ do 186 | msg "Building Hoogle database..." 187 | Turtle.sh 188 | (Turtle.shell 189 | (filePathToText (textToFilePath stackBinPath "hoogle") <> 190 | " generate") 191 | empty) 192 | liftIO $ Turtle.writeTextFile (hvnCfgDest ".vim" "coc-settings.json") cocSettings 193 | 194 | stackResolverText :: (MonadIO m) => FilePath -> m Text 195 | stackResolverText stackYamlPath = do 196 | let defaultResolver = "lts" 197 | mLine <- Turtle.fold 198 | (Turtle.grep stackResolverPattern (Turtle.input stackYamlPath)) 199 | Foldl.head 200 | case mLine of 201 | Nothing -> pure defaultResolver 202 | (Just line) -> do 203 | let lineText = Turtle.lineToText line 204 | let mStackResolver = listToMaybe 205 | . Turtle.match stackResolverPattern 206 | $ lineText 207 | case mStackResolver of 208 | Nothing -> do 209 | err "Failed to determine stack resolver" 210 | pure defaultResolver 211 | (Just resolver) -> pure resolver 212 | 213 | stackResolverPattern :: Turtle.Pattern Text 214 | stackResolverPattern = Turtle.prefix 215 | (Turtle.skip "resolver:" *> Turtle.skip Turtle.spaces *> 216 | Turtle.plus Turtle.dot) 217 | 218 | stackInstall :: (MonadIO m) => Text -> Text -> Bool -> m () 219 | stackInstall resolver package exitOnFailure = do 220 | let installCommand = 221 | "stack --resolver " <> resolver <> " install " <> package <> 222 | " --install-ghc --verbosity warning" 223 | detail installCommand 224 | installResult <- Turtle.shell installCommand empty 225 | case installResult of 226 | (Turtle.ExitFailure retCode) -> do 227 | err $ 228 | "\"" <> installCommand <> "\" failed with error " <> 229 | (Text.pack . show $ retCode) 230 | case exitOnFailure of 231 | True -> Turtle.exit (Turtle.ExitFailure 1) 232 | False -> handleFailure package where 233 | handleFailure :: (MonadIO m) => Text -> m () 234 | handleFailure _ = pure() 235 | Turtle.ExitSuccess -> pure () 236 | 237 | helperDependencies :: [Text] 238 | helperDependencies = 239 | [ "apply-refact" 240 | , "hlint" 241 | , "hoogle" 242 | ] 243 | 244 | helperDependenciesCabalTemplate :: Template 245 | helperDependenciesCabalTemplate = [mustache|name: dependencies 246 | version: 0.1.0.0 247 | synopsis: helper binaries for vim 248 | homepage: https://github.com/begriffs/haskell-vim-now 249 | license: MIT 250 | author: Joe Nelson 251 | maintainer: cred+github@begriffs.com 252 | category: Development 253 | build-type: Simple 254 | cabal-version: >=1.10 255 | 256 | library 257 | build-depends: base >=4.9 && < 5 258 | {{#dependencies}} 259 | , {{.}} 260 | {{/dependencies}} 261 | default-language: Haskell2010 262 | |] 263 | 264 | msg :: (MonadIO m) => Text -> m () 265 | msg = 266 | consoleLog 267 | System.IO.stdout 268 | [ ANSI.SetColor ANSI.Foreground ANSI.Vivid ANSI.Green 269 | , ANSI.SetConsoleIntensity ANSI.NormalIntensity 270 | ] 271 | 272 | warn :: (MonadIO m) => Text -> m () 273 | warn = 274 | consoleLog 275 | System.IO.stdout 276 | [ ANSI.SetColor ANSI.Foreground ANSI.Vivid ANSI.Yellow 277 | , ANSI.SetConsoleIntensity ANSI.BoldIntensity 278 | ] 279 | 280 | err :: (MonadIO m) => Text -> m () 281 | err = 282 | consoleLog 283 | System.IO.stderr 284 | [ ANSI.SetColor ANSI.Foreground ANSI.Vivid ANSI.Red 285 | , ANSI.SetConsoleIntensity ANSI.BoldIntensity 286 | ] 287 | 288 | detail :: (MonadIO m) => Text -> m () 289 | detail txt = 290 | consoleLog 291 | System.IO.stdout 292 | [ ANSI.SetConsoleIntensity ANSI.BoldIntensity ] 293 | (" " <> txt) 294 | 295 | consoleLog :: (MonadIO m) => System.IO.Handle -> [ANSI.SGR] -> Text -> m () 296 | consoleLog handle sgrs txt = liftIO $ 297 | bracket_ 298 | (ANSI.setSGR [ANSI.Reset] *> ANSI.setSGR sgrs) 299 | (ANSI.setSGR [ANSI.Reset]) 300 | (Text.IO.hPutStrLn handle txt) 301 | 302 | -- a.k.a. $(...) 303 | commandSubstitution :: (MonadIO m) => Text -> m Text 304 | commandSubstitution cmd = do 305 | mval <- Turtle.fold (Turtle.inshell cmd empty) Foldl.head 306 | pure $ maybe mempty Turtle.lineToText mval 307 | 308 | -- For Unix, could use System.Posix.Files createSymbolicLink instead of raw 309 | -- shell. 310 | mkDirLink :: (MonadIO m) => FilePath -> FilePath -> m () 311 | mkDirLink src dest = 312 | Turtle.sh $ do 313 | detail $ filePathToText dest <> " -> " <> filePathToText src 314 | if isWindows 315 | then Turtle.shell 316 | ("mklink /d " <> filePathToText dest <> " " <> filePathToText src) 317 | empty 318 | else Turtle.shell 319 | ("ln -sf " <> filePathToText src <> " " <> filePathToText dest) 320 | empty 321 | 322 | isWindows :: Bool 323 | isWindows = os == "mingw32" 324 | 325 | filePathToText :: FilePath -> Text 326 | filePathToText = Turtle.format Turtle.fp 327 | 328 | textToFilePath :: Text -> FilePath 329 | textToFilePath = FS.fromText 330 | 331 | hvn :: Text 332 | hvn = "haskell-vim-now" 333 | 334 | type HelperRepository = Text 335 | type HelperTool = Text 336 | type Resolver = Text 337 | 338 | -- install ghcide, write compiler and file type plugin files 339 | installGhcide :: (MonadIO m, MonadReader HvnConfig m) => m () 340 | installGhcide = do 341 | hvnCfgDest' <- asks hvnCfgDest 342 | gitCloneInstall "https://github.com/digital-asset/ghcide.git" "ghcide" Nothing 343 | let vimDir = hvnCfgDest' <> ".vim" 344 | writeCompilerFile vimDir 345 | writeaFtPluginFile vimDir 346 | where 347 | mkdir' d = Turtle.testpath d >>= \exists -> unless exists (Turtle.mktree d) 348 | toFile f c = liftIO $ Turtle.writeTextFile f c 349 | writeaFtPluginFile d = do 350 | let d' = d "after" "ftplugin" 351 | mkdir' d' 352 | toFile (d' "haskell.vim") "compiler ghcide" 353 | writeCompilerFile d = do 354 | let d' = d <> "compiler" 355 | mkdir' d' 356 | toFile (d' "ghcide.vim") [r| 357 | CompilerSet errorformat=%-Gghcide\ %s 358 | CompilerSet errorformat+=%-GReport\ bugs\ at\ %s 359 | CompilerSet errorformat+=%-GStep\ %s 360 | CompilerSet errorformat+=%-GFound\ %s 361 | CompilerSet errorformat+=%-GCradle\ %s 362 | CompilerSet errorformat+=%-GRange:\ %s 363 | CompilerSet errorformat+=%-GFile:\ %s 364 | CompilerSet errorformat+=%-GSource:\ %s 365 | CompilerSet errorformat+=%-GSeverity:\ %s 366 | CompilerSet errorformat+=%-GCompleted\ %s 367 | " exclude empty or whitespace-only lines 368 | CompilerSet errorformat+=%-G\\s%# 369 | CompilerSet errorformat+=%EMessage:%\\s%#%> 370 | CompilerSet errorformat+=%C%\\s%#%f:%l:%c:%\\s%#error:%\\s%#%> 371 | CompilerSet errorformat+=%C%m 372 | CompilerSet errorformat+=%ZCompleted%m 373 | 374 | setlocal makeprg=ghcide\ %\ 2>&1\ \\\|\ sed\ 's/\\x1B\\[[0-9;]*m//g' 375 | |] 376 | 377 | gitCloneInstall :: MonadIO m => HelperRepository -> HelperTool -> Maybe Resolver -> m () 378 | gitCloneInstall repo tool maybeResolver = Turtle.sh $ do 379 | liftIO $ msg (unwords' ["clone", repo, "and install", tool]) 380 | tmp <- liftIO getTemporaryDirectory >>= return . decodeString 381 | Turtle.mktempdir tmp hvn >>= Turtle.pushd 382 | inShell (unwords' ["git clone", repo, tool]) 383 | Turtle.pushd (textToFilePath tool) 384 | inShell (unwords' ["stack", resolver, "install"]) 385 | where 386 | resolver = case maybeResolver of 387 | Nothing -> "" 388 | Just x -> unwords'["--resolver ", x] 389 | unwords' = Text.intercalate " " 390 | inShell cmd = 391 | Turtle.shell cmd empty 392 | >>= \case 393 | Turtle.ExitSuccess -> return () 394 | Turtle.ExitFailure n -> 395 | err (unwords' [cmd, "failed with exit code:", Turtle.repr n]) 396 | >> Turtle.exit (Turtle.ExitFailure 1) 397 | 398 | cocSettings :: Text 399 | cocSettings = [r|{ 400 | "languageserver": { 401 | "haskell": { 402 | "command": "ghcide", 403 | "args": [ 404 | "--lsp" 405 | ], 406 | "rootPatterns": [ 407 | ".stack.yaml", 408 | ".hie-bios", 409 | "BUILD.bazel", 410 | "cabal.config", 411 | "package.yaml" 412 | ], 413 | "filetypes": [ 414 | "hs", 415 | "lhs", 416 | "haskell" 417 | ] 418 | } 419 | } 420 | } 421 | |] 422 | --------------------------------------------------------------------------------