2020/01/20


昨年から Oracle Cloud の無料枠を使っています。2 vCPU な VM を2台も無料で使わせて頂けるという Oracle Cloud さんの大盤振る舞いに感謝しつつ、Oracle Cloud Function でしりとりをしてみました。

Oracle Cloud Function は Fn Project というサーバレスプラットフォームをベースにしており、同プロジェクトの fn というツールを使う事で、他の Fn Project を使うクラウドと同様に操作を行う事ができます。
Fn Project

Open Source. Container-native. Serverless platform.

https://fnproject.io/

fn コマンドは以下の手順でインストールする事ができます。

curl -LSs https://raw.githubusercontent.com/fnproject/cli/master/install | sh

MacOS の場合は homebrew からインストールが可能。

brew update && brew install fn

Windows の場合は僕が送っている pull-request を使わないとエラーになります。ソースを git clone して go build して下さい。

Fix atomicwrite for Windows by mattn - Pull Request #605 - fnproject/cli
https://github.com/fnproject/cli/pull/605

fn コマンドを使える様になるまでは sugimount さんが Qiita に書いている記事を参考にしました。

サーバレスな Oracle Functions (Fn) をやってみた - Qiita
https://qiita.com/sugimount/items/018e08f575ecefb1546c

注意点としては、Oracle Cloud はデフォルトで VCN が作られていますが、Function を使う場合は別途 VCN を作らないと DHCP オプションが一致しないというエラーが出てしまいます。新しく作って下さい。あと sugimount さんが書かれているリポジトリ名と異なる物が実際には用意されるので、詳細は Oracle Cloud のダッシュボードからファンクションを選択し、アプリケーション詳細の「開始」タブを見るとほぼやるべき事が書いてあります。

Oracle Cloud

アプリケーションを作る所まで出来たら、しりとりサーバを作ります。短いのでコード全体を載せます。

package main

import (
    "bufio"
    "context"
    "encoding/json"
    "errors"
    "io"
    "math/rand"
    "strings"

    _ "func/statik"

    fdk "github.com/fnproject/fdk-go"
    "github.com/rakyll/statik/fs"
)

var upper = strings.NewReplacer(
    "ぁ""あ",
    "ぃ""い",
    "ぅ""う",
    "ぇ""え",
    "ぉ""お",
    "ゃ""や",
    "ゅ""ゆ",
    "ょ""よ",
)

