とりあえず日記

VIM→秀丸エディタ→VIM→秀丸エディタ→VIM→秀丸エディタ→VIM→秀丸エディタ→VIM→秀丸エディタ→VIM→秀丸エディタ(いまここ🍄)

秀丸エディタにUnite.vimを移植してみたよ😐

はじめに

秀丸エディタUnite.vimが無くて不便だったのでサクッと移植してみたよ😐

一通り動くものが出来たのでお試し版として公開したよ

スクリーンショット

文字列の挿入

f:id:ohtorii:20200222212639g:plain

ファイル履歴(file_mru)を検索してファイルを開く

f:id:ohtorii:20200222213100g:plain

ファイル履歴(file_mru)の削除

f:id:ohtorii:20200222213424g:plain

プロセスのkill

f:id:ohtorii:20200222213309g:plain

auto preview オプション

auto preview オプションをONにするとpreviewアクションを自動的に呼び出します。

ファイルの場合

f:id:ohtorii:20200222213353g:plain

備考

ファイルの先頭行をアウトプット枠に表示します。

ハイライト(強調表示)の場合

autopreview hilight

備考

秀丸エディタのハイライト(強調表示)を変更すると、ウインドウフォーカスが切り替わり使い勝手が悪いです。 その使いにくさを回避するため事前に手作業でウインドウを分離しました。

Unityマクロのインターフェース

f:id:ohtorii:20200222223200p:plain

画面の名称

二つの画面(バッファ)があります。

  • 検索バッファ
  • 候補バッファ

f:id:ohtorii:20200222223515p:plain

検索バッファ

検索の候補を空白区切りで入力します、候補は大文字小文字の区別をせずAND検索します。

候補バッファ

絞り込み検索した結果をリアルタイムで表示します。

さらに、アクションの対象とする候補を複数選択することができます(赤文字の行)。

もっと詳細を知りたいんだけど?

「操作方法・固有名詞の説明・インストール方法…」などはプロジェクトのトップページを参照してください。

github.com

ダウンロード

https://github.com/ohtorii/unity/releases

今後の予定

ヒデマラーの皆さんが本マクロを自分好みにカスタマイズする目的で、

  • 「ショートカットキー・カラー」をカスタマイズ可能にする 。
  • ヒデマラーの方々がkindとsourceを作るためのドキュメントを用意する。

利便性を高める目的で、

  • Unite.vimから有用なsourceを更に移植する。

など予定しています。

その他

マクロ名について

あっ、そうそう、マクロ名はUnityだよ。

名前に違和感を感じた人はここの説明を読んでね😴

パフォーマンス

候補数が数百個程度ならサクサク動きます。

試しに、約26万個の候補*1を絞り込み検索するとキー操作がカクカクします。思い通りに操作できないため結構なストレスですが、表示も選択も出来て正常に動作します。

あと、カーソルキーの上下移動はサクサクなので、秀丸エディタ自体に問題はありません。 単に自作の絞り込み検索アルゴリズムがシンプル*2なのが原因です。

万単位の候補を絞り込むことは無いと思うので放置かな~😴

謝辞

思想とソースコードをかなり参考にさせてもらいました😘😘😘

参考書籍

*1:C:\Windows\以下のファイル全て

*2:手抜きとも言う

今更ですがVim Scriptのハイライト設定を公開しました

秀丸エディタVim Scriptをハイライト表示する設定を公開しました。

イメージ

vim script
vim script

公開先

秀まるおのホームページ(サイトー企画)−Vim scriptの強調表示

Github

最新版はGitHubから取得してください。 GitHub - ohtorii/highlight: 秀丸エディタのハイライト

monday.vimを秀丸エディタへ移植してみた。

2012年9月22日(土) 追記

窓の杜で紹介されました。
http://www.forest.impress.co.jp/docs/review/20120920_559434.html

始めに

vimエディタに面白いマクロがあったので秀丸エディタへ移植してみました。

キーを押すだけで単語をインクリメント・デクリメントできます。

  • January → February → March → April
  • class → struct
  • private → public → protected
  • iterator → const_iterator
  • true → false

動作イメージ(プログラマー向け)

動作イメージ(一般)

元ネタ

名無しのvim使い
http://nanasi.jp/articles/vim/monday_vim.html

monday : Lets you use ctrl-a and ctrl-x on names of months and weekdays
http://www.vim.org/scripts/script.php?script_id=1046

オリジナル版と比べて劣化している機能

大文字小文字の構成は無視しています。以下の変換を行いたいならマクロ本体のデータを修正するというベタな方法しかないです。

  • january → february
  • January → February
  • JANUARY → FEBRUARY

