Open
Description
Issue about the next vitalizer design, discussion, and so on.
What purpose of this issue?
- For logging vim-jp.slack.com (#vital-dev room)
- Want to hear people's ideas outside vim-jp.slack.com
What is the problem?
To use vital modules, users must load them by vital#of()
or vital#myplugin#new()
.
This allows different versions of a module to be installed on 'runtimepath'.
But this makes vital
- complicated
- hard to understand (for users)
- slow (overhead of import())
- if
autoload/vital.vim
is needed to be changed, ALL PLUGINS USING VITAL MUST UPGRADE Vital.Next: autoloadization of modules in :Vitalize #674 (comment)
So we need vital like
- simple
- fast as ordinary autoload scripts as possible
Related issues
Slack log (Japanese)
Click to expand
lambdalisue [11:29 AM]
新 vital に関して勝手に考えてた。
色々考えた結果、依存関係を解決して autoload/(prefix)/(plugin)/xxx.vim ってファイルにリネームコピーしてくれるだけのツールで良く無い?という結論に至ったけどどう思います?今の import 方式ってメリットより特殊であるデメリットの方が大きくなってる気もする。
欠点として、全ての vital モジュール作り直しになるけど....まぉ新しいVimに最適化する作業もやりたいしなぁという気分。
↑ んで依存関係調査は (prefix)#_#xxx#yyy みたいな文字列拾ってきたら大体わかんじゃね?と
thinca [11:31 AM]
依存先の関数の呼び出しはどのようにする想定でしょ?
lambdalisue [11:32 AM]
書き換え
thinca [11:32 AM]
動的に呼び出していたら…?
lambdalisue [11:32 AM]
それを捨てるのはどうでしょう?w
thinca [11:32 AM]
fu-mu
lambdalisue [11:33 AM]
捨てるとシンプルになる
拾うと複雑
thinca [11:33 AM]
(prefix) には何が入ります?
lambdalisue [11:33 AM]
vital以外の任意文字列
supplement とか、とりま新しい vital の名前って思ってました
thinca [11:34 AM]
ああ、そもそも vital と言う名前ではなくすと (区別のために)
lambdalisue [11:34 AM]
はい。仕組み全く違うし
これなら動的な関数呼び出しが難しい以外は普通の Vim script になるからとっつきやすくなる
thinca [11:35 AM]
デメリットがあるとすると関数1つ1つ呼び出すのが長くなるとかかなぁ
lambdalisue [11:35 AM]
それ
まあ、仕方ないかなーと。それがデフォなのでデフォに合わせる
thinca [11:36 AM]
xxx.vim の部分は階層化もする想定ですよね
lambdalisue [11:36 AM]
もちろん
thinca [11:36 AM]
`supplement#my_awsome_plugin#Web#HTTP#Agent#new()`
lambdalisue [11:37 AM]
(基本はsnake_caseに治したい
thinca [11:37 AM]
そこw?
lambdalisue [11:37 AM]
まあ長いですよね
でも長いだけ
thinca [11:38 AM]
new() ならいいけど頻繁に使う系の関数が若干しんどくなりそうだなぁ。小粒のユーティリティ系
とは言え既存の vital が複雑というのはわかる
lambdalisue [11:38 AM]
僕は一度インポートするのが無駄に感じるから小粒のユーティリティは関数直接呼びたい派
やっぱり本来の仕組みに乗っかりたい
thinca [11:39 AM]
import() はスクリプトロード時だけなので一旦読んじゃえばそこまで速度に影響はないはずではある。初回が重くなると言われるとまあそう
lambdalisue [11:40 AM]
まあ速度もあるんですが、どっちかってと感覚。書き心地の好み
関数名長いの嫌って気持ちと同じ
thinca [11:40 AM]
書き心地は import 割と嫌いではない。長い名前に狭いスコープで短い別名付けるの好き
lambdalisue [11:41 AM]
わかる
わかる、が、その為に複雑増やすのかーって気持ちに
thinca [11:41 AM]
Python で import 使わずに常にパッケージ名フルで書けって言われている感がある
lambdalisue [11:42 AM]
やろうと思えば関数リファレンス作れるし
複雑性を増やしてまで享受したいか?と言われると僕は怪しいと思った
thinca [11:43 AM]
隠蔽されていれば気にならないかなーと思うかな。Python の import も詳しい仕組みしらないけどとりあえずそう書けば動くから使ってるわけだよね
lambdalisue [11:43 AM]
あーそれは一理
thinca [11:43 AM]
下手に中身を知ってると複雑〜と言う気持ちになってしまう
lambdalisue [11:43 AM]
現に今インポート周りがバグってる訳ではないですしね
thinca [11:44 AM]
ですね。バグってて毎度 import 周りでトラブってるとかだと見直さないといけないのわかる
lambdalisue [11:44 AM]
ただなぁVim scirpt本来の使い方したい気持ちががが
thinca [11:45 AM]
その気持ちもわかるw
lambdalisue [11:45 AM]
まあ、既存のやつ置き換えるのクソ大変だから頓挫しそうだけどね!w
poc的につくってみるかな
thinca [11:47 AM]
内部的には alisue さんのを採用しつつ、import すると `{'foo': function('vital#plugin_name#module#foo')}` が返ってくる仕組みとかどうでっしゃろ。既存との互換性がキビしくなる問題は若干だけど残るな…
lambdalisue [11:47 AM]
それ、やるとしたら supplement プラグインみたいな感じがいいかなー
公式の方法は一つにして、シンタックスシュガーは別枠で用意する
仮 supplement は関数直接呼び出しを想定して vital 的に使いたい人は supplement#vital#import みたいなの足すとか
thinca [11:52 AM]
モジュールの作り方が全く別物のフレームワークが複数できちゃうと作る側がしんどい予感がある
lambdalisue [11:52 AM]
vital と supplement(仮) の話?
まあ、↑の話は vital 2 というか完全に新しいの作りませんかって提案なので
thinca [11:53 AM]
ですね
その場合 vital は捨てましょうってことですよね
lambdalisue [11:54 AM]
頓挫しなければw
thinca [11:54 AM]
言うて私も全然動けていなくて申し訳なさしかない
lambdalisue [11:55 AM]
正直頓挫する予感しかしない
thinca [11:55 AM]
w
lambdalisue [11:56 AM]
まあ気が向いたらpocをvim-vital グループにでも作ります
ゴリラ [11:59 AM]
>言うて私も全然動けていなくて申し訳なさしかない
つまり:gorilla:が改造に全力を注げば問題解決?
Tsuyoshi CHO [12:17 PM]
Vim script流儀にちゃんと沿うものは、それはそれであってもいいかな、と思うけど、HMACにHashモジュールを動的にセット、とかやって動いている機能作った身としては、いまのvitalのような使い勝手もほしい、というのはある。
単純なシンタックスシュガーというより、モジュールオブジェクトをちゃんと生成していることのメリット、みたいなところ
# どうしようもなければ作りなおすこと自体は可能だと思うので、方針自体には是非どちらもあまりないかな...。
ujihisa [12:21 PM]
うーん、整理すると、vim本体に適切なnamespaceとimportを導入することな気がしてきた
tyru [12:44 PM]
モジュールオブジェクト、というのがおそらくこれになる予感
https://vim-jp.slack.com/archives/C8F0X4DHN/p1562035634082700
thinca
内部的には alisue さんのを採用しつつ、import すると `{'foo': function('vital#plugin_name#module#foo')}` が返ってくる仕組みとかどうでっしゃろ。既存との互換性がキビしくなる問題は若干だけど残るな…
Posted in #vital-dev | Yesterday at 11:47 AM | View message
普通に autoload 関数として書いて、vitalizer が配置する時に↑のオブジェクトを返す関数をファイル内に生成すると良いかも
Tsuyoshi CHO [12:48 PM]
そこですね、それが実現できてれば、たぶん問題なく今の使い方も出来る、でよいことづくめですが...
tyru [12:56 PM]
vim本体として要るかどうかっていうと、(凄い primitive ではあるけど) こんな感じのユーティリティ関数を定義すれば長い関数名はどうにかなるんですよね。
本体でほしい気はするけど ES import とか見てるとなるべく単純なやつがほしいという気持ち
let s:http = s:require('web#http')
let [encodeComponent, decodeComponent] =
\ s:require('web#http', ['encodeComponent', 'decodeComponent'])
あともし vital のモジュールやめるならどのバージョンをインストールしたかの情報を .json でいいのでほしい気はしますね。
ファイルに埋め込むのもサイズ膨れるし、どうせならメタ情報は package.json みたいに外に吐いていいのでは
json なら万一バージョン情報ほしいプラグインも json_decode() で読めるので
ujihisa [1:04 PM]
jsonがネイティブに使えるようになったの本当に便利
(それができなかったからclojureからvimsonでやりとりできるようにとかかなり努力した記憶が)
tyru [1:08 PM]
require() 位は普通に vim 本体にほしい気持ちになってきた
あとでちょっとやってみようかな…
tyru [1:20 PM]
polyfill でできる範囲だけど、autoload 長すぎ問題から名前空間必要だよねって意識共有 (to vim-dev) も兼ねて issue か PR でも送りたい
あと話戻るけど vital の require() もこんな感じでいいし確かに今のモジュール方式をやめる利点はある (あと vim の autoload 使いたいも共感)
function! __vital__#require(name, ...)
return require(printf('__vital__#%s#%s', s:PLUGIN_NAME, name))
endfunction
let s:http = __vital__#require('web#http')
あと one binary で入って vim-addon.json に dependencies を記録するパッケージマネージャーを vital で提供すれば :banjaku: という感じかな (おちつけ)
tyru [11:03 PM]
とりあえず `require()` の Vim script 版はこんな感じ
let s:L = vital#of('vital').import('Data.List')
function! s:require(ns, ...) abort
let ns = strtrans(a:ns)
let fnlist = map(
\ split(execute('function /' . ns), '\n'),
\ {_,l -> matchstr(l, '^function ' . ns . '#\zs\w\+\ze(')}
\)
call filter(fnlist, '!empty(v:val)')
if a:0
let [fnlist, nofuncs] = s:L.partition({fn -> index(fnlist, fn) isnot -1}, a:1)
if !empty(nofuncs)
" TODO: show multiple func name at once?
throw printf('Exxx: No function ''%s'' in autoload ''%s''', nofuncs[0], ns)
endif
return map(fnlist, {_,fn -> function(ns . '#' . fn)})
endif
let d = {}
for fn in fnlist
let d[fn] = function(ns . '#' . fn)
endfor
return d
endfunction
let v:errors = []
" vital#of('vital').import('Data.List').pop([1,2,3])
call assert_equal(function('vital#of'), s:require('vital').of)
call assert_equal(3, s:require('vital').of('vital').import('Data.List').pop([1,2,3]))
call assert_equal([function('vital#of')], s:require('vital', ['of']))
call assert_equal(3, s:require('vital', ['of'])[0]('vital').import('Data.List').pop([1,2,3]))
try
call s:require('vital', ['xxx'])
call assert_true(0, 'Exxx was not thrown')
catch
call assert_exception('Exxx:')
endtry
echohl ErrorMsg
for s:_ in v:errors
echo s:_
endfor
echohl None
tyru [11:29 PM]
replied to a thread:
`s:require('vital', ['xxx', 'yyy'])` みたいに存在しない関数名を複数指定された場合ってどう表示した方がいいですかね?
今の所最初の存在しなかった関数だけ表示しちゃってますがView newer replies
kuu [11:34 PM]
replied to a thread:
エンドユーザーが直接使うものじゃないだろうし最初だけでいいんじゃないかなView newer replies
lambdalisue [11:42 PM]
お、いつのまにか話進んでいた
require 大賛成。外側の仕組みとして用意したい
vimconfのcfpで出すかw
tyru [11:45 PM]
:sasulambdalisue:
lambdalisue [11:47 PM]
受かったら作る締め切り駆動
jsonで依存管理は考えてました
jsonでやるのが今一番楽だし
んで
call z#gina#promise#new()
か
let promise = z#gina#require('promise')
call promise.new()
で呼べるって感じ良さそうかなと思いました。
zにしたのは名前長いから短くしたいってのとファイラで見たときに最後にまとめたいっての
ちな、↑でpromiseモジュール使うときに必要な関数リストは補完関数使えば取れそう(未実証
まあ既存のvitalモジュール使えないってデメリットあるけど、まあcfp的には面白いか?
vim-z
z.vim
なんか満ちてきたからまとまるかなー
tyru [1:31 AM]
ひとまずは惜しいとこまでいけた https://vim-jp.slack.com/archives/C83UXKU86/p1562085061184000
tyru
https://github.com/vim-jp/issues/issues/1282#issuecomment-507751616
すみません、助けてください。
`E685: Internal error: hash_add()` が出るのと、autoload スクリプトを読み込む手段が分かってない感じです。
Posted in #dev | Today at 1:31 AM | View message
lambdalisue [1:46 AM]
sugoi
lambdalisue [1:52 AM]
Gist にもコメントしたけど、こんなんどうでしょう?
let s:name = fnamemodify(expand('<sfile>:p'), ':t:r')
function! z#_#require(path) abort
let l:prefix = printf('z#%s#%s', s:name, a:path)
let l:script = printf(
\ 'autoload/%s.vim',
\ substitute(l:prefix, '#', '/', 'g')
\)
execute printf(
\ 'runtime! %s',
\ fnameescape(s:from_slash(l:script)),
\)
let l:fnames = map(
\ split(execute('function /' . l:prefix), '\n'),
\ { -> matchstr(v:val, '^function \zs[a-zA-Z0-9_#]\+\ze(') }
\)
let l:module = {}
for l:fname in filter(l:fnames, '!empty(v:val)')
let l:name = matchstr(l:fname, '.*#\zs[a-zA-Z0-9_]\+')
let l:module[l:name] = funcref(l:fname)
endfor
return l:module
endfunction
function! s:from_slash(path) abort
return fnamemodify(a:path, 'gs?/?\\?')
endfunction
Vim 自体に入るの最高だけどプラグインとしては新しすぎて使えないので…
tyru [2:02 AM]
コメントしました。そういや require なのにロードしてなかった…
C 版では解決しようとしてたけど Vim script 版は vital でしかテストしてなかったからうまくいっちゃってたままだった…
lambdalisue [2:05 AM]
POC: https://github.com/lambdalisue/vim-z/blob/master/autoload/z/_/promise/process.vim
GitHub
lambdalisue/vim-z
Contribute to lambdalisue/vim-z development by creating an account on GitHub.
Vitalizer 相当を作るのがとーーーってもだるい…
そのうち…
job/promise はたぶんできたから、これつかって git clone して任意リポジトリの任意ファイルを勝手にダウンロードとか色々。。。
まあねるか
tyru [2:17 AM]
POC はやい
tyru [6:23 AM]
let ns = require('my#awesome#plugin#feature')
で `ns == {}` の時もあればちゃんと入っている時もあるという状態になってしまってお手上げ状態です :innocent:
(再現手順は issue に書きました https://github.com/vim-jp/issues/issues/1282#issuecomment-507848425)
GitHub
autoload 関数を呼ぶのが長いので省略できる手段が欲しい · Issue #1282 · vim-jp/issues
内容 echo some#nested#plugin#func() みたいに autoload 関数の名前が長くなる事があります。 これを解決する手段が欲しいです。 実現方法 以下のような require() 関数が組み込みで欲しいです。 " very#long#long#plugin#func(42) let s:plugin = require('very#long#l...
tyru [7:27 AM]
既存 vital モジュールの書き換えですが、もしかしたら vitalizer が頑張ったら今の書き方でも良いとかありませんかね?
結局 vital はプラグインにインストール (embed) ができるライブラリというのが強みなので、名前空間 (=autoload の prefix) をどう vital モジュール内で表現するかという問題はあると思います (↑の vim-z では `z#_` で定義してる)。
結局インストール時に prefix を書き換える必要がありそう。
tyru [7:31 AM]
なるべく書き換えなし (autoload/vital.vim 除く) で module という形で動的に名前空間を実現してたけど、おそらく「ちゃんと」やるならインストール時に vimlparser とかでパースして書き換える必要はあると思います。
(PREFIX みたいな文字列を単純に置き換えみたいなのでもいけますが、その仕様にしたらもちろん vint とかの恩恵が受けられるなくなるので vim script の syntax は保ったままにしたい…)
まぁこれだと AST → Vim script の printer が必要になってしまい大仰になってしまう (逆に言えば↓が実用的になれば解決する)
https://github.com/vim-jp/vim-vimlparser/pull/70
GitHub
Add 'printer' feature: Restore source code string from AST by tyru · Pull Request #70 · vim-jp/vim-vimlparser
This is big change. so I don't think I want this feature to get merged soon. Disclaimer This feature is not intended to restore full original source code. This feature provides a way to get Vim...
tyru [15 hours ago]
いやでも volt では plugconf って .vim ファイルに書いた関数を抜き出して関数名だけ変えて、他にも色んな変数をトップレベルに定義したりとかやってるので全てを変換するとかそんな大げさな事しないでいいのでは??
volt と同じことやればできるでしょって思ったからそう言ったのでは???
tyru [7:33 AM]
vim-vimlparser で現実的な速度でパースできるのかとかの問題はある
あと今は vital モジュールをプラグインのリポジトリに同梱しても動いてたけどそういうのは autoload 方式でサポートできるのか、する必要があるのかとか
lambdalisue [8:44 AM]
僕はなるべくシンプルにしてエッジケース捨てる方がいいかなーと思います。
要はバンドル対処のスクリプトをコピーした後 z#_# って文字列(かなり特殊な文字列ですよね?)を z#gina# とかに単純置換する。
動的な関数呼び出しの一部のケースとかに対応出来ないですが、それを救うために複雑性増すよりは仕様にしてしまった方がいいかなと。
POC はそういう方向で作って行こうかなーと考えてます。
tyru [8:48 AM]
ですね。PoC は単純に置換でいいし、パースすれば function のリスト (位置含む) を取得してその関数名のみ書き換えるのはそんな難しくないと思います。
lambdalisue [8:50 AM]
良さそうなの出来たら既存の vital モジュールを z#_#vital 名前空間下にバンドルできるような積極的な置換とかも有りですね
既存の s: 関数を autoload 関数にガリガリ書き換え
tyru [8:50 AM]
良さそう
あとちょっと気になってるんですが、(prefix)#(plugin) の (plugin) ていりますかね?
prefix 指定すればいい気がする。
lambdalisue [8:51 AM]
それないと衝突しちゃう
ginaとfila で違うバージョンの promise使いたい、とか出来なくなっちゃいます
tyru [8:54 AM]
あ、はい。
自分が思ったのは、vitalize 時に「vital module のユーザが」 prefix だけ指定すればどこの prefix でもいいかなーと思ったんです。
:Vitalize に (prefix)#(plugin) を指定する感じ。
lambdalisue [8:58 AM]
あー
わかる、わかるけど少し怖いです
感覚的なものですが、名前の付け方は厳密なルールにしといたほうが無難な気がします
z#gina#promise#new だったりfila#lib#promise#new だったりって事ですよね?
具体的なヤバイケース思い付いてないけど感覚的に恐怖感がある
tyru [9:08 AM]
ふむ… まぁ vital module を本当に autoload 化できるか (s:_vital_created() 辺りの仕組みとか) とかも気になるので、実装していく中で不都合あれば気付きそうですね
つまり作ればわかる
やっていきの機運
lambdalisue [9:09 AM]
cfp 出した
「受かったら作ります(締め切り駆動」
まあ受かる受からない関係なしにやるとは思うけどw
tyru [9:13 AM]
vitalizer に関しては業務後、今日ちょっと作ってみます。 `:Vital {subcmd}` 的なの作ろうかなと
とりあえず `:Vital install` として作ろうかな
vital module として書く事でエコシステムに乗っかれると便利そげなので、subcommand にしてみた (lint とかのサポートもできたりとか、別リポジトリの vital module の fetch とか)
lambdalisue [9:15 AM]
:Zee add promise -repo=github.com/vim-jp/z
ってやったら
git clone -n ...
git checkout ...
して対処ファイルを落としてくる
までは考えてた
tyru [9:17 AM]
下手に分担とかしないで両方で作っても良さそうですね。実装ありきでここが良いとか話せると良さげ
(実装が途中でも PoC なので全然アリ)
lambdalisue [9:20 AM]
うむ
Tsuyoshi CHO [9:20 AM]
依存関係が外部リポジトリにある場合に、現在は静的に用意できていることが前提だからまだ...というのがありますが、動的に個別モジュールを取ってくると依存関係解決でやばそう
依存する上位のリポジトリの情報を__xx__直下とかに持つとかが必要になってくるかも?
lambdalisue [9:21 AM]
その辺は deps.json みたいな形で保持するのが良いかなと思ってました
Tsuyoshi CHO [9:21 AM]
なるほど
lambdalisue [9:22 AM]
autoload/z/gina/deps.json みたいなファイルに依存モジュールの情報(リポジトリとかも含む)保持して頑張る系
tyru [9:24 AM]
動的に個別モジュールを取ってくるというか、個人的にはパッケージマネージャ (`:Vital get` (仮)) とインストーラ (`:Vital install`) は別物だと思ってます
あとメタ情報は自分も json に保持する予定ですね。Vim でなくても読めるし (読めると嬉しいユースケースが見つかってる訳ではない)
例えばバージョンの管理はパッケージマネージャ (例: `:Vital get`, npm) の仕事、ファイルシステムからスクリプトをロードするのはローダー (vital で提供する `require()` 関数、JS の `require()`) の仕事
lambdalisue [9:37 AM]
たしかにマネージャーとインストーラ(バンドラー?)は分けたほうがよさそう。
tyru [9:38 AM]
なるほどバンドラー
lambdalisue [9:38 AM]
仕事そっちのけで作りたくなるw
が、仕事しよう
tyru [9:39 AM]
あとバンドラーで思い出したけど、webpack みたいなバンドラーツールからもアイデアが拝借できると思っていて、
具体的に言うと NYSL でない外部 vital module のために、ライセンス文を冒頭に埋め込めるようにもしたい
すごく :wakaru:
lambdalisue [9:40 AM]
あ、それよさそう。そのアイディアもらい
tyru [9:21 PM]
寝てた
定期的に夕方寝落ちるの、リモートになってから多くなってしまった… (理性よ…)
Tsuyoshi CHO [9:38 PM]
定期停電...
tyru [10:03 PM]
※計画的ではない
tyru [10:27 PM]
ガワだけ作った (既存の :Vitalize の実装を :Vital bundle でも動くようにしただけ)
https://github.com/vim-jp/vital.vim/tree/vital-command
GitHub
vim-jp/vital.vim
A comprehensive Vim utility functions for Vim plugins - vim-jp/vital.vim
tyru [10:35 PM]
ちなみにパッケージマネージャーとバンドラーを分けた方がいいというのは内部的にはそうなんだけど
ユーザ的には別に分ける必要はないと思っている (ユーザ的には `:Vital install` で外部のリポジトリも透過的に扱えたほうが嬉しい)
tyru [10:47 PM]
モジュールの大文字やめたいも同意なんだよなぁ
python 2 -> 3 感あるな
thinca [11:02 PM]
モジュールの大文字そこまで嫌ではない派なのだけど、みんな結構嫌派なんかな?
Tsuyoshi CHO [11:03 PM]
どっちもアリはありかなと思いますが、大文字小文字を使い分けるのがうまくいかないものが出たときに悩むくらい?
tyru [11:05 PM]
自分は Vim script はBram が Python に寄せてる気がするのでなんとなくスタイル的には Python を参考にしようかなみたいに考える場合が多いです (あくまでそういう傾向があるだけ)
あと他は小文字だけどモジュールだけ大文字というのも今見れば確かにちょっと変かな?ぐらいですかね
(ファイル名、関数名)
thinca [11:09 PM]
むしろスコープごとに違う方がパッと見でどのスコープの名前かわかって便利かなぁ
Metadata
Metadata
Assignees
Labels
No labels