VimとFortran

# この記事に書いてあること

指が[Vim](https://www.vim.org/)に慣れきってしまっている筆者は、Vimを 「*エミュレート*」 する他のエディタのプラグインでは満足できなくなってしまいました。Fortran, R, Python, bash, 。。。全て Vim (Neovimではない) で編集しているのですが、この記事はFortranをVimで編集する際の環境を徒然なるままに記録した記事です。（特定のツールや手法を推奨する意図はありません。）

# VimとLSP
Vimは、.vimrcに `syntax on` と書くだけでfortranのシンタックスハイライトが可能です。
その他にも、開いたファイルに記述済みの文字列に対しては Ctl+N, Ctl+P などで補完が効きます。
一方、デフォルトでは、ライブラリで定義されたサブルーチンや、Fortran言語自身で定義済みのモジュール変数などは初出箇所で補完することができません。そこで、最近流行りのLSPを導入し、ソースコードの編集を便利にしてみます。

## 自動補完環境を導入
導入方法の一例は[昔の記事](https://qiita.com/sakamoti/items/fa2040ce127e2c3cbb10)に書きましたが、OSなどの環境によって変わると思います。
[Ubuntu20.04だとaptでfortlsを導入できるようです。](https://qiita.com/osada-yum/items/099d244b1fab6c96c595)

なお、2021/12時点の私の.vimrc (一部抜粋)は次のようになっています。

```vim:.vimrc
"--- vim plug settings ---
call plug#begin('~/.vim/plugged')
  Plug 'majutsushi/tagbar'
  Plug 'kshenoy/vim-signature'
  Plug 'Yggdroot/indentLine'
  "--- languadge server protocol
  Plug 'prabirshrestha/asyncomplete.vim'
  Plug 'prabirshrestha/asyncomplete-lsp.vim'
  Plug 'prabirshrestha/vim-lsp'
  Plug 'mattn/vim-lsp-settings'
  Plug 'mattn/vim-lsp-icons'
  "--- Outliner
  Plug 'vim-voom/voom'
call plug#end()
"--- Language server protocol の設定
let g:lsp_diagnostics_enabled=1
let g:lsp_diagnostics_echo_cursor=1
let g:lsp_text_edit_enabled=1
let g:asyncomplete_auto_popup=1
"let g:asyncomplete_auto_completeopt=0
let g:asyncomplete_popup_delay=200
```

## LSPの機能を使ってみる

カレントディレクトリのソースファイルに定義された関数類はLSPによる補完対象となりますが、設定ファイルを適切に編集すれば、その他のフォルダに保存されたソースコードも補完対象とすることができます。
ここでは、基本的な線形代数演算を行うBLASに定義された関数を補完対象に追加してみます。

```bash
mkdir sample-fortls
cd sample-fortls
wget http://www.netlib.org/blas/blas-3.10.0.tgz
tar xvzf blas-3.10.0.tgz
```
なお、上記でダウンロードしたコードはリファレンス実装なので、ここでは定義を参照するためだけに使います。
実際のアプリケーションをビルドする際には、OpenBLASやベンダ製のライブラリを使う方が速いです。

カレントディレクトリ以外の場所をLSPに認識させるためには、`.fortls` ファイルを編集します。
下記のようにBLASのソースコードの場所を指定します。

```:.fortls
{
    "source_dirs": ["./BLAS-3.10.0/", "./"],
    "include_dirs": ["./BLAS-3.10.0/"]
}     
```
- `source_dirs` はインターフェイス定義やサブルーチン本体などが記述されたソースコードの場所です。
-  `include_dirs`は、コンパイラで生成される `.mod` ファイルの置き場所です[^1]。

[^1]: モジュールの記述が無い場合はinclude_dirsの記述は不要かも知れません。

すると、下の図のように、関数名の先頭数文字を記入するだけで補完候補が現れるようになりました。
![Screenshot from 2021-12-10 23-38-16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275690/5b34a1df-9916-53e1-e596-0c84d9a3b635.png)

今回はBLASを例にとりましたが、自作ライブラリや、[NAG数値計算ライブラリ](https://www.nag-j.co.jp/naglib/index.htm)などでも、関数のインターフェイス定義ファイルの場所を指定すれば同様のことが可能です。

# 画面分割とターミナルの活用

Vimは、ノーマルモードで `K` を入力すると、OS付属のマニュアルを表示することができます。
Linuxでは `man daxpy` とすると下図のようなドキュメントを表示されるので、調べたいキーワード上にカーソルを合わせて `K` を押すことで、ソースコードとBLAS関数のリファレンスを行き来しながらコーディングが可能です。
画面分割の片方をターミナルにして、ターミナル上でmanコマンドなども良く使います（下図参照）。

![Screenshot from 2021-12-11 15-35-05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275690/3d895bf3-454f-ea7c-2967-e6c9002bc3f9.png)

短時間で終わるプログラムなら、`:terminal` でターミナルを開き、結果をプロットさせることも、エディタを抜けずに(キーボードから手を離す事無く)実施可能です。

```gnuplot:gnuplot
> p "< ./a.out" u 1:2 w l
```

Fortranに限った話ではありませんが、Vimの画面分割でターミナルを表示させることで、エディタを抜けず、かつマウスへ手を伸ばす必要なく、出来る事は無限に広がります。

- `htop`で並列実行の様子をリアルタイムモニタリング。
- `time`コマンドで時間計測。
- `make` or `cmake` or `configure` or `ctest` or `make check` などのコマンド実行。
- `ranger` でファイルのコピー、移動、シンボリックリンクの作成など[^2]。

[^2]: terminalで`xterm`などをバックグランドで起動し、`Alt+Tab`で画面を切り替え、`ranger`でフォルダをブラウジングすることで、画像ファイルもVimライクに閲覧できます。


# Modern Fortranのデバッグ

[fortranとデバッグ](https://qiita.com/sakamoti/items/c8968c0185a820267c86)という記事では、普通の配列を用いた例のみを示していました。今回は、拡張型を使った場合の表示結果を例示します。

```fortran:サンプルプログラム
module deftype
  use iso_fortran_env
  implicit none
  type t_base
    real(real64),allocatable :: x(:),y(:)
    contains
      procedure :: vecsum => vecsum_base
  end type
  type,extends(t_base) :: t_ex1
    real(real64),allocatable :: z(:)
    contains
      procedure :: vecsum => vecsum_ex1
  end type
  contains
    function vecsum_base(self)result(vout)
      class(t_base) :: self
      real(kind=real64) :: vout
      vout=sum(self%x)+sum(self%y)
    end function
    function vecsum_ex1(self)result(vout)
      class(t_ex1) :: self
      real(kind=real64) :: vout
      vout=sum(self%x)+sum(self%y)+sum(self%z)
    end function
end module deftype
!==============================================
program modern_test
  use deftype
  implicit none
  type(t_base) :: bs
  type(t_ex1) :: ex1
  integer,parameter :: n=10
  allocate(bs%x(n),bs%y(n),source=1d0)
  allocate(ex1%x(n),ex1%y(n),ex1%z(n),source=1d0)
  print*, "bs :",bs%vecsum()
  print*, "ex1:",ex1%vecsum()
end program
!==============================================
```

下図は、次の手順を実施した時の画面例です。上段がgdb入出力、中段が標準出力、下段がソースコードで、これらが連携されて表示されます。(Ubuntu20.04,Vim8.1,gdb9.2の組み合わせ)

1. ソースコード35行目まで実行。
1. メインプログラムのスコープで `bs` と`ex1`を表示。
1. 36行目のprint文に入り、18行目の `vecsub_base` 関数に入る。
1. 引数の `self` を表示。

![Screenshot from 2021-12-11 16-18-09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275690/ec874904-1ef7-1860-1cc9-a93fc3cc3292.png)

プログラム実行中のスコープ如何により、`self`（データが定義されているスコープでは`bs`）のデータ成分が値として表示されていたり、アドレス(?)で表示されていたりします。データの定義部分のスコープに移動すれば値が表示されることがあり、gdb内で`up` や`down`を繰り返していくことで、目的のデータの値を知ることができる場合もあります。

アドレスから値を知る方法については詳しくないので、上記写真の`p self`の結果を値で表示させる方法をご存知の方、コメントいただけると有り難いです。

# おわりに
Vimで`:help fortran` とすると、Fortranに関する文法ハイライトやシンタックスフォールディングの設定項目に関する説明があります。好みに応じて`.vimrc`に設定を追記することで、より幸せなFortranライフを送ることができると思います。