詳しくはマクロ内のコメントを参照して下さい。

マクロ(1つ次へ進める。switch_word_next.mac

/*単語を切り替える(一つ次へ)

秀丸エディタ ver8以降で動作。
キーアサインして下さい。
http://d.hatena.ne.jp/ohtorii/
*/
execmacro currentmacrodirectory+"\\switch_word.mac","1";

マクロ(1つ前へ戻る。switch_word_prev.mac

/*単語を切り替える(一つ前へ)

秀丸エディタ ver8以降で動作。
キーアサインして下さい。
http://d.hatena.ne.jp/ohtorii/
*/
execmacro currentmacrodirectory+"\\switch_word.mac","0";

マクロ(本体。switch_word.mac

/*単語を切り替える(内部処理マクロ)

(例)
	true -> false -> true ...
	class -> struct -> class ...
	private -> protected -> public -> private ...
	■ -> □ -> ■ ...
	● -> ○ -> ● ...

下記マクロから呼び出される。
	switch_word_next.mac
	switch_word_prev.mac


http://d.hatena.ne.jp/ohtorii/
*/


////////////////////////////////////////////////////////////////////////////
//		データ定義(必要に応じて編集して下さい)
//		データの追加削除はパターン化しているので分かるかと思います。
////////////////////////////////////////////////////////////////////////////
$eos		= "";
#num_tbl	= 0;

//注意:大文字小文字は区別します

//true -> false
$word_tbl[#num_tbl][0] = "true";
$word_tbl[#num_tbl][1] = "false";
$word_tbl[#num_tbl][4] = $eos;
#num_tbl = #num_tbl + 1;

//True -> False
$word_tbl[#num_tbl][0] = "True";
$word_tbl[#num_tbl][1] = "False";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//TRUE -> FALSE
$word_tbl[#num_tbl][0] = "TRUE";
$word_tbl[#num_tbl][1] = "FALSE";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//private -> protected -> public
$word_tbl[#num_tbl][0] = "private";
$word_tbl[#num_tbl][1] = "protected";
$word_tbl[#num_tbl][2] = "public";
$word_tbl[#num_tbl][3] = $eos;
#num_tbl = #num_tbl + 1;

//class -> struct
$word_tbl[#num_tbl][0] = "class";
$word_tbl[#num_tbl][1] = "struct";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//■ -> □
$word_tbl[#num_tbl][0] = "■";
$word_tbl[#num_tbl][1] = "□";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//● -> ○
$word_tbl[#num_tbl][0] = "●";
$word_tbl[#num_tbl][1] = "○";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//▲ -> △
$word_tbl[#num_tbl][0] = "▲";
$word_tbl[#num_tbl][1] = "△";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//▼ -> ▽
$word_tbl[#num_tbl][0] = "▼";
$word_tbl[#num_tbl][1] = "▽";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//★ -> ☆
$word_tbl[#num_tbl][0] = "★";
$word_tbl[#num_tbl][1] = "☆";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//◆ -> ◇
$word_tbl[#num_tbl][0] = "◆";
$word_tbl[#num_tbl][1] = "◇";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//on -> off
$word_tbl[#num_tbl][0] = "on";
$word_tbl[#num_tbl][1] = "off";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//On -> Off
$word_tbl[#num_tbl][0] = "On";
$word_tbl[#num_tbl][1] = "Off";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//ON -> OFF
$word_tbl[#num_tbl][0] = "ON";
$word_tbl[#num_tbl][1] = "OFF";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//あり -> なし
$word_tbl[#num_tbl][0] = "あり";
$word_tbl[#num_tbl][1] = "なし";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//はい -> いいえ
$word_tbl[#num_tbl][0] = "はい";
$word_tbl[#num_tbl][1] = "いいえ";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//in -> out
$word_tbl[#num_tbl][0] = "in";
$word_tbl[#num_tbl][1] = "out";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//In -> Out
$word_tbl[#num_tbl][0] = "In";
$word_tbl[#num_tbl][1] = "Out";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//IN -> OUT
$word_tbl[#num_tbl][0] = "IN";
$word_tbl[#num_tbl][1] = "OUT";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//begin -> end
$word_tbl[#num_tbl][0] = "begin";
$word_tbl[#num_tbl][1] = "end";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//first -> last
$word_tbl[#num_tbl][0] = "first";
$word_tbl[#num_tbl][1] = "last";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//. -> ->
$word_tbl[#num_tbl][0] = ".";
$word_tbl[#num_tbl][1] = "->";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//* -> &
$word_tbl[#num_tbl][0] = "*";
$word_tbl[#num_tbl][1] = "&";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//
$word_tbl[#num_tbl][0] = "iterator";
$word_tbl[#num_tbl][1] = "const_iterator";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//
$word_tbl[#num_tbl][0] = "reverse_iterator";
$word_tbl[#num_tbl][1] = "const_reverse_iterator";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//
$word_tbl[#num_tbl][0] = "unsigned";
$word_tbl[#num_tbl][1] = "signed";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//
$word_tbl[#num_tbl][0] = "min";
$word_tbl[#num_tbl][1] = "max";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//
$word_tbl[#num_tbl][0] = "yes";
$word_tbl[#num_tbl][1] = "no";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//
$word_tbl[#num_tbl][0] = "Yes";
$word_tbl[#num_tbl][1] = "No";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//
$word_tbl[#num_tbl][0] = "YES";
$word_tbl[#num_tbl][1] = "NO";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

//0...9
$word_tbl[#num_tbl][0]	= "0";
$word_tbl[#num_tbl][1]	= "1";
$word_tbl[#num_tbl][2]	= "2";
$word_tbl[#num_tbl][3]	= "3";
$word_tbl[#num_tbl][4]	= "4";
$word_tbl[#num_tbl][5]	= "5";
$word_tbl[#num_tbl][6]	= "6";
$word_tbl[#num_tbl][7]	= "7";
$word_tbl[#num_tbl][8]	= "8";
$word_tbl[#num_tbl][9]	= "9";
$word_tbl[#num_tbl][10] = $eos;
#num_tbl = #num_tbl + 1;

$word_tbl[#num_tbl][0]	= "0";
$word_tbl[#num_tbl][1]	= "1";
$word_tbl[#num_tbl][2]	= "2";
$word_tbl[#num_tbl][3]	= "3";
$word_tbl[#num_tbl][4]	= "4";
$word_tbl[#num_tbl][5]	= "5";
$word_tbl[#num_tbl][6]	= "6";
$word_tbl[#num_tbl][7]	= "7";
$word_tbl[#num_tbl][8]	= "8";
$word_tbl[#num_tbl][9]	= "9";
$word_tbl[#num_tbl][10] = $eos;
#num_tbl = #num_tbl + 1;

$word_tbl[#num_tbl][0]	= "0";
$word_tbl[#num_tbl][1]	= "1";
$word_tbl[#num_tbl][2]	= "2";
$word_tbl[#num_tbl][3]	= "3";
$word_tbl[#num_tbl][4]	= "4";
$word_tbl[#num_tbl][5]	= "5";
$word_tbl[#num_tbl][6]	= "6";
$word_tbl[#num_tbl][7]	= "7";
$word_tbl[#num_tbl][8]	= "8";
$word_tbl[#num_tbl][9]	= "9";
$word_tbl[#num_tbl][10] = $eos;
#num_tbl = #num_tbl + 1;

//月(先頭大文字バージョン)
$word_tbl[#num_tbl][0]	= "January";
$word_tbl[#num_tbl][1]	= "February";
$word_tbl[#num_tbl][2]	= "March";
$word_tbl[#num_tbl][3]	= "April";
$word_tbl[#num_tbl][4]	= "May";
$word_tbl[#num_tbl][5]	= "June";
$word_tbl[#num_tbl][6]	= "July";
$word_tbl[#num_tbl][7]	= "August";
$word_tbl[#num_tbl][8]	= "September";
$word_tbl[#num_tbl][9]	= "October";
$word_tbl[#num_tbl][10] = "November";
$word_tbl[#num_tbl][11] = "December";
$word_tbl[#num_tbl][12] = $eos;
#num_tbl = #num_tbl + 1;

//月(小文字バージョン)
$word_tbl[#num_tbl][0]	= "january";
$word_tbl[#num_tbl][1]	= "february";
$word_tbl[#num_tbl][2]	= "march";
$word_tbl[#num_tbl][3]	= "april";
$word_tbl[#num_tbl][4]	= "may";
$word_tbl[#num_tbl][5]	= "june";
$word_tbl[#num_tbl][6]	= "july";
$word_tbl[#num_tbl][7]	= "august";
$word_tbl[#num_tbl][8]	= "september";
$word_tbl[#num_tbl][9]	= "october";
$word_tbl[#num_tbl][10] = "november";
$word_tbl[#num_tbl][11] = "december";
$word_tbl[#num_tbl][12] = $eos;
#num_tbl = #num_tbl + 1;

// #if 0
$word_tbl[#num_tbl][0] = "#if 0";
$word_tbl[#num_tbl][1] = "#if 1";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;

$word_tbl[#num_tbl][0] = "#if\t0";
$word_tbl[#num_tbl][1] = "#if\t1";
$word_tbl[#num_tbl][2] = $eos;
#num_tbl = #num_tbl + 1;






////////////////////////////////////////////////////////////////////////////
//			処理本体(以下は編集しないようお願いします。)
////////////////////////////////////////////////////////////////////////////
#g_index_row = -1;
#g_index_col = -1;
call Main;
endmacro;


Main:
	if(rectselecting){
		message("矩形選択は対応していません");
		return false;
	}

	//動作環境を固定化する。
	//折りたたみを維持,部分編集を維持,範囲選択,最後に移動
	//マクロ終了時に元に戻る。
	setcompatiblemode 0x0003|0x000c|0x0200|0x2000;
	disabledraw;
	if(selecting){
		//選択あり、そのまま使う。
		call Proc ##is_next;
		##ret = ##return;
	}else{
		//選択なし
		//1文字選択して試す。
		//一文字のデータ(1 -> 2 -> 3...)を考慮。
		if((0x0d == code)||(-1==code)){
			call SelectRange column, lineno, column-1,lineno;
		}else{
			call SelectRange column, lineno, column+1,lineno;
		}
		call Proc ##is_next;
		##ret = ##return;
		if(! ##ret){
			//次は単語選択して試す。
			escape;
			selectword;
			call Proc ##is_next;
			##ret = ##return;
		}
	}
	return ##ret;


/*
	返値	変換無し	0
			変換あり	1
*/
Proc:
	##is_next	= true;
	if(argcount){
		##is_next	= val(getarg(0));
	}

	//選択された単語を取得する
	$$sel_word		= gettext(seltopx, seltopy, selendx, selendy, 1);
	if($$sel_word == $eos){
		return false;
	}
	call FindNextWord $$sel_word, ##is_next;
	$$next_word = $$return;
	if($eos == $$next_word){
		return false;
	}

	begingroupundo;
	delete;
	##old_column=column;
	##old_lineno=lineno;
	insert $$next_word;
	//選択しておいて何度も呼ばれたときに備える。(使い勝手の向上なのでコメントアウトしても構わない)
	call SelectRange ##old_column, ##old_lineno, ##old_column+strlen($$next_word), lineno;
	endgroupundo;
	return true;


FindTableIndex:
	$$sel_word = $$1;

	##i = 0;
	while(##i < #num_tbl){
		##j = 0;
		while(1){
			$$word = $word_tbl[##i][##j];
			if($$word == $eos){
				break;
			}
			if($$sel_word == $$word){
				#g_index_row = ##i;
				#g_index_col = ##j;
				return true;
			}
			##j=##j+1;
		}
		##i=##i+1;
	}
	return false;


/*	テーブルから次の文字列を取得する
	返値	成功:次の文字列
			失敗:$eos
*/
FindNextWord:
	$$sel_word	= $$1;
	##is_next	= ##2;
	call FindTableIndex $$sel_word;
	if(! return){
		return $eos;
	}

	//変換先の単語を取り出す
	if(##is_next){
		//次の単語へ
		$$next = $word_tbl[#g_index_row][#g_index_col + 1];
		if($$next == $eos){
			//一周したので先頭へもどる
			$$next = $word_tbl[#g_index_row][0];
		}
	}else{
		//前の単語へ
		if(#g_index_col==0){
			//最後の単語を返す(秀丸マクロは配列長を求めることが出来ないのでループで回す)
			##i 	= 0;
			$$next	= $eos;
			while(1){
				if($eos == $word_tbl[#g_index_row][##i]){
					$$next = $word_tbl[#g_index_row][##i - 1];
					break;
				}
				##i = ##i + 1;
			}
		}else{
			$$next = $word_tbl[#g_index_row][#g_index_col - 1];
		}
	}
	return $$next;

SelectRange:
	escape;
	movetolineno 1+##1, ##2;
	beginsel;
	movetolineno 1+##3, ##4;
	endsel;
	return ;

PyConsoleでcmd.exeの代わりにCygwinをつかってみた

前回の日記はこちら(id:hatenadiary:20110409:1302340942)


PyConsoleでCygwinがとりあえず動きました、正常動作しているかどうかは分かりません、動いただけです。

pyconsole_wx.py を以下のように書き換えます。

改変前(pyconsole_wx.py)

class ConsoleProcessWindow (wx.stc.StyledTextCtrl):
    def __init__ (self, *args, **kwargs):
        wx.stc.StyledTextCtrl.__init__ (self, *args, **kwargs)
        
        <<snip>>
        
        self.console_process = pyconsole.ConsoleProcess ('cmd.exe',
                console_update=self.console_update)

改変後(pyconsole_wx.py)

class ConsoleProcessWindow (wx.stc.StyledTextCtrl):
    def __init__ (self, *args, **kwargs):
        wx.stc.StyledTextCtrl.__init__ (self, *args, **kwargs)        

        <<snip>>        

        #
        #C:\cygwin\Cygwin.bat の内容をコピー。
        #
        self.console_process = pyconsole.ConsoleProcess ('C:\\cygwin\\bin\\bash.exe --login -i',
                console_update=self.console_update)

スクリーンキャプチャ

bash のバージョンを表示

ファイルとフォルダ一覧


UTF8.txtを表示

CP932.txtを表示(この後、wxPythonがフリーズしました)


(フリーズの原因は特定していません。)

  • UTF8.txt の内容
日本語(UTF8)のテキストです。
2行目。
3行目です。
ダメ文字、表円竹曾察UTF8なので関係ないとは思いますが・・・
  • CP932.txt の内容
日本語(CP932)のテキストです。
2行目。
3行目です。
ダメ文字、表円竹曾察

ショートカットキー

画面をクリアするCygwinのショートカットキー(Ctrl-L)は使えず。
wxPython側はTextCtrlの内容しか見ていないのが原因だと思います。なのでキー入力を捕まえてPyConsoleへ送れば画面クリアされる??
妄想だけで未確認です。

VIM

pyconsole_vim.vim を改造すればVIMの中でCygwinが使えるんじゃないかと・・・
自分、vimerじゃないんでよく分かりませんが・・・
すいません。

<<snip>>
au CursorHold <buffer> call CheckUpdated()
function CheckUpdated()
    if g:console_process_row > g:console_process_row_last
        let g:console_process_row_last = g:console_process_row
        " since this a timer based command the :startinsert
        " does not work.  So send in the line append command
        call remote_send (v:servername, 'A')
    endif
endfunction

python import pyconsole_vim
python vc = pyconsole_vim.VimConsole('cmd.exe')

imap <buffer> <cr> <esc>:python vc.exec_line()<cr>
imap <buffer> <tab> <esc>:python vc.exec_part()<cr>
|<<

pyconsoleを日本語対応してみた

アプリに組み込めるコマンドラインのライブラリを探していたところ、pythonで書かれた pyconsole(http://code.google.com/p/pyconsole/) が良さげなので試そうとしたら、そもそも動きませんでした・・・なんてこったい!!
ソースをザッと眺めたところアスキーコードでしか動かない、よくあるコードだったので日本語対応しました。

before


after


環境

python 2.7.1
pywin32 build 216
windows vista 32bit

CONSOLE PROCESS ENDED が表示されないようにする

例外が発生し子プロセスが死ぬのが原因です。
最近の pywin32 は下位互換性がないようなので、pyconsole.py を書き換えます。

class _ConsoleChildProcess (_ConsoleProcessBase):
    def __init__ (self, parent_pid, lst_cmd_line):
        <snip>
        if 0:
            #original
            self.con_window = win32console.GetConsoleWindow().handle
        else:
            self.con_window = win32console.GetConsoleWindow()

文字コードを日本語(cp932)にする

親プロセスに送る文字コードを日本語(cp932)にします。

def shmem_write_text (shmem, bytes_in_use, msg_hdr_fmt, msg_hdr_tpl, msg_text):
    msg_text=msg_text.encode("cp932") #この行を追加
    bytes_in_use = max (bytes_in_use, _shmem_hdr_len)
    msg_hdr_fmt += 'i'  # int indicating length of text

デバッグ

バックグランドで動いているコンソールを表示します、こうしないと何が発生しているのかサッパリ分かりません。

def _start_console_process (self, cmd_line):
    <snip>
    si = win32process.STARTUPINFO()
    si.dwFlags |= win32con.STARTF_USESHOWWINDOW
    si.wShowWindow=win32con.SW_SHOW
    #si.wShowWindow = win32con.SW_HIDE
    # si.wShowWindow = win32con.SW_MINIMIZE


ログファイルがテンポラリディレクトリへ生成されてログを確認しにくいので、カレントディレクトリへ出力します。

_debug = True
if _debug:
    # Note: if this module is imported then the logging may be
    # started from a different module with different parameters
    if is_child():
        filename_log = 'pyconsole_child.log'
    else:
        filename_log = 'pyconsole_parent.log'
    logging.basicConfig (level=logging.INFO,
        format='%(asctime)s %(levelname)-8s %(message)s\n -- %(pathname)s(%(lineno)d)',
        datefmt='%H:%M:%S',
        filename=filename_log,
        filemode='w')
    logging.info ('starting')

秀丸エディタから VisualStudio を制御するマクロ

2011年8月5日(追記)
・日本語ファイル名を扱えるようにしました。
秀丸エディタで編集ファイルをVisualStudioで開くマクロを追加しました。
・ダウンロードはこちらから。 https://github.com/ohtorii/visual_studio_hidemaru

秀丸エディタから VisualStudio を制御するプログラマ向けのマクロです。

(github:左上のダウンロードボタンからzipを選択してください。開発中の最新版はこちら)
https://github.com/ohtorii/visual_studio_hidemaru
秀丸エディタのマクロライブラリ。安定版はこちら)
http://hide.maruo.co.jp/lib/macro/visual_studio_hidemaru.html



ソースコード秀丸エディタで書いて、ビルドする度に VisualStudio へ移動するのが面倒なので、「ビルド・リビルド・実行...etc」を秀丸エディタから出来るようにしました。
秀丸エディタ ver8.03(ver8系なら多分動くと思います)

動作イメージ

見ての通り、メニューから「コンパイル/リビルド/デバッグ・・・」の動作を選択する単純な方式です。

アウトプット枠

出力はアウトプット枠へ表示します、コンパイルエラーのあるファイル名位置で改行キーを押すかマウスクリックすると該当のファイル名と行位置へジャンプできます。

出来ること

  • コンパイル
  • ビルドのキャンセル
  • ソリューションのビルド
  • ソリューションのリビルド
  • ソリューションのクリーン
  • プロジェクトのビルド
  • プロジェクトのリビルド
  • プロジェクトのクリーン
  • デバッグ開始
  • デバッグなしで開始
  • デバッグの停止

動作環境

VisualStudio 2008
VisualStudio 2010

C++/C#/VBプロジェクトで動作を確認しています。
VisualStudioは複数起動していても正しく動作します。
Express版は未対応です(本マクロでは認識しません)。

マクロの説明

コンパイル・ビルドのキャンセル・ソリューションのビルド...etc」は1コマンド1ファイルになっています。
visual_studio_menu_simple.mac は各コマンドを呼び出しているサンプル的な位置づけです。

ファイル名 説明
visual_studio_menu_simple.mac 簡易メニュー
visual_studio_cf_compile.mac コンパイル
visual_studio_cf_cancel.mac ビルドのキャンセル
visual_studio_cf_debug.mac デバッグ開始
visual_studio_cf_debug_stop.mac デバッグの停止
visual_studio_cf_project_build.mac プロジェクトのビルド
visual_studio_cf_project_clear.mac プロジェクトのクリーン
visual_studio_cf_project_rebuild.mac プロジェクトのリビルド
visual_studio_cf_run_without_debug.mac デバッグなしで開始
visual_studio_cf_solution_build.mac ソリューションのビルド
visual_studio_cf_solution_clear.mac ソリューションのクリーン
visual_studio_cf_solution_rebuild.mac ソリューションのリビルド
visual_studio_cf_hmbook.mac 秀丸の(.hmbook)ファイルを作る(おまけマクロ)
visual_studio_call.mac 橋渡しをするマクロ
visual_studio_hidemaru.exe VisualStudioを制御する実行ファイル

visual_studio_hidemaru.exe が Visual Studio を制御しています、秀丸に依存しないので他のテキストエディから使用できるかもしれません。

内部動作

秀丸エディタで編集しているファイル名を含む Visual Studio を特定して、各種コマンド(ビルド/デバッグ・・・)を送りつけて結果を取得しています。

例えば Visual Studio が二つ起動しているとします、

・VisualStudio_0
    c:\my_app\src\main.cpp  (ソリューションエクスプローラーに含まれる)

・VisualStudio_1
    c:\project\main.cpp

このとき、c:\my_app\src\main.cpp を秀丸エディタでリビルドすると visual_studio_hidemaru.exe が VisualStudio_0 に対してリビルドコマンドを送り、ビルド中のメッセージを取得しています。

どうやって、Visual Studio を見つけるかというと、
Visual Studio を起動すると COM の RunningObjectTable に登録されます。


RunningObjectTable は IROTVIEW.EXE で表示できます。


(VC2010の例)
!VisualStudio.DTE.10.0:4464

末尾の4464はプロセスIDです。


あとは、プログラムから CreateBindCtx → monikers → GetObject → QueryInterface → Dispatch すると 該当のプロセスIDに結びついた DTE を取り出せます。
githubにソースがあるのでそちらを参照してください。


おまけ機能

Visual Studioに登録してあるファイル(cpp/h...etc)ら秀丸のプロジェクトファイル(.hmbook)を作る機能があります。

visual_studio_menu_simple.mac 90行目付近 のコメントを参照してください。if(0) を if(1) に変更すると有効
になります。

秀丸エディタ側の表示

Visual Studio側の表示

元ネタ

VIM の visual_studio.vim から拝借しました。

clangでコード補完してみる(1回目)

clangとはC++の「静的コード解析」ができるコマンドラインツールです。
(本家)http://clang.llvm.org/get_started.html

clang (クラン(グ)のように発音 [2] )とは、C、C++Objective-Cプログラミング言語向けのコンパイ
ラのフロントエンドであり、Low Level Virtual Machine (LLVM) をバックエンドとして使用する。

wikipediaから引用)

まずは、コマンドライン上で動かしてみる

(test2.cpp)

class foo{
    int     get();
    void    set(int x);
};

int hoge(foo &c){
    c.           //←ここの候補をリストアップする。
}

コマンドライン

clang.exe -cc1 -fsyntax-only -code-completion-at=test2.cpp:7:7 test2.cpp
COMPLETION: foo : foo::
COMPLETION: get : [#int#]get()
COMPLETION: operator= : [#foo &#]operator=(<#const foo &#>)
COMPLETION: set : [#void#]set(<#int x#>)
COMPLETION: ~foo : [#void#]~foo()

こんなかんじで、COMPLETION の後に補完候補が現われます。


Windows用バイナリ

ビルド済みのモノをありがたく頂戴しま、ゲフンゲフン。
http://unitedsoft.ch/clang/clang.exe

上記バイナリはGCCでビルドされているようです。VisualStudio2010でビルドしたものとはまた違うらしいです。

(4月に llvm 2.9の正式版がリリースされるらしいので、そのときになったらVC2010でビルドしてみます。llvm 2.8のC++0x対応は弱いらしい)

秀丸エディタに組み込んでみる

とりあえず、秀丸エディタに組み込んでみました。

メソッドの補完


スコープも対応しています。変数cはスコープによって型が異なる。


std::stringのメソッド候補


std::vectorのメソッド候補


クラス中でtypedefされた型の候補


operator[]


iteratorの型を正しく認識しています。(その1)


iteratorの型を正しく認識しています。(その2)


typedef も認識しています。

とりあえず作った秀丸マクロ

githubはこちら。(右上のダウンロードからzipを選択)
https://github.com/ohtorii/clang

/* clang.mac
clangでキーワード補完する秀丸マクロ
*/

////////////////////////////////////////////////////////////////////////////
//      ユーザーカスタマイズ
////////////////////////////////////////////////////////////////////////////

//clang.exeへのパス
//ここから落としたもので動作検証しています。
//http://unitedsoft.ch/clang/clang.exe
//
//GCC版にVisual Studioのヘッダをくわせているので色々怪しいですが、一応動いています。
//
$g_clang_exe = "C:\\Users\\hoge\\Documents\\hidemaru_macro\\clang\\clang.exe";


//includeディレクトリのパス(Visual Studio 2010)
$g_include_dir[0] = "C:\\Program Files\\Microsoft Visual Studio 10.0\\VC\\include";
$g_include_dir[1] = "C:\\Program Files\\Microsoft Visual Studio 10.0\\VC\\atlmfc\\include";
$g_include_dir[2] = "C:\\Program Files\\Microsoft SDKs\\Windows\\v7.0A\\include";
#g_include_dir_num=3;

//define定義
//備考:_MSC_VER=1600はVisual Studio 2010の場合
$g_define = "-DWIN32 -D_WINDOWS -DNDEBUG -D_MSC_VER=1600 -D__MSVCRT_VERSION__=0x700 -D_WIN32_WINNT=0x0500";

//オプション定義
$g_option = "-cc1 -fsyntax-only -x c++ -fno-caret-diagnostics -fdiagnostics-print-source-range-info -std=c++0x -fms-extensions -fmsc-version=1600 -fgnu-runtime";




////////////////////////////////////////////////////////////////////////////
//      メイン処理
////////////////////////////////////////////////////////////////////////////

//環境毎に異なるオプションを固定する、マクロ終了時に戻るので利用者の環境を変更することはない。
//折りたたみを維持,部分編集を維持,範囲選択,最後に移動
setcompatiblemode 0x0003|0x000c|0x0200|0x2000;

$old_searchbuffer = searchbuffer;
#old_searchoption = searchoption;
call Main;
setsearch $old_searchbuffer, #old_searchoption;
endmacro;


Main:
    if("new"==filetype){
        message("ファイルに保存してから試してください。\nこのマクロはサンプルなので手抜きです。");
        return false;
    }
    if(updated){
        message("ファイルが更新されています、まずは保存してから試してください。\nこのマクロはサンプルなので手抜きです。");
        return false;
    }

    call MakeIncludeOption;
    $$include = $$return;
    if(0==strlen($$include)){
        message("includeディレクトリが空白です。グローバル変数 $g_include_dir[] の定義が間違っているかも。");
        return false;
    }
    $$fn = basename;
    $$completion = "-code-completion-at=" + $$fn + ":"+str(lineno)+":"+str(column+1)+" " + $$fn;
    $$arg = $g_option + " " + $$completion + " " + $g_define + " " + $$include;

    title "running clang.";
    ##old_hidemaru = hidemaruhandle(0);
    openfile "/h ";
    disabledraw;
    disableinvert;
    ##cur_hidemaru = hidemaruhandle(0);

    runex $g_clang_exe + " " + $$arg, 1
            , 0, ""
            , 5, ""
            , 0, ""
            , 1, ""
            , 2, 1, 0;
    if(!result){
        message("runexの呼び出しで失敗");
        setactivehidemaru ##old_hidemaru;
        closehidemaruforced ##cur_hidemaru;
        return false;
    }

    //補完の候補を $$guess[] へ代入する。
    gofiletop;
    ##guess_num = 0;
    $$guess[0]  = "";
    $$guess_str = "";
    searchdown2 "(?<=^COMPLETION:[ \\t]+)[^ ]+",casesense,regular,nohilight;
    while(result){
        $$tmp = gettext2(seltopcolumn,seltoplineno,selendcolumn,selendlineno);
        if(strlen($$tmp)){
            $$guess_str = $$guess_str + $$tmp + "\n";
            $$guess[##guess_num] = $$tmp;
            ##guess_num = ##guess_num + 1;
        }
        finddown2;
    }

    if(##guess_num){
        selectall;
        backspace;
        insert($$guess_str);
        setactivehidemaru ##old_hidemaru;
        //直前の秀丸を辞書として使用する。
        autocomplete -1, 0x00000040, 0x00008000;
    }else{
        setactivehidemaru ##old_hidemaru;
    }
    closehidemaruforced ##cur_hidemaru;
    title 0;
    return true;

MakeIncludeOption:
    ##i     = 0;
    $$retult= "";
    while(##i < #g_include_dir_num){
        if(0 < ##i){
            $$retult = $$retult + " ";
        }
        call ConvertShortFolderName $g_include_dir[##i];
        $$short_name = $$return;
        if(! strlen($$short_name)){
            return"";
        }
        $$retult = $$retult + "-I" + $$short_name;
        ##i=##i+1;
    }
    return $$retult;

//長い文字列を runex に与えるとハングするっぽいので回避。
ConvertShortFolderName:
    ConvertShortName:
    $$fn = $$1;
    ##fso = createobject("Scripting.FileSystemObject");
    ##obj = callmethod_returnobj(##fso,"GetFolder",$$fn);
    $$short_name = getpropstr(##obj,"ShortPath");
    releaseobject(##fso);
    return $$short_name;

今回書いた秀丸マクロについて(まとめ)

clangを使用して型を認識した補完が出来ましたが、ヘッダファイルを増やすと候補リストが表示されるまで時間がかかります。clangはプリコンパイルヘッダ(.PCH)が使用できるようなので、今後はその対応とかでしょうか。
今のところ常用できるほど高速ではないです。
常用できるようにするにはバックグランドで更新したりキャッシュを導入したりして高速化しないと駄目っぽいです。

参考になるマクロとか

clang_complete.vim

vimのマクロです。
clang.exeを呼び出すだけの素直な実装と、pythonのctypeモジュール経由で libclang.dll を呼び出す実装がある。
unix 環境だと libclang.so を呼び出すらしい。

auto-complete-clang.el

emacsのマクロです。
clang.exeを呼び出すだけの素直な実装っぽいです。