EasyAlignとvim-textobj-indentを使って1ストロークで神経質な人も安心の綺麗なコードに
EasyAlign については、以下を参照のこと
baqamore.hatenablog.com
vim-textobj-indent については、以下を参照のこと
d.hatena.ne.jp
導入後、以下のようにmapping
"S-vで行選択モードにして、 "Kana/vim-textobj-indentの機能でiiにより、同じインデントの1ブロックを選択して ":EasyAlignに*\を指定することで、全部のspaceを区切り文字として扱い整列 "isで最も浅いindentに合わせる nmap <C-i> <S-v>ii:EasyAlign*\is<CR>
1ストロークで、神経質な人も安心の綺麗なコードにできます。
C#でGenericなSingleton
C#でGenericなSingletonを実装したい。Genericでない基本的なSingletonの実装法についてはMSDNに書いてある。
MSDNに載っていたSingleton実装
Implementing Singleton in C#
最も基本的なのは、以下の様な感じ。
public class Singleton { private static Singleton instance; private Singleton() {} public static Singleton Instance { get { if (instance == null) { instance = new Singleton(); } return instance; } } }
この実装は、スレッドセーフではない。
複数のスレッドから同時にInstanceにアクセスされると、タイミングによってはinstanceが複数できてしまう場合がある。
これは避けたいので、以下の様な実装が勧められている。
public sealed class Singleton { private static readonly Singleton instance = new Singleton(); private Singleton(){} public static Singleton Instance { get { return instance; } } }
もう一個、instanceの生成を遅らせたい場合や、規定以外のコンストラクタを使いたい場合のシングルトンの作り方も載っていて、ここには引用しないが、それはinstanceの生成をlockブロックで囲って排他制御するような実装になっていた。
GenericなSingleton
基本的には、MSDNで二番目に載っていたものをベースに考える。
C++のtemplateでの感覚で
public class Singleton<T> : T { private static readonly T _instance = new T(); ... }
のような形を考えていたのだけど、どうもこれはうまくいかない。
Genericはtemplateと違ってランタイム時に解決されるものらしく、そのため、これを許すと色々と問題が出てくる。
なので、ちょっと色々と制限を掛けないと駄目らしい。
Tはclassで、new T()ができますよー、という誓約を掛ける必要がある。
なので、以下の様な感じにする。
public class Singleton<T> where T : class, new() { private static readonly T _instance = new T(); ... }
最終的には、以下の様な感じに
public class Singleton<T> where T : class, new() { // 万一、外からコンストラクタを呼ばれたときに、ここで引っ掛ける protected Singleton() { Debug.Assert( null == _instance ); } private static readonly T _instance = new T(); public static T Instance { get { return _instance; } } }
Singletonにしたいクラスがあるときは、
public class MySingleton : Singleton< MySingleton > { public MySingleton() { } ... }
として利用する。
だいたい目的は達成されたのだけど、気になる点が1点。
これを利用するためには継承先のコンストラクタをpublicにしておかないと行けない。
なのでシングルトンなのに外からコンストラクタを呼ばれてしまってオブジェクトが複数作られてしまう可能性をコンパイル時に防げない。
これが、C++だったら、
public class MySingleton : Singleton< MySingleton > { private MySingleton() { } friend class Singleton<MySingleton>; }
として防ぐことができるのだけど、C#にはfriend classというものはないので、それは不可能。
まああまりその手のバグに引っかかったこともないし、ランタイム時にはAssertで引っかけることができるので、この当たりで妥協しようかと思う。
Omnisharp導入でハマったところ
Unityでゲームを作るときに、VimにOmnisharpというプラグインを入れておくと便利と聞いて導入してみました。
が、いろいろハマったのでメモ。
自分の環境は、Windows10(Bootcamp)でVim7.4(Kaoriya版)
導入には以下のサイトを参考にさせていただきました。
Vim で C# を書くなら OmniSharp で決まり! - 永遠に未完成
Unity 用に IntelliSense 環境を Vim で実現するメモ - Qiita
インストールはNeoBundleで
NeoBundle 'tpope/vim-dispatch'
NeoBundleLazy 'OrangeT/vim-csharp', { 'autoload': { 'filetypes': [ 'cs', 'csi', 'csx' ] } }
NeoBundleLazy 'nosami/Omnisharp', {
\ 'autoload': {'filetypes': ['cs']},
\ 'build': {
\ 'windows': 'MSBuild.exe server/OmniSharp.sln /p:Platform="Any CPU"',
\ 'mac': 'xbuild server/OmniSharp.sln',
\ 'unix': 'xbuild server/OmniSharp.sln',
\ }
\ }
vim-dispatch、vim-csharpも一緒に入れておくと良いという話だったので、それらも一緒にインストール。
あと、
autocmd FileType cs set omnifunc=OmniSharp#Complete
もvimrcに追記しておきます。
最初のハマり。サーバーのビルドがうまくいかない。
Omnisharpはサーバーを立ち上げます。まずは、そのサーバーアプリをビルドしないといけないのですが、私の環境ではMSBuild.exeにパスが通っていなかったのが問題でした。
MSBuild.exeは、
C:\Windows\Microsoft.NET\Framework64\v4.0.30319
にあります。(バージョンは適宜読み替えていただいて。)
しかし、結局、ここにパスを通してもNeoBundleではビルドがうまくいきませんでした。ちょっと原因が分からなかったので手動でビルドすることに。
コマンドプロンプトからOmnisharpプラグインフォルダ以下のserverフォルダに移動してビルド
cd server
MSBuild.exe /p:Platform="Any CPU"
これでサーバーアプリは出来上がったもののOmnisharpは、まだうまく動作しませんでした。
第二のハマりポイント。has('python')が0
Omnisharpは、vim上で
:echo has('python')
とした時に、1がプリントされる環境(Vimのpythonインターフェースが使える環境)でないと動きません。
これには、まずVimが+pythonでビルドされている必要があります。
vim上で
:version
としてみると、Kaoriya版Vimは +python でビルドされていることが分かりました。なのでVimの方は問題ない。
has('python')が1になるためには、もうひとつ条件があってpythonのあるところにpathが通っている必要があります。
なんということでしょう。そもそも自分のWindows環境にはpythonがインストールされていませんでした。
さらにVimのbitバージョンとPythonのbitバージョンを揃えないとhas('python')は0になるそうです。
自分は64bit版Vimを使っていたのでPythonも64bitバージョンをインストールしました。
これで、has('python')が1を返すようになってくれました。一歩前進。
第三のハマり。VimのPythonインターフェースがうまく動かない
前2個のハマりは、すぐに解決したのですが、3つめがなかなか分かりませんでした。Omnisharpを使おうと、<c-x><c-o>すると、
ごめんなさい。site モジュールが読み込めませんでした。
というようなエラーメッセージが出ました。(正確な文言はちょっと違ったかも...)
試しに、
:python print('hello')
としても、同じようなエラーが出ます。
いろいろネット上の情報を漁っていたら、どうやらVimをビルドした時のpythonと、自分のところにあるpythonのバージョンが違うと、そういうメッセージが出ることあるよ。というのを見つけました。
このときは最新の2.7系、python2.7.11をインストールしていたのですが、試しに一個下の、python2.7.10をインストールしてみたところ、とうとうOmnisharpが動き出しました!
2.7.10のダウンロードは以下から
Python Release Python 2.7.10 | Python.org
ちなみに、Omnisharpの説明書には32bitバージョンを使ってくれって書いていたので、最終的には32bitバージョンのvim&pythonを使ってます。
Mac版
ちなみにMac環境では比較的簡単に導入できました。
まずはxbuildコマンドを使うためにmonoをインストール。
そのあとslnファイルを作るために、一回、Unity上で「Open C# Project」をします。
Omnisharp感動的な便利さ!超嬉しい!
Unity5.3で、WebGL版がローカルでは動くのにサーバーにアップすると動かない
Unityで作ったゲームのWebGL版がローカルでは動いているのに、サーバーにアップすると、うまく動かない...
調べるとWebGL版が動かないのは、どうやらmimeタイプの指定が原因という記事をみつけた。
[Unity] Unity5(Beta)で制作したゲームをWebGLで公開する | ftvlog
これによると、.htaccessに
AddType application/octet-stream .memgz
AddType application/octet-stream .datagz
を追加したらうまくいくという話だったのですが...
それでも動かない...
もうちょい調べると、
という記事を発見。
ここを見ると、.htaccessを消しちゃいなっていうアドバイスがあったので、Releaseフォルダ内にある.htaccessを削除してみたらうまくいった。
しかし、なぜうまくいったのかなんかすっきりしなくて気持ち悪い。
ちなみにサーバーがIISの場合は、
が参考になると思います。
ローカルでうまく動いていたのは、Unity上でWebGL版を「build & run」すると、Unityがローカルにサーバーを立てて、そのサーバーの設定が良い感じに設定してくれているからみたいです。
よくみるとブラウザのアドレスもlocalhostになっている。
Mac book proをスリープさせてから復帰すると、bluetoothが効かなく
Mac book proをスリープさせてから復帰すると、bluetoothが効かなくなることが多くて困っておりました。
そこで、見つけたのが、こちらの記事。
こちらにあるように、ターミナルから
sudo kextunload -b com.apple.iokit.BroadcomBluetoothHostControllerUSBTransport
sudo kextload -b com.apple.iokit.BroadcomBluetoothHostControllerUSBTransport
とすることで治りました。しかし、またスリープするとbluetoothが死ぬので記事にあるようにアプリに。
- 1. SpotlightからAutomatorを実行
- 2. "New Document"をクリック
- 3. "Application" を選択
- 4. "Utilities"をクリック
- 5. "Run AppleScript"をダブルクリック
- 6. (* Your script goes here *)を以下のように書き換える
do shell script "kextunload -b com.apple.iokit.BroadcomBluetoothHostControllerUSBTransport;
kextload -b com.apple.iokit.BroadcomBluetoothHostControllerUSBTransport" with administrator privileges
- 7. play を押してテストする
- 8. File->Saveを選んでデスクトップなり何なりにセーブする。
以前のだと拡張子のないファイルが候補に表示されていなかったので修正。
let s:save_cpo = &cpo set cpo&vim let s:True = 1 let s:False = 0 let s:is_cache_invalid = s:True let s:output_filenames = [] if !exists( "g:project_unite_source_filter_extentions" ) let g:project_unite_source_filter_extentions = [] endif if !exists( "g:project_unite_source_dirnames" ) let g:project_unite_source_dirnames = [] endif function! unite#sources#project#define()"{{{ return s:source endfunction"}}} let s:source = { \ 'name': 'project', \ 'max_candidates' : 30, \ } function! s:format_for_word( index, max_digit, path ) let format = '%' . a:max_digit . 'd: %s' return printf(format, a:index + 1, matchstr(a:path, '[^\\]*$')) endfunction function! s:source.gather_candidates(args, context) let l:extentions = g:project_unite_source_filter_extentions let l:dirnames = g:project_unite_source_dirnames for l:arg in a:args if l:arg == "invalid" echo "invalid" let s:is_cache_invalid = s:True endif endfor if s:is_cache_invalid == s:True let s:output_filenames = [] echo "no cache" " don't use cache for l:dir in l:dirnames let l:filenames = split( glob( l:dir . "**/*" ), "\n" ) for l:ext in l:extentions let l:file_filter_pattern = '.*' . l:ext . "$" let l:outputs = filter( copy(l:filenames), 'matchstr( v:val, l:file_filter_pattern ) != ""' ) let s:output_filenames = extend( s:output_filenames, l:outputs ) endfor endfor let format = '%' . strlen(len(s:output_filenames)) . 'd: %s' let s:is_cache_invalid = s:False let s:output_filenames = map( s:output_filenames, '{ \ "word": s:format_for_word( v:key, strlen(len(s:output_filenames)), v:val ), \ "kind": "file", \ "action__path": v:val, \ }') return s:output_filenames else " use cache return s:output_filenames endif endfunction let &cpo = s:save_cpo unlet s:save_cpo
これで、とりあえず、普段使いには問題なさそう。
次の記事のネタを探そう...
どうやら重いのは数千ファイルを表示使用としているからというのがメインの要因だった。
候補の上位数十個出すという風に指定しておけば、実用的な速度になった。
あと、引数にinvalidと渡すと、もう一度ファイルリストを作り直すように。
引数の渡し方が:区切りということに気づかず、ちょっと躓いた。
:Unite project:invalid
などとすればよいらしい。
これで、project.vimの代わりに使えそうになってきた。
let s:save_cpo = &cpo set cpo&vim let s:True = 1 let s:False = 0 let s:is_cache_invalid = s:True let s:output_filenames = [] if !exists( "g:project_unite_source_filter_extentions" ) let g:project_unite_source_filter_extentions = [] endif if !exists( "g:project_unite_source_dirnames" ) let g:project_unite_source_dirnames = [] endif function! unite#sources#project#define()"{{{ return s:source endfunction"}}} let s:source = { \ 'name': 'project', \ 'max_candidates' : 30, \ } function! s:format_for_word( index, max_digit, path ) let format = '%' . a:max_digit . 'd: %s' return printf(format, a:index + 1, matchstr(a:path, '\w*\..*$')) endfunction function! s:source.gather_candidates(args, context) let l:extentions = g:project_unite_source_filter_extentions let l:dirnames = g:project_unite_source_dirnames for l:arg in a:args if l:arg == "invalid" let s:is_cache_invalid = s:True endif endfor if s:is_cache_invalid == s:True let s:output_filenames = [] " don't use cache for l:dir in l:dirnames let l:filenames = split( glob( l:dir . "**/*" ), "\n" ) for l:ext in l:extentions let l:file_filter_pattern = '.*\.' . l:ext . "$" let s:outputs = filter( copy(l:filenames), 'matchstr( v:val, l:file_filter_pattern ) != ""' ) let s:output_filenames = extend( s:output_filenames, s:outputs ) endfor endfor let format = '%' . strlen(len(s:output_filenames)) . 'd: %s' let s:is_cache_invalid = s:False let s:output_filenames = map( s:output_filenames, '{ \ "word": s:format_for_word( v:key, strlen(len(s:output_filenames)), v:val ), \ "kind": "file", \ "action__path": v:val, \ }') return s:output_filenames else " use cache return s:output_filenames endif endfunction let &cpo = s:save_cpo unlet s:save_cpo