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>


f:id:wkpn:20160427141838g:plain
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がプリントされる環境(Vimpythonインターフェースが使える環境)でないと動きません。
これには、まず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を返すようになってくれました。一歩前進。


第三のハマり。VimPythonインターフェースがうまく動かない

前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をインストール。

Download | Mono

そのあとslnファイルを作るために、一回、Unity上で「Open C# Project」をします。

 

f:id:wkpn:20160303213117p:plain

 その後C#のファイルをVimで開いたらいけました!

 

Omnisharp感動的な便利さ!超嬉しい!

Unity5.3で、WebGL版がローカルでは動くのにサーバーにアップすると動かない

Unityで作ったゲームのWebGL版がローカルでは動いているのに、サーバーにアップすると、うまく動かない...

 

調べるとWebGL版が動かないのは、どうやらmimeタイプの指定が原因という記事をみつけた。

 

[Unity] Unity5(Beta)で制作したゲームをWebGLで公開する | ftvlog

 

これによると、.htaccess

AddType application/octet-stream .memgz
AddType application/octet-stream .datagz

を追加したらうまくいくという話だったのですが...

それでも動かない...

 

もうちょい調べると、

forum.unity3d.com

という記事を発見。

ここを見ると、.htaccessを消しちゃいなっていうアドバイスがあったので、Releaseフォルダ内にある.htaccessを削除してみたらうまくいった。

しかし、なぜうまくいったのかなんかすっきりしなくて気持ち悪い。


ちなみにサーバーがIISの場合は、

answers.unity3d.com

が参考になると思います。

 

ローカルでうまく動いていたのは、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を選んでデスクトップなり何なりにセーブする。

こうして、bluetoothが効かなくなる度に、アプリをダブルクリックしてbluetoothを復帰させてます。

以前のだと拡張子のないファイルが候補に表示されていなかったので修正。

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