func kana2hira(s stringstring {
    return strings.Map(func(r runerune {
        if 0x30A1 <= r && r <= 0x30F6 {
            return r - 0x0060
        }
        return r
    }, s)
}

func hira2kana(s stringstring {
    return strings.Map(func(r runerune {
        if 0x3041 <= r && r <= 0x3096 {
            return r + 0x0060
        }
        return r
    }, s)
}

func search(text string) (stringerror) {
    rs := []rune(text)
    r := rs[len(rs)-1]

    statikFS, err := fs.New()
    if err != nil {
        return "", err
    }
    f, err := statikFS.Open("/dict.txt")
    if err != nil {
        return "", err
    }
    defer f.Close()
    buf := bufio.NewReader(f)

    words := []string{}
    for {
        b, _, err := buf.ReadLine()
        if err != nil {
            break
        }
        line := string(b)
        if ([]rune(line))[0] == r {
            words = append(words, line)
        }
    }
    if len(words) == 0 {
        return "", errors.New("empty dictionary")
    }
    return words[rand.Int()%len(words)], nil
}

func shiritori(text string) (stringerror) {
    text = strings.Replace(text, "ー""", -1)
    if rand.Int()%2 == 0 {
        text = hira2kana(text)
    } else {
        text = kana2hira(text)
    }
    return search(text)
}

func handleText(text string) (stringerror) {
    rs := []rune(strings.TrimSpace(text))
    if len(rs) == 0 {
        return "", errors.New("なんやねん")
    }
    if rs[len(rs)-1] == 'ん' || rs[len(rs)-1] == 'ン' {
        return "", errors.New("出直して来い")
    }
    s, err := shiritori(text)
    if err != nil {
        return "", err
    }
    if s == "" {
        return "", errors.New("わかりません")
    }
    rs = []rune(s)
    if rs[len(rs)-1] == 'ん' || rs[len(rs)-1] == 'ン' {
        s += "\nあっ..."
    }
    return s, nil
}

func main() {
    fdk.Handle(fdk.HandlerFunc(myHandler))
}

type Siritori struct {
    Word string `json:"word"`
}

func myHandler(ctx context.Context, in io.Reader, out io.Writer) {
    var s Siritori
    json.NewDecoder(in).Decode(&s)
    var err error
    s.Word, err = handleText(s.Word)
    if err != nil {
        s.Word = err.Error()
    }
    json.NewEncoder(out).Encode(&s)
}

辞書ファイルは statik を使ってバイナリに埋め込みました。ソースコードは GitHub に置いておきます。

mattn/oracle-cloud-function-siritori
https://github.com/mattn/oracle-cloud-function-siritori/

デプロイは以下の手順で行います。

$ fn  --verbose deploy --app [your-app]

デプロイが完了すると標準入力で JSON を受け取り、標準出力で JSON を出力するコマンドが動く様になっています。

Oracle Cloud

VCN を作らないといけない事に気付くまで結構時間を使ってしまったけど、動く事が分かってからは結構サクサク操作できる様になりました。これだけ遊んでもまだ無料範囲内らしいので、もう少し遊んでみたいと思います。

Posted at by



2020/01/06


はじめに

この文章は、普段から Vim を使い、仕事でも趣味でも Go 言語を書いている僕が、最近どの様な環境で書いているかを説明した文章です。ベストプラクティスではありません。

vim-go と僕

元々、Go 言語はリポジトリの misc/vim に Vim で Go 言語を書くための syntax やコマンドを持っていました。今でもそれらは Google のリポジトリに置かれています。ミュージアム的な物なので、実用的ではないと思います。

GitHub - google/vim-ft-go

A rudimentary Go filetype plugin. Provides syntax files and basic settings for go files. This is a f...

https://github.com/google/vim-ft-go

これを Fatih Arslan 氏が拡張した vim-go が、最近では Vim で Go 言語を書くためのデファクトスタンダードになっていました。vim-go は :GoImport コマンドによる import 文の追加や、:w での保存時にソースコードを整形する機能、:GoDef による定義位置ジャンプなど、色々な機能を搭載していきました。

僕もずっと vim-go を愛用してきて、幾らかコントリビュートもさせて頂いたりしました。正直、vim-go が無いと Go 言語が書けない、そんな風にも思っていました。

大きくなりすぎた vim-go

最近、vim-go が Language Server Client を実装しました。僕は vim-lsp という Language Server Client を使っていたのでそれらを無効にしていました。少し残念だったのが vim-go の Language Server Client の実装に伴い、幾らかバグが発生していました。またシンタックスハイライトにもバグがありました。そしてこれは最近思い始めた事ではないのですが、幾らかの機能を無効にしていても「若干重たいな」と感じていました。

vim-go1
vim-go2

普段から vim-go の幾らかの機能を無効にする為の設定を vimrc に書いていたのですが、ふと「もしかして僕には vim-go は大きすぎるのかもしれない」と思ったのです。

僕が欲しい機能は何だ

改めて、僕が Vim による Go 言語の編集環境で欲しい物を洗い出してみました。

  • Go 言語のシンタックスハイライト
  • 入力補完
  • :GoImport コマンド
  • :w による自動ソースコード整形、およびそのエラー表示

これだけあれば、僕には十分なのです。

Go 言語のシンタックスハイライトに関しては、最近の Vim であればデフォルトランタイムに含まれています。Go 言語は新しい予約語を増やす事はないので、これで十分です。

入力補完に関しては vim-lsp を使っており、こちらの方が色々な言語をサポートしており拡張性もあり、そもそも僕は vim-lsp のコントリビュータでもあるのでこちらが使いたいです。ただし、実は僕は入力補完は使っていなかったりします。Vim による編集の体験を良くする為に Vim や vim-lsp へのコントリビュートは行っていますが、僕本人はほぼ手打ちしていたります。

残るは :GoImport コマンドと :w による自動ソースコード整形なのですが、これだけの為に vim-go に依存する必要はないと思い立ち、vim-go を卒業する事を決めました。

vim-goimports による引越し

GitHub - mattn/vim-goimports: Vim plugin for Minimalist Gopher

vim-goimports Vim plugin for Minimalist Gopher Features Auto-formatting with :w GoImport/GoImportAs ...

https://github.com/mattn/vim-goimports

vim-goimports は、上記の残る2つの問題を解決する為だけに作ったプラグインです。

  • :GoImport コマンド
  • :w による自動ソースコード整形、およびそのエラー表示

メンテナンスやバグ修正は行いますが、これ以上、機能を足す予定はありません。もし「あー、vim-go のあの機能はさすがに欲しかった」と思い出す事があれば、僕はおそらく別のプラグインを作るでしょう。

これは vim-go がイケてないという話ではなく、僕と vim-go との距離感の話です。僕が vim-go に歩みよるよりも、僕のスタイルに何かを足して僕が欲しかった物を作った方が近かった、というだけの話です。

Plug 'prabirshrestha/async.vim'
Plug 'prabirshrestha/asyncomplete.vim'
Plug 'prabirshrestha/asyncomplete-lsp.vim'
Plug 'prabirshrestha/vim-lsp'

Plug 'mattn/vim-lsp-settings'
Plug 'mattn/vim-goimports'

Language Server や入力補完が必要ない方であれば以下でも構いません。

Plug 'mattn/vim-goimports'

これにより、vim-go の幾らかの機能を無効にする為に書いていた設定類を消しました。:GoDef による定義位置ジャンプは vim-lsp が有効になっていれば gd で可能ですし、ソースコードの整形は vim-goimports がやってくれます。ソースコード整形は、本来 Language Server でやれる話ですが、これに関しては絶賛取り組み中で、もう少し時間が必要です。それまでの間は vim-goimports に頼る事にします。また下記のエントリでもご紹介した通り、Vim の歩むべき道は、個別の言語の個別のプラグインによって個々のツールの導入に時間を割いたり vimrc を太らせる事ではないはずなのです。

Big Sky :: Vim をモダンな IDE に変える LSP の設定

Go 言語の IDE 機能を得る為に何か知る必要はありません。Java の IDE 機能を得る為に何か知る必要はありません。HTML の IDE 機能をインストールする為に npm コマンドの使い方を...

https://mattn.kaoriya.net/software/vim/20191231213507.htm

おわりに

上記の引越し作業により、幾分 vimrc の Go に関する項目がスッキリしました。今後、本来は必要なはずだった vim-go の機能が見つかれば、自分で作っていくしかありません。ですが 2019 年末に「こっちの方が近い」と思った自分を信じて、しばらくはやってみます。

Posted at by



2019/12/31


この記事は Go の編集環境について書いていません。昨日書いた、ぼくがかんがえたさいきょうの Vim のこうせい 2019年 年末版は、僕個人の好みに依存するため一緒に書くべきではないですし、おすすめするつもりも無いです。IDE 機能の説明だけ欲しいと思う方もいるでしょうし、また純粋に Go の編集環境だけの説明が欲しいと思う方もいると思ったからです。

はじめに

以前からも「Vim はテキストエディタではない IDE だ」と言われる事は割と多かったのですが、昨今 Language Server Protocol の登場により本当に Vim を「テキストエディタ」と呼べなくなってきてしまう状況になりつつあります。

これまで Vim は、ctags でタグを生成し、Vim から定義位置ジャンプを行い、フォーマットコマンドを使ってファイルを整形してきました。これはとてもうまく行きました。ctags は多くの各種プログラミング言語をサポートしていますし、taglist の様なプラグインを使えば tags ファイルを読み込んでシンボルリストを表示する事もできます。Vim 標準の機能を伝えばタグスタックも使えます。対応していない言語についてはそれ専用の tags 生成プログラムを探せば良いですし、フォーマットコマンドもそれ専用の物を探してくればよかったのです。

しかしこれをずっと繰り返してきた Vim の設定ファイルは一体どの様な物になっていったでしょうか。個々のプラグイン専用の設定項目が散乱し、プラグイン毎にスタイルの異なった設定を行う必要があり、それ専用のタグ生成コマンド、整形コマンドを用意する時間を捻出しなければなりませんでした。入力補完については色々な言語毎に異なる仕様に悩まされるばかりでした。

結局、僕が行き着いた先は「消しやすい Vim の構成」でしたが、この消しやすい Vim の構成であっても、各プログラミング言語毎に用意すべきツール類をまとめる事は出来ませんでした。

Language Server Protocol の登場

そこにやってきたのが Language Server Protocol です。Language Server を導入する事で、各言語に対する定義位置ジャンプや入力補完のインタフェースが統一され、Vim の設定はある程度統一される様になりました。元々の Vim を「昭和」、各プログラミング言語向けのプラグインが氾濫していた頃を「平成」と例えるなら、ようやく Vim は「令和」になったと言って良いと思います。それでも問題が全て消え去った訳でありません。Language Server を導入する為にはその言語に対する知見が必要です。「このプログラミング言語を触ってみたいな」と思った人が定義位置ジャンプや入力補完、ソースファイル整形といった、最近の IDE であればあって当然の機能を得るには、Language Server を導入する為の知見が必要でした。そこで筆者は先日、vim-lsp-settings というプラグインを作りました。

vim-lsp の導入コストを下げるプラグイン vim-lsp-settings を書いた。 - Qiita

これら全ての機能は、テキストエディタと Language Server との間で JSON-RPC を使い、ソースコード本体、コード補完候補、座標情報などを交換する事で実現されています。 温故知新 実...

https://qiita.com/mattn/items/e62b9f16bc487a271a7f

vim-lsp を利用するには

このプラグインを導入する事で、そのプログラミング言語について詳しくない人でもコマンド1発で入力補完や定義位置ジャンプやソースコード整形といった「IDE 機能」を得る事ができる様になります。やるべき事は2つ

  • vim-lsp とその動作に必要なプラグインを入れる事
  • vim-lsp-settings を入れる事

ファイルを開いて拡張機能のインストールを提案されたら :LspInstallServer を実行するだけなのです。これだけで以下のプログラミング言語の IDE 機能が得られるのです。

Language Language Server Local Install
C/C++ clangd No
C# omnisharp Yes
Clojure clojure-lsp Yes
TypeScript typescript-language-server Yes
JavaScript typescript-language-server Yes
JavaScript javascript-typescript-stdio Yes
Python pyls Yes
Rust rls No
Go gopls Yes
Ruby solargraph Yes
PHP intelephense Yes
Java eclipse-jdt-ls Yes
Lua emmylua-ls Yes
Vim vim-language-server Yes
Bash bash-language-server Yes
Terraform terraform-lsp Yes
Dockerfile dockerfile-language-server-nodejs Yes
YAML yaml-language-server Yes
XML lsp4xml Yes
Fortran fortls Yes
Scala Metals Yes
Elm elm-language-server Yes
JSON json-languageserver Yes
Swift sourcekit-lsp No
COBOL cobol-language-support Yes
Reason reason-language-server Yes
TeX texlab Yes
TeX digestif No
Nim nimls No
D dls No
Elixir elixir-ls Yes
Groovy groovy-language-server Yes

Go 言語の IDE 機能を得る為に何か知る必要はありません。Java の IDE 機能を得る為に何か知る必要はありません。HTML の IDE 機能をインストールする為に npm コマンドの使い方を覚えたり、LaTeX の IDE 機能をインストールする為に、配置場所を考える必要もありません。もしインストールを実行しても動かなかったら、それは vim-lsp-settings のバグです。

以前まででれば vim-lsp を導入すると Language Server の登録が必要でした。

if executable('gopls')
    au User lsp_setup call lsp#register_server({
        \ 'name''gopls',
        \ 'cmd'{server_info->['gopls']},
        \ 'whitelist': ['go'],
        \ })
    autocmd BufWritePre *.go LspDocumentFormatSync
endif

その為、新しい Language Server を導入する度に以下の様な設定が増えていき、設定ファイルがどんどん増えてしまっていました。それが vim-lsp-settings の導入により、その殆どを消し去る事ができました。以下が僕の ~/.vim/_config/200-lsp.vim です。

if empty(globpath(&rtp, 'autoload/lsp.vim'))
  finish
endif

functions:on_lsp_buffer_enabled() abort
  setlocal omnifunc=lsp#complete
  setlocal signcolumn=yes
  nmap <buffer> gd <plug>(lsp-definition)
  nmap <buffer> <f2> <plug>(lsp-rename)
  inoremap <expr> <cr> pumvisible() ? "\<c-y>\<cr>" : "\<cr>"
endfunction

augroup lsp_install
  au!
  autocmd User lsp_buffer_enabled call s:on_lsp_buffer_enabled()
augroup END
command! LspDebug let lsp_log_verbose=1 | let lsp_log_file = expand('~/lsp.log')

let g:lsp_diagnostics_enabled = 1
let g:lsp_diagnostics_echo_cursor = 1
let g:asyncomplete_auto_popup = 1
let g:asyncomplete_auto_completeopt = 0
let g:asyncomplete_popup_delay = 200
let g:lsp_text_edit_enabled = 1

サーバ登録に関する物は無くなりました。let を設定している箇所は、vim-lsp およびそれに必要なプラグインの動作を変更する物です。人によっては好ましくない物もあるので皆さんの好きな様に変更頂くのが良いです。

lsp_diagnostics_enabled はファイルの変更に伴いリアルタイムにエラー表示する機能 Diagnostics を有効にする設定、asyncomplete_auto_popup および asyncomplete_auto_completeopt は自動で入力補完ポップアップを表示する設定、asyncomplete_popup_delay はポップアップを表示するまでのディレイ、lsp_text_edit_enabled は LSP の仕様である textEdit を有効にする設定です。この lsp_text_edit_enabled に関しては少し実験的な実装になっている為、もし誤動作する様であれば 0 に設定するのが良いです。

vim-lsp-settings も合わせて vim-lsp を使いたいのであれば以下を vimrc に記述します。

Plug 'prabirshrestha/asyncomplete.vim'
Plug 'prabirshrestha/asyncomplete-lsp.vim'
Plug 'prabirshrestha/vim-lsp'
Plug 'mattn/vim-lsp-settings'
Plug 'mattn/vim-lsp-icons'

Plug 'hrsh7th/vim-vsnip'
Plug 'hrsh7th/vim-vsnip-integ'

vim-lsp-icons はソースコードにエラーがあった際にアイコンでエラー表示をしてくれる為のプラグインです。また Language Server の中には、穴あき形式で補完候補を返してくる物もあり、これをうまく対応してくれるのが vim-vsnip です。

これだけ設定して、上記の 200-lsp.vim を有効にしておけば、おおよその Language Server が正しく動作し、F2 キー(または :LspRename)でシンボルのリネーム、gd で定義位置ジャンプ、:LspDocumentFormat でソースコード整形、gVim を起動したらアイコンでエラー表示がされる様になります。

これを導入する事で、これまで C# の補完の為に導入していた omniSharp-vim や、tags ファイルで定義位置ジャンプを行っていた rust.vim の様な言語専用のプラグイン、それに必要な rustfmt といったツール類を消し去る事ができました。

僕が現在、言語専用に入れているプラグインは Vim のデフォルト syntax では対応しきれていない物に対応させる為に入れている数種類のプラグインだけになりました。

おわりに

昨日書いた、ぼくがかんがえたさいきょうの Vim のこうせい 2019年 年末版とは別に Vim をモダンな IDE にする為の設定を説明しました。人によっては、Vim の設定管理方法が僕とは違うが IDE 機能は欲しいと思われる方もいるだろうと思いましたので、別記事で書かせて頂きました。

昨今、僕が Vim に望んでいるのはこういった氾濫してしまった設定ファイル類をプラグイン等により減らす方向に向かって欲しいという事です。これらの整理なくしては、次の時代に対応できなくなってしまいます。できればプラグイン固有の設定すら書かなくても良い時代になる事を願っています。

最後に、これとは別に僕が「Go 言語の編集でこれだけは外せない」と思っている物を後日書きたいと思います。

さて、あと数時間で 2019 年も終わりです。今年も色々な方にお世話になり、色々な活動をする事ができたと思っています。皆さまに感謝しつつ、来年もまた皆さんのお役に立てられる情報を発信して行けたらと思います。来年も宜しくお願い致します。

Posted at by