Emacsでカーソル周辺のキーワードをpythonに渡して処理させる elisp スクリプト
Emacsでカーソル周辺のキーワードをpythonに渡して処理させる elisp スクリプト
eslip はこんなの。
(defun call-external-script-test () (interactive) (let* (cur beg end str cmd) (save-excursion (setq cur (point)) (setq beg (+ cur (skip-chars-backward "_A-Za-z0-9_. \t" (bolp)))) (setq end (+ beg (skip-chars-forward "_A-Za-z0-9_. \t" (eolp)))) (setq str (buffer-substring-no-properties beg end)) (setq cmd (concat "~/python/test.py \"" str "\"")) ;;(message "beg=\"%d\"\nend=\"%d\"\nstring=\"%s\"\n" beg end str) (message (shell-command-to-string cmd)) )))
eslip から呼び出す python スクリプトはたとえばこんなの
#!/usr/bin/python3 import sys print("Hello Python! argv={}".format(sys.argv[1:]))
Emacs でたとえばこんな C ファイルを開く
if ((AAA.for_x == 1) || (BBB . for_y == 1)) { printf("true\n"); }
AAA.for_x
という文字列のどこかにカーソルを置いて M-x call-external-script-test <Enter>
とすると、ミニバッファに
Hello Python! argv=['AAA.for_x']
と表示される。
カーソル位置はどこでも良い。BBB . for_y
みたいに空白があっても一つの文字列と見なすようにしてみた。結果→
Hello Python! argv=[’BBB . for_y ’]
Windows 上で、hta アプリの設定を JSON ファイルに保存する Javascript クラス。localstorage 風味。
MyStorage.hta
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Class Test</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- <link rel="stylesheet" type="text/css" media="screen" href="ClassTest.css" /> <script src="ClassTest.ja"></script> --> </head> <body> <script> "use strict" // class Storage { // constructor() { // this.hello = "Hello" // } // alert() { // window.alert(this.hello) // } // } // ------------------------------------------------------------ // namespace My var My = My || {}; // ------------------------------------------------------------ // class Storage // this.rootKey: ルートキー。このキーで JSON ファイルに保存される。文字列。 // this.rootData: ルートキーに対応する value として保存されるデータ。連想配列。 My.Storage = function (rootKey) { this.rootKey = rootKey; this.rootData = {}; } My.Storage.prototype = { // key に対応する value を返す。 // 対応する value が内場合は unefined を返す。 getItem: function (key) { if (key in this.rootData) { return this.rootData[key]; } return undefined; }, // key, value のペアを保存する。 // save() するまでは JSON ファイルに書き出されない。 setItem: function (key, value) { this.rootData[key] = value; }, // JSON ファイル名を返す。 // この hta ファイルの名前から拡張子を .json にしたファイル名を作って返す。 getJsonFileName: function () { let filename = location.pathname; // なぜか先頭に "/" が入っているので削除する filename = filename.replace(/^\//, ""); // alert(filename); let jsonfile = filename.replace(/^(.+)\.hta$/gi, "$1.json"); // alert(jsonfile); return jsonfile; }, // JSON ファイルから this.rootKey に紐付く連想配列を読み込む。 load: function () { const ForReading = 1; const TristateTrue = -1; // unicode let allData = {}; let fso = new ActiveXObject("Scripting.FileSystemObject"); let jsonfile = this.getJsonFileName(); if (fso.FileExists(jsonfile)) { const ForReading = 1; let ts = fso.OpenTextFile(jsonfile, ForReading, true, TristateTrue); if (!ts.AtEndOfStream) { allData = JSON.parse(ts.ReadAll()); } ts.Close(); } this.rootData = allData[this.rootKey] || {}; }, // JSON ファイルに this.rootKey に紐付く連想配列を保存する。 save: function () { const ForReading = 1; const TristateTrue = -1; // unicode let allData = {}; allData[this.rootKey] = this.rootData; let fso = new ActiveXObject("Scripting.FileSystemObject"); let jsonfile = this.getJsonFileName(); if (fso.FileExists(jsonfile)) { let ts = fso.OpenTextFile(jsonfile, ForReading, true, TristateTrue); if (!ts.AtEndOfStream) { let readed = JSON.parse(ts.ReadAll()); readed[this.rootKey] = this.rootData; allData = readed; } ts.Close(); } let ts = fso.CreateTextFile(jsonfile, true, TristateTrue); ts.Write(JSON.stringify(allData)); ts.Close(); }, } // ------------------------------------------------------------ window.onload = function () { window.resizeTo(800, 640); // alert("onload"); let sto = new My.Storage("Class1"); sto.load(); alert(sto.getItem("Hello")); alert(sto.getItem("ハロー")); sto.setItem("Hello", "World"); sto.setItem("ハロー", "ワールド") sto.save(); } </script> </body> </html>
秀丸のキーアサインをEmacs風にする
秀丸のキーアサインを Emacs 風にする
VS Code に Emacs Friendly Keymap という Extension を入れて Emacs 風の キーマップにしたので、秀丸も Emacs Friendly Keymap に合わせてキーマップを変更した。あと自分用に少し追加。
ダウンロード
- LittleEmacs.KEY ファイル
- kill-line.mac (C-k)
- open-line.mac (C-o)
- recenter-top-bottom.mac (C-l)
- delete-other-window.mac (C-x 1)
- comment-region.mac (C-c C-c)
- keyboard-quit.mac (C-g)
- tab-to-tabstop.mac (M-i)
- just-one-space.mac (M-space)
- delete-horizontal-space.mac (M-)
- delete-blank-lines.mac (C-x C-o)
- other-window (C-x o)
- C-u.mac (C-u 7 2 = で = を72個挿入したりする)
- C-x_o.mac (C-x o と C-x C-o を切り分ける)
- C-x-experimental.mac (C-x.mac の実験用)
おまけ
- hr.mac 水平線ひく
- datetime.mac 日付挿入
- hr-and-datetime.mac.mac 水平線引いて日付挿入
- keycode.mac キーコードを表示 (C-x の実験用)
- todo.mac TODO管理用のツール
- today.mac 今日の日付と曜日を挿入 "04/23(木)" とか
- backtab.mac Shift+TAB でカーソルより後ろの空白を一つ前のタブ位置まで消す
インストール
- .mac ファイルを秀丸のマクロフォルダに入れる。メニューからツール>マクロ用のフォルダ で開く。たぶんデフォルトではここ↓ C:\Users\name\AppData\Roaming\Hidemaruo\Hidemaru\Macro
- LittleEmacs.KEY を秀丸の設計ファイル置き場に置く。たぶんここ↓ C:\Users\name\AppData\Roaming\Hidemaruo\Hidemaru\Settings
- LittleEmacs.KEY ファイルを秀丸のメニュー>その他>キー割り当て>読込み で読み込む。
キーストローク
基本的に Emacs Friendly Keymap Extension と同じ。 いくつかのキーはうまく設定できなかった(後述)。
追加したキーストローク
いくつか、Emacs Fridndly Keymap にはない秀丸の機能をキーに割り当てたり、マクロを書いたりした。
キーストローク | 機能 | メモ |
---|---|---|
C-k | カーソルより後ろをカット | kill-region.mac で一応対応。 |
マクロ内の変数 #kill_whole_line が 1 なら行頭で C-k すると改行も切り取る。 | ||
0 なら Emacs のデフォルトの動作になる。 | ||
C-g | キャンセル | keyboard-quit.mac で一応対応。秀丸で Esc 押すのと同じ。 |
M-i | タブ | tab-to-tabstop.mac で対応。 |
C-o | 空行を挿入。カーソル位置はそのまま | open-line.mac で対応。 |
C-x i | カーソル位置へ読込み | insert-file.mac で対応。 |
C-c C-c | 選択範囲をコメント | comment-region.mac でざっくり対応。 |
Emacs では C-u C-c C-c でアンコメントだが C-c C-c でトグルします。 | ||
対応言語は C/C++/Java/C#/Swift/Go/html/css/javascript/ | ||
TypeScript/PHP/perl/python/ruby/shell/ | ||
秀丸マクロ(mac)/bat を予定。 | ||
言語はファイルの拡張子で判断する。判断できないときは '#' をコメント用の行頭文字と見なす。 | ||
C/C++ のコメント形式はデフォルトで // コメント。 | ||
/ / コメントにする場合はマクロ内の $comment_text_pre/post を変更する。 | ||
他の言語も同様。言語ごとに設定できるようにしてある。(すべての言語でテストはしてないけど) | ||
M-space | カーソルの周囲の空白を1つにする | just-one-space.mac で実現。 |
M-\ | カーソルの周囲の空白を全部削除 | delete-horizontal-space.mac で実現 |
C-x C-o | 空行を一つにする | delete-blank-lines.mac で実現。 |
C-x C-x | 前のカーソル位置 | Emacs では exchange-point-and-mark. |
C-@ | 直前の操作の繰り返し | Emacs では C-x z zzz... |
C-x y | やり直しのやり直し | 元 Ctrl-y。 C-x u が Undo なのでその隣。 |
C-_ | やり直しのやり直し | C-/ がやり直しなので隣のキーに設定。 |
C-\ | やり直しのやり直し | C-/ がやり直しなのでBackslashに逆の意味を。 |
M-l | 小文字変換 | 範囲選択後実施する |
M-u | 大文字変換 | これも範囲選択必要。 |
M-y | 貼り付け+履歴戻し | Emacs の M-y (yank-pop) とはやや動きが違うけれど少し似てる。そのうちマクロで yank-pop 同等にしたい。 |
M-s u | TAB -> 空白変換 | よくやるのでキー割り当て。 |
M-s t | 空白 -> TAB 変換 | よくやるのでキー割り当て。 |
M-s s | すべての候補を選択 | 直近の検索文字列をすべて選択。VS Code の Ctrl-d に似た機能。 |
M-s c | すべての候補を色つけ | 直近の検索文字列に色つけしてハイライト |
M-s l | すべての候補を一覧表示 | 直近の検索文字列にヒットする一覧 |
M-s y | 引用符付き貼り付け | メールの返信で使いたい。 |
制限付きのキーストローク
キーストローク | 機能 | メモ |
---|---|---|
C-x C-l | 英小文字へ変換 | 「大文字 ←→ 小文字変換」にした。これは一文字ずつしか変換できない。いまいちだったので、M-l, M-u に別途、小文字変換、大文字変換を設定した。 |
C-x 1 | 画面分割を解除して1画面にする | 秀丸に分割解除の単独コマンドはないので左右分割を割り当てた。左右分割中にもう一度左右分割すると1つに戻るので。 |
C-x 2 | 左右二分割 | 左右分割するが、最大2分割まで。もう一度左右分割すると1つに戻る。 |
C-x 3 | 上下二分割 | 上下分割するが最大2分割まで。もう一度丈夫分割すると1つに戻る。 |
C-x z | 全画面表示 | Emacs では操作の repeat 機能。Emacs Friendly Keymap では Zenモード(動かないけど)。とりあえず秀丸では全画面表示を割り当てる。 |
C-l | 最後の編集箇所へ移動 | Emacs ではカーソル位置を画面センターにする機能。秀丸にこの機能は無い様子。かわりにもともとアサインされていたままコマンドにしておく。 |
C-Enter | 改行 | Emacs Friendly Keymap ではなにか置換ダイアログで1つ置換に使うらしいが改行にしとく。 |
C-M-n | 対応する括弧に移動 | Emacs Friendly Keymap ではeditor.action.addSelectionToNextFindMatch(デフォルト Ctrl-d)だが、 Emacs では forward-list とかなので Emacs の動きに近い処理にする |
C-' (C-^) | 単語補間 | Intellisense は秀丸にはないので単語補間のリスト表示にした。 |
C-M-Space | Toggle Sidebar Visivility | これは秀丸のファイルマネージャ枠表示の切替にしてみた。でもトグル動作してくれないなあ。 |
設定していないキーストローク
キーストローク | 機能 | メモ |
---|---|---|
C-x C-u | 英大文字へ変換 | C-x u (Undo) とかぶるので設定不可。Emacs でも間違いやすいので disabled になっている。 |
C-x C-k | タブを全部閉じる | Emacs Friendly Keymap 独自のキーアサイン。C-x k (ファイルを閉じる)と区別が付かないので設定不可。 |
M-x | コマンド入力窓 | この機能は秀丸には無い。 |
C-; | 行ごとにコメント | 取り合えす C-c C-c で代替 |
M-; | 選択範囲をコメント | これも C-c C-c で代替 |
制限
- 秀丸は C-x s と C-x C-s とかの
区別が付かない。区別つく。iskeydown(0x11)
でCtrlキーが押されているかどうか判定できた。 - C-x s とか C-x r t とか多段キーストロークで一つのコマンドを実行する機能は、秀丸ではユーザーメニューを C-x などのキーに割り当てて実現するが、ユーザーメニューは最大 8 枚しかない
のでせいぜい 2 ストロークまでかな。 3 ストロークの割り当てはできるけど数が限られる。でも、マクロの中でinputchar()
でキー入力を受け取ることが可能なためユーザーメニューを使わずに多段キーストロークに対応可能だった。そのとき、iskeydown(0x10)
でのShiftキー判定、iskeydown(0x11)
のCtrlキー判定、iskeydown(0x12)
のAltキー判定を組み合わせれば何でもできちゃいそう。たとえば C-x キーにC-x.mac
マクロを割り当て、C-x.mac
の中でinputchar()
で次の入力を受け取って、r
が押されてたら Prefi Command だから再びinputchar()
で次の入力を待ち合わせる・・・とかやれば、いくらでも多段キーストロークのコマンドがつくれそう。(但しinputchar()
で読むとr
とC-r
は違うキーコードになるので、なんか汎用的に作るのはそれなりにたいへんそうな気がする) - 当然ながら、用意されている機能は Emacs の機能とは違う。秀丸の機能をそのまま割り当てても Emacs と同じようには機能しない場合がある。
- 但し、いくつかは同じ動作になるようマクロを書いた。
- LittleEmacs.KEY には、ここに記載のないコマンドも登録されてたりするかも。普段使っていたキーマップに追加してしまったので。
Windows のファイルをドラッグ&ドロップして Cygwin 上のコマンド、スクリプトを実行するための Python スクリプト
モチベーション
cygwin 上で動くスクリプトをエクスプローラーからドラッグ&ドロップして実行させたかったので作成。
ただし、ドロップするファイルの絶対パス中に日本語があるとうまくいかない。 (英文字だけなら動く)
Python ver
>>> import sys >>> sys.version '3.7.2 (tags/v3.7.2:9a3ffc0492, Dec 23 2018, 23:09:28) [MSC v.1916 64 bit (AMD64)]'
コード
#!/usr/bin/python3 # -*- coding: utf-8 -*- # # このスクリプトは Windows のファイルをドラッグ&ドロップするだけで # cygwin 向けに書かれたスクリプトを実行できるようにするために書かれた。 # # 具体的には、このスクリプトは、渡された引数をすべて検査し、Windows の # 絶対パスらしき箇所を cygwin 形式の絶対パスに変換し、cygwin 上のスク # リプトに引き渡す。 # # cygwin 上のスクリプトは、このスクリプトの第一引数に指定する。このス # クリプトの第二引数以降は、第一引数に指定したスクリプトの引数となる。 # # コマンドラインからの実行例 # python execute-on-cygwin.py echo "Hello World!" # # ファイルを引数にとるスクリプトの実行例 # python execute-on-cygwin.py script-name.py C:\path\to\file1 C:\path\to\file2 # # ショートカットを作り、ファイル名の指定だけを省いてショートカットのリ # ンク欄に書いておくと、そのショートカットにファイルをドロップすること # で cygwin 上のスクリプトを実行できる。 # # 但し、フォルダ名、ファイル名に日本語を含むとうまく動かない。 import os import sys import subprocess import re import traceback def conv_path_win2unix(path): if re.search(r'^\w:\.*', path): path = path[0].lower() + path[1:] path = re.sub(r'^(\w):', r'/cygdrive/\1', path) path = re.sub(r'\\', r'/', path) return path if __name__ == '__main__': try: # 先頭はarg[0]はこのスクリプトのファイル名なので省く argv = sys.argv[1:] # commandline はあとで bash -c に渡すので全体を "" で囲んでおく commandline = '"' for arg in argv: # print(f'arg={arg}') tmp = conv_path_win2unix(arg) #tmp = arg # 空白を含む場合はダブルクオーテーションでクォートする。 # commandline 全体がダブルクォートで囲まれるのでエスケーブ # しておく。 if re.search('\s', tmp): tmp = r'\"' + tmp + r'\"' commandline += tmp commandline += ' ' commandline = commandline[:-1] # 最後の空白は消しておく commandline += '"' # print(f'commandline={commandline}') os.chdir(r'C:/cygwin64/bin') subprocess.run(f'bash --login -i -c {commandline}') except: ei = sys.exc_info() print('Exception detected.') # スタックトレースの取得 traceback.print_tb(ei[2]) # throw された例外とその説明を表示 print(f'\n {ei[0]} {ei[1]}') # ファイルをドロップしたときにコンソール画面がすぐに消えないように # しておく。コマンドラインから実行されたときは必要ないけど、どう判 # 定すればいい? input('\nHit any key to finish. ')
アイディア
- なんとか日本語にも対応させたい。日本語を含む場合、cygwin 上の bash が No such file or directory と言ってエラーになる。echo の引数に日本語を渡してもダメ。 "echo ハローワールド" とかダメ。"echo Hello World" はOK
タスクごとにフォルダを掘る作業を自動化する Python スクリプト
モチベーション
一度に複数の仕事が降ってくるので、タスクごとにフォルダを掘って関連する資料を一か所にまとめるようにしている。
それからそのタスクの作業ログを専用のテキストファイルに書いている。
テキストファイルは週一でバックアップしていて、すぐにバックアップできるよう一つのフォルダに全部のタスクのを入れている。
タスクごとのフォルダからはショートカットでそのテキストにリンクを張っている。
仕事を振られるたびに専用のフォルダを掘って、専用のテキストファイルを用意して、ショートカットを作る。
この一連の作業をいつか自動化したいと思っていたので、やってみた。 とりあえず Python の CUI スクリプトで実現してみた。
#!/usr/bin/python3 # -*- coding: utf-8 -*- # # TASKDIR フォルダに指定したタスク専用のフォルダを掘り、 # TASKLOGDIR フォルダにそのタスク専用のログファイルを作成する。 # その後、ログファイルのショートカットを作成したフォルダに作る。 # import os import sys import re from datetime import datetime import traceback import subprocess from pathlib import Path import glob import win32com.client # \\ を / に置換した HOME # HOME = re.sub(r'\\', '/', os.environ['HOME']) HOME = "C:/cygwin64/home/somebody" # ★フォルダ名をここで指定する TASKDIR = f"{HOME}/タスク" # ★共通ログ置き場をここで指定する TASKLOGDIR = f"{HOME}/タスクログ" def get_task_dir_and_logfile_path(title): # TASKDIR フォルダがなければ何もしない if not os.path.exists(TASKDIR): return # YYYYMM_xxx で始まるフォルダをリストアップ today = datetime.today() dir_header = f"{TASKDIR}/{today.year:04d}{today.month:02d}_" taskdirs = sorted([d for d in glob.iglob(dir_header + "*") if os.path.isdir(d)]) # YYYYMM_xxx の数が一番大きいものを取り出す length = len(taskdirs) if length != 0: latest_dir = taskdirs[length-1] name = os.path.basename(latest_dir) # YYYYMM_xxx_yyy の xxx (番号)を取り出す number = int(name[7:10], base=10) # 次の番号は + 10 する number += 10 numstr = f"{number:03d}" else: # 初期値は 010 numstr = "010" dir_path = dir_header + f"{numstr}_{title}" logfile_path = f"{TASKLOGDIR}/{os.path.basename(dir_path)}.txt" return dir_path, logfile_path def win32_create_shortcut(target_path, shortcut_path): shell = win32com.client.Dispatch("WScript.shell") shortcut = shell.CreateShortcut(shortcut_path) shortcut.TargetPath = target_path shortcut.WindowStyle = 1 shortcut.Save() def win32_start(path): shell = win32com.client.Dispatch("WScript.shell") shell.Run(path) def create_task(title): # タスクフォルダとログファイルのパスを取得 dir_path, logfile_path = get_task_dir_and_logfile_path(title) # タスクフォルダがなければ新規作成 os.makedirs(dir_path, exist_ok=True) # ログファイルがなければ新規作成 if not os.path.exists(logfile_path): with open(logfile_path, "w") as f: # ちょっとした記述を追加しておく f.write("\n\n\n\n\n") f.write("-" * 72 + "\n") today = datetime.today() f.write( f"{today.year:04d}{today.month:02d}{today.day:02d}\n\n\n") # ショートカットの作成 shortcut_path = f"{dir_path}/{os.path.basename(logfile_path)}.lnk" win32_create_shortcut(logfile_path, shortcut_path) return dir_path, logfile_path, shortcut_path if __name__ == "__main__": title = input("Input new task title (q:quit): ") if title != "q": dir, logfile, shortcut = create_task(title) print("Task Created:") print("task dir:", dir) print("task log:", logfile) print("shortcut:", shortcut) win32_start(dir) win32_start(logfile) # input('\nHit any key to finish. ')
アイディア
VS Code のキーバインディングを Emacs 風にしてみた
↓この Extension を利用(キーバインディングもここに書かれている)
https://github.com/SebastianZaha/vscode-emacs-friendly
日本語キーボードだとちょっとキーアサインが異なる模様
ドキュメントの記述 | 日本語キーボードの場合 | 説明 |
---|---|---|
C-; | C-: | 行のコメント・アンコメントのトグル |
M-; | M-: | 選択範囲のコメント・アンコメントのトグル |
C-' | C-^ | Intelisense の表示 |
- 取りあえず記号を含むやつはうまくいかないらしい。英語キーボードの配列をもとに書かれているのでしょう。
Python メモ
win32com を使ってショートカットを作る
↓参考にしたサイト
[python] デスクトップにショートカットを作る | Reincarnation+
#!/usr/bin/python3 # -*- coding: utf-8 -*- import win32com.client # ショートカットを作る def win32_create_shortcut(target_path, shortcut_path): shell = win32com.client.Dispatch("WScript.shell") shortcut = shell.CreateShortcut(shortcut_path) shortcut.TargetPath = target_path shortcut.WindowStyle = 1 shortcut.Save()
ついでに start コマンドみたいな python 関数も作ってみた
# cmd.exe で実行する start みたいな、ファイルを開くやつ def win32_start(path): shell = win32com.client.Dispatch("WScript.shell") shell.Run(path)
でも、win32com は標準でついてないのでインストール必要。
win32com 本家
GitHub - mhammond/pywin32: Python for Windows (pywin32) Extensions
バイナリからインストールが簡単らしい
https://github.com/mhammond/pywin32/releases
VS Code で Python 関連
Python の選択
> Python: Select Interpreter // Python の実行ファイルを選ぶ
Linter の選択
> Python: Select Linter // linter を選ぶ
VSCode のターミナルからの win32com インストールしてみた
C:/Users/<your-name>/AppData/Local/Programs/Python/Python37/python.exe -m pip install -U pywin32 --user
(Python3.7が入っている前提)