helpline 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,300 @@
1
+ <html>
2
+ <head>
3
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
4
+ <title>HelpLine</title>
5
+ <link rel="icon" href="favicon.ico" />
6
+ <link href='https://fonts.googleapis.com/css?family=Open+Sans:400,700' rel='stylesheet' type='text/css'>
7
+ <link rel="stylesheet" type="text/css" href="stylesheets/normalize.css">
8
+ <link rel="stylesheet" type="text/css" href="stylesheets/stylesheet.css">
9
+ <link rel="stylesheet" type="text/css" href="stylesheets/github-light.css">
10
+ </head>
11
+ <body>
12
+ <section class="page-header">
13
+ <h1 class="project-name">HelpLine</h1>
14
+ <h2 class="project-tagline">シェルで使う万能ヘルプ</h2>
15
+ <a href="https://github.com/masui/HelpLine" class="btn">ソース @ GitHub</a>
16
+ <a href="https://Scrapbox.io/HelpLine" class="btn">データ @ Scrapbox</a>
17
+ <a href="#install" class="btn">インストール</a>
18
+ </section>
19
+
20
+ <section class="main-content">
21
+
22
+ <center>
23
+ <video src="https://s3-ap-northeast-1.amazonaws.com/masui.org/d/b/db555e8535ff111e794b95e6d3d7a8f5.mp4" width=600 controls></video>
24
+ </center>
25
+
26
+ <p>
27
+ 以下、GitHelpのテキストをコピペしただけなのでHelpLineの説明になってません。すみません
28
+ </p>
29
+
30
+ <p>
31
+  Gitのような複雑なシステムは使い方が難しい。
32
+ ある程度使い方を知っている場合でも、あまり一般的でない機能を使うのは難しい。
33
+ たとえば以下のような場合はどういうコマンドを使えばいいだろうか?
34
+ </p>
35
+
36
+ <ul>
37
+ <li><i>ひとつ前のバージョンの<code>README.md</code>からの変更を見たい</i></li>
38
+ <li><i><code>README.md</code>は3日前からどう変わった?</i></li>
39
+ <li><i><code>package.json</code>に<code>coffee</code>という名前が入ったのはいつ?</i></li>
40
+ <li><i>ここ1週間ぐらい変更されてないファイルは?</i></li>
41
+ <li><i>最近大量に修正したファイルはどれだっけ?</i></li>
42
+ </ul>
43
+
44
+ <hr>
45
+
46
+ <p>
47
+  最初の例について考えてみる。
48
+
49
+ Gitでは「<code>HEAD^</code>」「<code>HEAD^^</code>」のような表現で昔のコミットを参照できるので
50
+
51
+ <blockquote>
52
+ <code>$ git diff HEAD^ README.md</code>
53
+ </blockquote>
54
+
55
+ のようにすればひとつ前のコミットの<code>README.md</code>との比較ができるが、
56
+ 最近のコミットで<code>README.md</code>を編集していなかった場合は
57
+ このコマンドを起動しても何も出力されない。
58
+
59
+ <blockquote>
60
+ <code>$ git log README.md</code>
61
+ </blockquote>
62
+
63
+ とすれば<code>REDME.md</code>の編集履歴を調べられるので、
64
+ ひとつ前のバージョンのコミットIDを使えば
65
+
66
+ <blockquote>
67
+ <code>$ git diff (ひとつ前のバージョンのコミットID) README.md</code>
68
+ </blockquote>
69
+
70
+ のようにして
71
+ ひとつ前のバージョンの<code>README.md</code>からの変更を見ることができる。
72
+ </p>
73
+
74
+ <hr>
75
+
76
+ <p>
77
+  しかしこれはなかなか面倒な話である。
78
+
79
+ <i>手動でコミットIDを手打ちかコピペしなければならない</i>
80
+
81
+ し、当然ながら
82
+
83
+ <i><code>git log</code>や<code>git diff</code>といったコマンドの存在と動作を知っていなければならない</i>。
84
+
85
+ <code>git log</code>や<code>git diff</code>はよく使うコマンドなので
86
+ Gitユーザなら誰でも知っているだろうが、
87
+ このような簡単な仕事でもコピペのような面倒な操作が必要なのは嫌である。
88
+
89
+ <br>
90
+  実はGitには<code>rev-list</code>というコマンドがあり、
91
+ 編集があったコミットのIDをリストすることができる。
92
+ これを利用すると、ひとつ前の編集のコミットIDは
93
+
94
+ <blockquote>
95
+ <code>$ git rev-list HEAD -- README.md | head -2 | tail -1</code>
96
+ </blockquote>
97
+
98
+ で取得できる。
99
+ この結果を利用すると、
100
+ 「ひとつ前のバージョンの<code>README.md</code>からの変更を見たい」
101
+ という要求は
102
+
103
+ <blockquote>
104
+ <code>git diff $(git rev-list HEAD -- README.md | head -2 | tail -1) -- README.md</code>
105
+ </blockquote>
106
+
107
+ のようなコマンドで実行できることになる。
108
+ (<code>$(...)</code>というのは<code>bash</code>の記法で、コマンド実行結果を文字列として扱うものである。
109
+ <code>--</code>というのは、その後に続く文字列がオプションではなくてファイル名等だということを示すもの。)
110
+ </p>
111
+ <p>
112
+  <code>rev-list</code>の<code>-n</code>オプションを利用すると
113
+
114
+ <blockquote>
115
+ <code>git diff $(git rev-list -n 1 HEAD -- README.md)^ -- README.md</code>
116
+ </blockquote>
117
+
118
+ のように書くこともできる。
119
+ </p>
120
+
121
+ <hr>
122
+
123
+ <p>
124
+  それにしても、このように
125
+ <b>無駄知識が大量に必要</b>
126
+ だったり
127
+ <b>単純な要求の実行が大変</b>
128
+ だったりするのは嫌すぎる。
129
+
130
+ 上のような工夫によって、
131
+ ひとつ前のバージョンの<code>README.md</code>からの変更は調べられるようになったわけだが、
132
+ こういった要求は無限にあるわけで、
133
+ そのたびにいろんなGit機能を調べたり思い出したりしなければならないのだろうか。
134
+
135
+ 「<i><code>README.md</code>は最後にどこ変えたっけ?</i>」
136
+ のような自然な質問を簡単に
137
+ Gitコマンドに翻訳する方法が欲しい。
138
+
139
+ <br>
140
+  こういった要望に対して最近は<b>人工知能</b>的に解決しようとするアプローチが人気かもしれない。
141
+ しかしそのためには高度な自然言語処理が必要で、
142
+ ちょっと違った表現を許したり内容を変えたりすることは簡単ではない。
143
+ 大阪弁で質問できる日が来るとは思えない。
144
+
145
+ また、「<code>longfilename</code>」のようなものを指定しようとして
146
+ 「<code>logfile</code>」のように間違って入力しても動くようにするには
147
+ 単純な予測/補完/誤り修正機能などを使った方が良いだろう。
148
+
149
+ <br>
150
+  逆引き辞典などでは
151
+ 「ひとつ前のバージョンのファイルとの違いを知る」
152
+ のようなエントリはあるかもしれないが、
153
+ それを調べた後で「<code>README.md</code>」のような名前を指定してコマンドを起動する必要がある。
154
+ こういった二度手間も減らしたいものである。
155
+ </p>
156
+ <p>
157
+  Macのヘルプで「時間 セット」と入力しても時間をセットする方法は出てこないし、ヘルプを自分で追加することはできない。
158
+ <br>
159
+ <img src="https://gyazo.com/9af1335835f8ded134203f55cbc3bc96.png" width=400>
160
+ <br>
161
+ 時間をセットする方法がヘルプに書いてあったとしても、
162
+ 時間を4時にセットするためには自分で「4時」という値を指定してから「時間のセット」機能を実行しなければならない。
163
+ </p>
164
+
165
+ <p>
166
+  マニュアルやヘルプを書くのは面倒なものである。
167
+ システムのドキュメントやマニュアルやヘルプシステムを独立に開発するのは
168
+ 面倒すぎるし齟齬も起きやすいだろう。
169
+ ユーザをサポートするシステムがひとつにまとまっていて、
170
+ 誰でも情報を足したり修正したりできたら嬉しいだろう。
171
+ </p>
172
+
173
+ <h3>GitHelpのアプローチ</h3>
174
+
175
+ <p>
176
+  GitHelpは、以下のような方針で上のような課題をすべて解決しようというものである。
177
+
178
+ <ul>
179
+ <li>ユーザのあらゆる曖昧な表現にマッチするようにヘルプ文字列を正規表現で表現し、
180
+ Gitコマンドに変換する</li>
181
+ - ExpandHelp(<a href="https://github.com/masui/expand-ruby">ソース</a> /
182
+ <a href="http://www.interaction-ipsj.org/archives/paper2012/data/Interaction2012/oral/data/pdf/12INT012.pdf">論文</a>)を利用
183
+ <li>データをすべてクラウド上に置いて編集可能にすることにより、
184
+ 誰でもデータを追加/修正できるようにする</li>
185
+ - <a href="https://Scrapbox.io/GitHelp">Scrapbox</a>を利用
186
+ <li>ユーザが指定したパラメタはそのまま利用して実行に使う</li>
187
+ - ユーザが「4」「時間」などと指定すると「時刻を4時にセットする」のようなものを提案して実行可能にする
188
+ <li>多少の誤入力を許す</li>
189
+ </ul>
190
+
191
+ この結果、<b>やりたいことの一部を漠然と言えばすぐ実行できる</b>ようになるのが理想である。
192
+ </p>
193
+
194
+ <h3>利用例</h3>
195
+
196
+ Gitに関連するタスクのキーワードやパラメタを指定して
197
+ <code>githelp</code>を起動すると
198
+ 候補のリストが表示され、
199
+ カーソルで選択すると実行される。
200
+
201
+ <ul>
202
+ <li><code>$ githelp 2 README</code>のように引数を指定して起動すると以下のような候補リストが提示される</li>
203
+
204
+ <img src="https://gyazo.com/6cc87cef5f28185199074b61716454de.png" width=500>
205
+
206
+ <p></p>
207
+ <li>カーソルで選択してリターンを押すと実行される</li>
208
+
209
+ <img src="https://gyazo.com/75c14885944c33f2671474b008120262.png" width=500>
210
+ </ul>
211
+
212
+ <a name="install"><h3>インストール</h3></a>
213
+
214
+ <pre>
215
+ % gem install githelp </pre>
216
+
217
+ <ul>
218
+ <li>対話的処理のために<a href="https://github.com/peco/peco">peco</a>が必要</li>
219
+ <ul>
220
+ <li>Macだと<code>% brew install peco</code>でインストール可能</li>
221
+ <li>Linuxなどの場合<a href="https://github.com/peco/peco/releases">こちら</a>のファイルからインストール (yumなどでは入らない)</li>
222
+ </ul>
223
+ </ul>
224
+
225
+ <h3>実装</h3>
226
+
227
+ <ul>
228
+ <li><a href="https://github.com/masui/expand_ruby">re_expand</a>
229
+ という正規表現展開ライブラリを利用</li>
230
+ <li><a href="https://Scrapbox.io/GitHelp">Scrapbox</a>にあらゆる情報を書いておく</li>
231
+ <br>
232
+ <img src="https://gyazo.com/b4ba42a96617c65db32020eb1ea19bf3.png" width=450>
233
+ <li>
234
+ 様々なタスクの説明と実際の操作を組にして記述しておき、
235
+ ユーザが与えたキーワードやパラメタにマッチするものを
236
+ リストして実行可能にする</li>
237
+ <li>行頭に<code>$</code>がある行でタスクの説明を記述し、行頭に<code>%</code>がある行で実行コマンドを示す</li>
238
+
239
+ <li>ファイル名にマッチする引数(e.g. <code>READM</code>)や
240
+ 数字にマッチする引数(e.g. <code>2</code>)が指定されると
241
+ <code>$</code>の行に記述された<code>#{filename}</code>や<code>#{number}</code>に
242
+ マッチする</li>
243
+ <li>マッチしたときは、マッチした文字列が<code>$1</code>などで参照/展開されてGitコマンド文字列になる
244
+ <li>ワンライナーでは難しい場合は <a href="https://github.com/masui/GitHelp/tree/master/exe"><code>exe</code></a>
245
+ の下にヘルパーコマンドを用意して利用する (e.g. <a href="https://github.com/masui/GitHelp/tree/master/exe/githelp-changed"><code>githelp-changed</code></a>) )</li>
246
+ </ul>
247
+
248
+
249
+ <h3>考察</h3>
250
+
251
+ <ul>
252
+ <li>生活の中ではこういった言い換えをいつも行なっているかもしれない。
253
+ たとえば「部屋暗くして」と頼まれたら
254
+ 部屋の入口にある電灯スイッチを操作するかもしれないが、
255
+ この場合は頭の中で
256
+ 「部屋を暗くする」⇒「電灯を消す」⇒「電灯のスイッチを切る」
257
+ という翻訳が行なわれていることになる。
258
+ こういうことは生活で非常に多いので
259
+ 翻訳作業があまり気にならないものなのかもしれないが、
260
+ そういう「翻訳」は少ない方が良いのは確かだろう。
261
+ </li>
262
+ <li>そういえば先日「らくらくホン」画面に出てくる鬱陶しい「羊」を消す方法が全くわからなかったのだが、
263
+ あれは「マチキャラ」と呼ばれるものなので
264
+ 「マチキャラ」を消すという操作が必要だった。
265
+ お前はMSのイルカか。
266
+ 「羊 消す」とか「消す」とかで消せるべきだろう。
267
+ githelpでは <code>$ githelp 削除</code> と入力すれば削除関連で何ができるのかわかるだろうし、
268
+ <code>(鬱陶しい|不快な)羊を(消す|殺す)</code>
269
+ のようなエントリをユーザが足すこともできるだろう。
270
+ </li>
271
+ <img src="http://service.smt.docomo.ne.jp/site/iconcier/img/iconcier_ptl_cha_img01.png" width=300>
272
+ <li>というわけで<b>Gitは単なる適用例であり、広い範囲で使いたいと思っている。</b></li>
273
+ </ul>
274
+
275
+ <h3> 注意</h3>
276
+
277
+ <ul>
278
+ <li><code>githelp</code>コマンドはGitリポジトリのディレクトリで実行する必要がある</li>
279
+ <li><a href="https://github.com/masui/expand_ruby">re_expand</a>の実装が富豪的なので
280
+ 大きなリポジトリだと不具合があるかも</li>
281
+ <li>データが全然足りない... 特にリモートリポジトリ関連のデータは皆無だが、
282
+ ローカルだけでも充分複雑なのでとりあえずローカル処理の情報を充実させたい</li>
283
+ <li>旧版は<a href="https://github.com/masui/GitHelp_old">こちら</a>
284
+ </ul>
285
+
286
+ <h3>関連システム</h3>
287
+
288
+ <ul>
289
+ <li><a href="http://dl.acm.org/citation.cfm?id=2814295">AnyCode</a></li>
290
+ <ul>
291
+ <li>自然言語キーワードからJavaスニペットを検索する</li>
292
+ <li><code>copy fileA fileB</code> みたいなキーワードから <code>FileUtil.copyFile(new File(fileA), new File(fileB))</code> みたいなコード候補を生成する</li>
293
+ </ul>
294
+
295
+ <li>文芸的プログラミング</li>
296
+ </ul>
297
+
298
+ </section>
299
+ </body>
300
+ </html>
@@ -0,0 +1,14 @@
1
+ #
2
+ # Ctrl-Hでhelplineを呼ぶようにする
3
+ #
4
+ function run-help() {
5
+ BUFFER=$(/Applications/HelpLine.app/Contents/MacOS/helpline "${BUFFER}")
6
+ CURSOR=${#BUFFER}
7
+ zle redisplay
8
+ }
9
+ zle -N run-help
10
+ bindkey "^h" run-help
11
+
12
+ # コマンドライン実行時にもコメントを使えるようにする
13
+ # setopt interactivecomments
14
+
@@ -0,0 +1,5 @@
1
+ require "helpline/version"
2
+
3
+ module Helpline
4
+ # Your code goes here...
5
+ end
@@ -0,0 +1,3 @@
1
+ module Helpline
2
+ VERSION = "0.1.0"
3
+ end
data/main.js ADDED
@@ -0,0 +1,123 @@
1
+ //
2
+ // HelpLine メインプロセス
3
+ //
4
+ var electron = require('electron');
5
+ var BrowserWindow = electron.BrowserWindow;
6
+ var app = electron.app;
7
+ var fs = require('fs');
8
+
9
+ var child_process = require('child_process');
10
+ var exec = child_process.exec;
11
+ var execSync = child_process.execSync;
12
+
13
+ // window objectがGCされないようにするために、globalに定義する
14
+ var win;
15
+
16
+ // レンダリングプロセスから呼べるようにする
17
+ //app.files = files;
18
+ //app.branches = branches;
19
+ //app.pwd = pwd;
20
+ //app.prompt = prompt;
21
+
22
+ //
23
+ // レンダリングプロセスから呼ばれる関数いろいろ
24
+ //
25
+ app.argv = function(){
26
+ return process.argv
27
+ }
28
+
29
+ app.finish = function(cmd){
30
+ console.log(cmd)
31
+ //console.log("echo comment string\necho abc")
32
+ app.quit()
33
+ }
34
+
35
+ app.pwd = function(){
36
+ return process.env.PWD
37
+
38
+ var pwd = process.env['HOME'];
39
+ var pwdfile = "/tmp/githelp.pwd";
40
+
41
+ try {
42
+ fs.statSync(pwdfile);
43
+ } catch(err) {
44
+ return pwd;
45
+ }
46
+ return fs.readFileSync(pwdfile, 'utf8').replace(/\n/,'');
47
+ }
48
+
49
+ // パタンにマッチするファイルのリストを計算 (レンダラプロセスから呼ばれる)
50
+ app.files = function(patterns){
51
+ // patterns = ['r']; // 動いているように見せる
52
+ const command = `cd ${app.pwd()}; git ls-files`;
53
+ var list = execSync(command).toString().split(/\n/);
54
+ var files = new Set;
55
+ for(var file of list){
56
+ for(var i=0;i<patterns.length;i++){
57
+ var pattern = patterns[i];
58
+ if(pattern.length > 0){
59
+ var re = new RegExp(pattern,'i');
60
+ if(file.match(re)){
61
+ files.add(file);
62
+ }
63
+ }
64
+ }
65
+ }
66
+ var a = Array.from(files);
67
+ if(a.length == 0) a = ["xxxxx"];
68
+ return a
69
+ //return a.join("|");
70
+ }
71
+
72
+ // ブランチリスト (レンダラプロセスから呼ばれる)
73
+ app.branches = function(){
74
+ const command = `cd ${app.pwd()}; git branch`;
75
+ var list = execSync(command).toString().split(/\n/);
76
+ var branches = [];
77
+ for(var branch of list){
78
+ if(branch != ''){
79
+ branches.push(branch.replace(/^\s*/,'').replace(/^\*\s*/,''));
80
+ }
81
+ }
82
+ if(branches.length == 0) branches = ["xxxxx"];
83
+ return branches.join("|");
84
+ }
85
+
86
+ app.allowRendererProcessReuse = true; // こうしないとwarningが出る
87
+
88
+ function createWindow () {
89
+ win = new BrowserWindow({
90
+ width: 600,
91
+ height: 400,
92
+ //frame: false,
93
+ webPreferences: { // Electron5.0で必要? https://stackoverflow.com/questions/56091343/typeerror-window-require-is-not-a-function
94
+ nodeIntegration: true
95
+ }
96
+ });
97
+ win.loadURL(`file://${__dirname}/helpline.html`);
98
+
99
+ // 常に最前面でフォーカスされるようにする
100
+ win.setAlwaysOnTop(true);
101
+ win.on('blur', () => {
102
+ win.focus();
103
+ });
104
+ win.setPosition(100,300)
105
+
106
+ // win.webContents.openDevTools(); // デバッグコンソール表示
107
+ }
108
+
109
+ app.on('ready', () => {
110
+ createWindow();
111
+ });
112
+
113
+ app.on('window-all-closed', () => {
114
+ if (process.platform !== 'darwin') {
115
+ app.quit();
116
+ }
117
+ });
118
+
119
+ app.on('activate', () => {
120
+ if (win === null) {
121
+ createWindow();
122
+ }
123
+ });