VimでFSharpを実行（Run)・テスト（Test)・デバッグ（Debug）する

```
=== 2018/05/21 Mon ===
はてなブログに以前書いたものを大幅に加筆・訂正したものです
======================
```

この記事はF# Advent Calendar2017 ２日目の記事です ( see : [F# Advent Calendar 2017 - Qiita](https://qiita.com/advent-calendar/2017/fsharp) )
この記事はVim2 Advent Calendar2017 ２日目の記事です ( see : [Vim2 Advent Calendar 2017 - Qiita](https://qiita.com/advent-calendar/2017/vim2) )


# IDEを使わなければ、コンピュータが自動でやってくれることなどほとんどなく、あとは自分の手でやるしかないのです。プログラミングとは元々そういうものだったはずです


see : [Hello, Worldから始めよう | プログラマが知るべき97のこと](https://ja.wikisource.org/wiki/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9E%E3%81%8C%E7%9F%A5%E3%82%8B%E3%81%B9%E3%81%8D97%E3%81%AE%E3%81%93%E3%81%A8/Hello,_World%E3%81%8B%E3%82%89%E5%A7%8B%E3%82%81%E3%82%88%E3%81%86)

# Greeting

こんにちは！最近ポンド円でほぼ死にかけの`callmekohei`です！みなさまいかがお過ごしでしょうか？今回は`F#`と`Vim`の`Advent Calendar`に同時にポストします！

思えばなんで僕、`Vim`で`F#`のコード書いてるんでしょうね？`Visual Studio`とか`Visual Studio Code`とか他にも書きやすい環境があるのに。。。なんというかですね、なんというかですね、`Vim`に`F#`はウイスキーに生ハムぐらい合うんですよ。も、もちろん個人の意見ですがwww。まぁ、あれですよ。僕が複雑なこと理解できないやつなんでシンプルな環境の方が好きなんですよね。

というわけで、`Vim`で`F#`のコード書くときのちょっとした環境構築について書いてみます。というか僕自身が趣味でコード書いてるのでお仕事とかで使うには辛いかもですが、まぁすごくシンプルな環境なんでお勉強とかちょっとしたテストとかにはいいと思ふのですよ。

それと環境構築は`Mac OSX`で書いてますが、`Windows`でも`Linux`でもおんなじ感じですっ（だと思う・・・）

もしなにかあれば `@callmekohei ( twitter )`まで〜

##  Environment

```shell
$ sw_vers 
ProductName:	Mac OS X
ProductVersion:	10.13.4
BuildVersion:	17E202
```

## 00 - Install

まずは下地を整えませう〜

### 01. インストーラーをゲッツ！

`Mac` の場合`Homebrew`というインストーラーがあるのでこれをまずゲッツします。
[Homebrewのホームページ](https://brew.sh/)からコマンドを`terminal`で実行します。

Example of install Homebrew ( 2018/05/21 Mon )

```shell
$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 
```

### 02. 各種アプリ・コマンドをゲッツ

`vim`はテキストエディタ、`mono`は`F#`の実行環境、そして`Paket`は`.NET library`ダウンローダーです。

```
$ brew install vim
$ brew install mono
$ brew install paket
```

`.bash_profile` or `.bashrc`に書く

```
export MONO_GAC_PREFIX="/usr/local"
```

### 03. `Vim`のプラグインをゲッツ

see also : [Vim におけるプラグイン管理についてまとめてみた](https://qiita.com/tanabee/items/e2064c5ce59c85915940)

ここでは例えば`dein.vim`というプラグインダウンローダー（`plugin manager`）を使ってみます。

#### 01. `dein`フォルダーと`dein.toml`ファイルを作成します。たとえばこんな感じ。

```shell
$ mkdir -p $HOME/.config/nvim/dein/
$ touch $HOME/.config/nvim/dein.toml
```

#### 02. `.vimrc`に下記を書きます。

```vim
" setting of dein

" ====== Fit your path!!! =====
let s:dein_dir = $HOME . '/.config/nvim/dein'
let s:toml     = $HOME . '/.config/nvim/dein.toml'
" =============================

let s:dein_repo_dir = s:dein_dir . '/repos/github.com/Shougo/dein.vim'

if !isdirectory(s:dein_repo_dir)
    execute '!git clone https://github.com/Shougo/dein.vim' s:dein_repo_dir
endif

execute 'set runtimepath+=' . s:dein_repo_dir

if dein#load_state(s:dein_dir)
    call dein#begin(s:dein_dir)
    call dein#load_toml(s:toml, {'lazy': 0})
    call dein#end()
    call dein#save_state()
endif

if dein#check_install()
    call dein#install()
endif

" turn on plugins
set nocompatible
syntax enable
filetype plugin indent on
```

#### 03. `dein.toml` ファイルにゲッツしたいプラグインを書いてみる

```toml
[[plugins]]
repo = 'Shougo/dein.vim'

#----------------------------------------------------------
# Aquires for running Neovim-plugins on Vim8
#----------------------------------------------------------
[[plugins]]
repo = 'roxma/nvim-yarp'

[[plugins]]
repo = 'roxma/vim-hug-neovim-rpc'

#----------------------------------------------------------
# Auto-complete for F# script
#----------------------------------------------------------
[[plugins]]
repo = 'Shougo/deoplete.nvim'
hook_add = '''
    let g:deoplete#enable_at_startup = 1
'''

[[plugins]]
repo = 'callmekohei/deoplete-fsharp'
build = 'bash install.bash'
hook_add = '''
    call deoplete#custom#option({
    \ 'auto_complete_delay': 0,
    \ 'ignore_case': v:true,
    \ })

    " remove duplicate candidates
    call deoplete#custom#source('_',
    \ 'converters', ['remove_overlap'])

    " Disable the candidates in Comment/String syntaxes.
    call deoplete#custom#source('_',
    \ 'disabled_syntaxes', ['Comment', 'String'])
'''

#----------------------------------------------------------
# Debug for F# script
#----------------------------------------------------------
[[plugins]]
repo = 'callmekohei/tigaDebugger'

#----------------------------------------------------------
# Run fsharp command
#----------------------------------------------------------
[[plugins]]
repo = 'Shougo/vimproc.vim'
build = 'make'

[[plugins]]
repo = 'thinca/vim-quickrun'
hook_add = '''
    set splitright
    let g:quickrun_config = {}

	let g:quickrun_config._ = {
        \  'runner'                          : 'vimproc'
        \ ,'runner/vimproc/updatetime'       : 60
        \ ,'hook/time/enable'                : 1
        \ ,'hook/time/format'                : "\n*** time : %g s ***"
        \ ,'hook/time/dest'                  : ''
        \ ,"outputter/buffer/split"          : 'vertical'
        \ ,'outputter/buffer/close_on_empty' : 1
    \}

	let g:quickrun_config.fsharp = {
        \  'command'                         : 'fsharpi --readline-'
        \ ,'tempfile'                        : '%{tempname()}.fsx'
        \ ,'runner'                          : 'concurrent_process'
        \ ,'runner/concurrent_process/load'  : '#load "%S";;'
        \ ,'runner/concurrent_process/prompt': '> '
    \}
'''

#----------------------------------------------------------
# Show indent guide lines
#----------------------------------------------------------
[[plugins]]
repo = 'nathanaelkane/vim-indent-guides'
hook_add = '''
    let g:indent_guides_enable_on_vim_startup = 1
    let g:indent_guides_start_level = 2
    let g:indent_guides_guide_size = 1
    let g:indent_guides_auto_colors = 0
    autocmd VimEnter,Colorscheme * :hi IndentGuidesOdd  guibg=black ctermbg=0
    autocmd VimEnter,Colorscheme * :hi IndentGuidesEven guibg=black ctermbg=0
'''
```

## 01 - 実行`Run`してみよう！

![sample.gif](https://qiita-image-store.s3.amazonaws.com/0/149269/102d1fbd-8a41-0636-09b8-85644054131e.gif)



### 01.コードを書いてみる

```shell
$ vim foo.fsx
```

foo.fsx

```fsharp
"hello" |> stdout.WriteLine
```

### 02.`vim command`を実行

```vim
:w
:QuickRun
```

result

```text
[Loading /Users/callmekohei/tmp/foo.fsx]
hello
namespace FSI_0003


*** time : 0.134435 s ***
```

## 02 - テスト`Test`してみよう！

![image.png](https://qiita-image-store.s3.amazonaws.com/0/149269/98e5ac77-3510-240c-47be-d420de3c4949.png)


### 01.`foo`フォルダを作成し移動
```shell
$ mkdir foo/
$ cd foo/
```

### 02.`persimmon.script`をダウンロード

```shell
$ paket init
$ vim paket.dependencies 

    generate_load_scripts: true
    source https://www.nuget.org/api/v2
    nuget persimmon.script

$ paket install
```

### 03.コードを書いてみる

```shell
$ vim foo.fsx
```

foo.fsx

```fsharp
#load "./.paket/load/net471/main.group.fsx"

open Persimmon
open UseTestNameByReflection
open System.Reflection

/// write your test code here.
let ``a unit test`` = test {
  do! assertEquals 1 2
}

/// print out test report.
new Persimmon.ScriptContext()
|> FSI.collectAndRun( fun _ -> Assembly.GetExecutingAssembly() )
```

### 04.`vim command`を実行

```vim
:w
:QuickRun
```

result

```text
[Loading /Users/callmekohei/tmp/deopletefs/.paket/load/net471/main.group.fsx
 Loading /Users/callmekohei/tmp/foo/foo.fsx]
x
begin FSI_0003.Foo
 Assertion Violated: a unit test
 1. Line Number: 9
    Expect: 1
    Actual: 2
end FSI_0003.Foo
============================== summary ===============================
run: 1, error: 0, violated: 1, skipped: 0, duration: 00:00:00.0008641
namespace FSI_0003.Main

namespace FSI_0003
  val ( a unit test ) : Persimmon.TestCase<unit>


*** time : 0.491798 s ***
```


## 03 - デバッグ`Debug`してみよう！

![tigadebugger.gif](https://qiita-image-store.s3.amazonaws.com/0/149269/86f7f0cf-79ad-0892-84a1-7bfc3d5cd3a0.gif)


### 01. install

[sdb](https://github.com/mono/sdb)と[sdbplg](https://github.com/callmekohei/sdbplg)が必要です。

[sdb](https://github.com/mono/sdb)

```shell
// download
$ git clone --depth 1 https://github.com/mono/sdb

// clone the submodules
$ cd ./sdb/
$ git submodule update --init --recursive

// build
$ make
$ make install
```

[sdbplg](https://github.com/callmekohei/sdbplg)

```shell
// download
$ git clone --depth 1 https://github.com/callmekohei/sdbplg

// build
$ cd ./sdbplg/
$ bash build.bash

// put `.sdb.rc` file on `$HOME`
$ cp .sdb.rc $HOME/

// set path
$ vim $HOME/.bash_profile
    export SDB_PATH=/PATH/TO/sdbplg/bin/
```

### 2. デバッグしてみる！

#### 01.コードを書いてみる

```shell
$ mkdir foo/
$ cd foo/
$ vim foo.fsx
```

foo.fsx

```fsharp
let foo() =
    let mutable x = 1
    x <- 2
    x <- 3
    x

foo ()
|> stdout.WriteLine
```

#### 02.`vim command`を実行（ デバッグシンボル`.mdb`を作成し、`debugger(tigaDebugger)`を起動 ）

```vim
:! sharpc -g --optimize- %
:TigaSetDebugger sdb
:Tiga foo.exe
```

#### 03.デバッガーコマンドを入力

```vim
" set break point
: TigaCommand bp add at foo.fsx 3

" run
: TigaCommand r

" next
: TigaCommand n

" quit debug mode
: TigaQuit
```

#### ショートカットでらくらく操作〜

| Press         | To                                     |
| :------------ | :-------------                         |
| ctrl b        | Add or delete <b>B</span></b>reakpoint |
| ctrl d        | <b>D</b>elete all breakpoints          |
| ctrl r        | <b>R</b>un                             |
| ctrl k        | <b>K</b>ill (Break)                    |
| ctrl p        | Re<b>p</b>lace watch variable          |
| ctrl y        | Add watch variable                     |
| ctrl t        | Delete watch variable                  |
| ctrl n        | Step over ( <b>N</b>ext )              |
| ctrl i        | Step <b>I</b>n                         |
| ctrl u        | Step out ( <b>U</b>p )                 |
| ctrl c        | <b>C</b>ontinue                        |