Author Archives: AKIYAMA

2023-04-11

dired-details-r.elの更新

次の点を変更しました。

  • image-diredのサムネイルやall-the-icons-diredのアイコンに対応
  • ファイル名が長いときの空白を調整
  • global-dired-details-r-modeを追加
  • find-dired系に一応対応
  • dired-after-readin-hookを使用するようにした

今回の一番の目的はimage-dired対応です。ファイル名の前に存在するオーバーレイの幅を計算に入れてレイアウトします。all-the-icons-diredのオーバーレイも一緒に考慮しなければならなかった(分離できなかった)のでそうしました。all-the-icons-diredは独自に色々手を入れているのでオリジナルでは未確認です。image-diredもサムネイルの幅が揃うように手元では独自に手を入れています。動作が遅くなったり不安定になる可能性が若干あるので無効化する変数も用意してあります。サムネイルのサイズは非同期生成や画像の編集操作で変化するのでその際はレイアウトが崩れます。gを押してください。

image-diredのサムネイルとall-the-icons-diredを考慮して詳細情報の右側表示を行う
図1: image-diredのサムネイルとall-the-icons-diredを考慮して詳細情報の右側表示を行う

以下はついでに直した所です。

ファイル名が長すぎるときはバッファ全体のファイル名欄の幅を拡大せずにそのファイルだけレイアウトが崩れるようにしてありますが、その際ファイル名の右に余分な空白があれば削除するようにしました。ファイルサイズが右寄せされているために無駄に空白が空いていることがありました。

global-dired-details-r-modeは全体のON/OFFをしやすくするために追加しました。

find-dired系(find-name-diredやfind-grep-dired)での動作を改善しました。デフォルトでは見た目を変更しないようになっています。find-diredではファイル名にディレクトリが含まれるため長すぎてレイアウトが崩れがちだからです。 ( を押すとレイアウトを変更するようにはしてあります。私はfind-dired系をあまり使わないのでどうするのが一番良いのかよく分かりません。

dired-after-readin-hookを使用するようにしました。これまではadvice-addでdired-insert-set-propertiesとdired-revertに処理を挟んでいましたが、こちらのやり方の方が普通のようです。ただし、find-diredも含めいくつかdiredバッファを直接書き替えた後にdired-insert-set-propertiesを呼び出してdired-after-readin-hookを呼び出さないケースがあるようです。そういったものにもちゃんと対応するならこれまでのやり方も併用した方が良いのかもしれません。

misohena/dired-details-r: Show file details on the right side of the filename in Emacs Dired mode

(追記: 長すぎるファイル名を切り詰める機能も追加しました)

(追記2: ファイル名の最大幅をウィンドウ幅から決める機能を追加しました)

(追記3: ついに詳細を左にも両側にも表示できるようになってしまいました。dired-details​-r​なのにw)

詳細を左にも両側にも表示する例
図2: 詳細を左にも両側にも表示する例
2023-03-23 ,

Emacs内で地図を見るosm.elを使う

osm.el というものがあることは少し前から知っていたのですが、別にEmacsの中で地図を見なくてもいいんじゃないかと思って試していませんでした。まぁEmacsの中で動く作図ソフトなんかを作ってるお前が言うのかという感じではあるのですが。

とは言え登山計画や登山記録の作成に何か活用できないかと考え、時間があったので少し試してみることにしました。

minad/osm: osm.el - OpenStreetMap viewer for Emacs

使ってみて驚いたのがとにかく速いということ。Emacs内のキー操作でシームレスに使えるというのもあるのですが、起動自体がブラウザでGoogle Mapsを開くよりも速いのです。もちろんタイルをキャッシュしているからというのはあるのですが、それは向こうだって同じ事。きっと余分な処理が少ないからなのでしょう。

キーボードだけで無くマウスでも普通に操作できますし、メニューもあるのでキー割り当てを覚えていなくても問題ありません。閉じる操作だけキーが必要でしょうか。qがquit-windowなので、バッファもkillしたいならC-u qすれば良いのでしょう。

私はC-f, C-b, C-p, C-nでも移動がしたかったので次のような設定を加えました。

(with-eval-after-load 'osm
  (define-key osm-mode-map [remap previous-line] #'osm-up-up)
  (define-key osm-mode-map [remap next-line] #'osm-down-down)
  (define-key osm-mode-map [remap forward-char] #'osm-right-right)
  (define-key osm-mode-map [remap backward-char] #'osm-left-left))

また、次の設定で国土地理院の地理院地図を表示するようにしました。

(with-eval-after-load 'osm
  (setq osm-server 'jgsi)
  (setf (alist-get 'jgsi osm-server-list)
        '(:name
          "地理院地図 標準地図"
          :max-zoom 18
          :description "地理院地図 標準地図"
          :url "https://cyberjapandata.gsi.go.jp/xyz/std/%z/%x/%y.png"
          :group "地理院地図"
          :copyright
          ("Map data © {国土地理院|https://www.gsi.go.jp/}"))))

後は自宅とか言語とか。

(setq osm-home '(35.XXXXXX 139.XXXXXX 14)
      osm-search-language "ja,en")

osm.elにはGPXファイルの軌跡データを表示する機能があります。GPXは登山の記録アプリでも広く使われている形式です。試しに表示させてみたところ次のようになりました。

2023-03-23-osm-el.jpg

大変面白いのですが、しかしこれだけだと単にEmacsの中で地図や軌跡が見られるというだけです。何かもう少し有意義な使い方が出来ないものでしょうか。最低限Emacsの中から任意の地点へリンクを張りたいですよね。

同梱されているosm-ol.elというのを使用すると次のような形式のorg-modeリンクが使えるようになります。

[[geo:36.399109,137.715168;z=15][燕山荘]]

……あれ、これ以前私も同じような仕組みを作りましたよね。

緯度経度リンクタイプをorg-modeに追加する

misohena/org-geolink: Adds geo location link type to org-mode.

私の方はリンクを各種地図サービス(Webブラウザ)で開いたり、エクスポート時に好きな形式に変換するためのものでした。

osm-ol.elの方は単にリンクをosm.elで開くだけのようです。エクスポートや外部サービスで開くことは考慮していないようです。

なのでosm-ol.elは使わず、私のorg-geolinkをosm.elと連携させることにしました。

org-geolinkがgeoリンクを開くときの方法としてosm.elを選べるようにしました。また、osm.elで見ている場所を各種地図サービスで開くコマンドを追加しました。

これによって

[[geo:~]] → osm.el(Emacs内) → 各種地図サービス(Webブラウザ内)

という流れで使うことができるようになりました。

つまり、まずは高速なosm.elで見て、必要に応じて(施設情報閲覧や経路検索がしたいなら)各種地図サービスを開く事が出来ます。

設定例:

(when (locate-library "org-geolink")
  (with-eval-after-load "org"
    (require 'org-geolink))

  (when (locate-library "osm")
    ;; [[geo:]]リンクをosm.elで開く
    (setq org-geolink-follow-function 'org-geolink-open-by-osm-el)

    ;; osmバッファ内において、Oで外部地図サービスを開く
    (with-eval-after-load 'osm
      (define-key osm-mode-map (kbd "O")
        #'org-geolink-open-osm-el-location-by-selected-web-service)))

  ;;地図サービスを追加
  (setq org-geolink-map-services-user
        ;; NAVITIME
        '((navitime
           (name . "NAVITIME")
           (url . "https://www.navitime.co.jp/maps/poi?lat={{{1}}}&lon={{{2}}}"))
          ;; ヤマケイオンライン
          (yamakei
           (name . "ヤマケイオンライン(ヤマタイム)")
           (url . "https://www.yamakei-online.com/yk_map/?latlon={{{1}}},{{{2}}}&zoom={{{z}}}"))
          ;; ヤマレコ
          (yamareco
           (name . "ヤマレコ(ヤマプラ)")
           (url . "https://www.yamareco.com/modules/yr_plan/step1_planner.php?lat={{{1}}}&lon={{{2}}}")))))

もっといろんな活用方法があるような気がするのですが、時間があったら色々やってみるかもしれません。

2022-12-30

cl-defmethodやcl-defunで作成した関数に対するeldocを改善する

例えば次のようなコードを書いたとしましょう。

(require 'eieio)

(defclass myshape-rect () ;;Emacs Lispは何でもかんでも接頭辞必須なのが鬱陶しいですね。
  ((x-min :initarg :x-min :type number)
   (y-min :initarg :y-min :type number)
   (x-max :initarg :x-max :type number)
   (y-max :initarg :y-max :type number)))

(defclass myshape-ellipse ()
  ((cx :initarg :cx :type number)
   (cy :initarg :cy :type number)
   (rx :initarg :rx :type number)
   (ry :initarg :ry :type number)))

(cl-defmethod myshape-scale ((rect myshape-rect) sx &optional (sy sx) &key (ox 0) (oy 0))
  (with-slots (x-min y-min x-max y-max) rect
    (setf x-min (+ (* (- x-min ox) sx) ox)
          x-max (+ (* (- x-max ox) sx) ox)
          y-min (+ (* (- y-min oy) sy) oy)
          y-max (+ (* (- y-max oy) sy) oy)))
  rect)

(cl-defmethod myshape-scale ((ellipse myshape-ellipse) sx &optional (sy sx) &key (ox 0) (oy 0))
  (with-slots (cx cy rx ry) ellipse
    (setf cx (+ (* (- cx ox) sx) ox)
          cy (+ (* (- cy oy) sy) oy)
          rx (* rx sx)
          ry (* ry sy)))
  ellipse)

で、myshape-scaleメソッドを試してみますか。

(let ((rect (myshape-rect :x-min 100 :x-max 200 :y-min 1000 :y-max 1100)))
  (myshape-scale …

ん?

2022-12-30-issue-method-args.png

「ARG &rest ARGS」って何だよ。

こんなの見せられたって一つ以上の引数を取る関数としか分からないじゃないか。

いや、分かってるよ。cl-defgenericを書けって言いたいんでしょ? うっせーバーカ! それにしたって「ARG &rest ARGS」は無いでしょう。こんなの出すなら何も出さない方がマシ。それとも煽ってるの?

……まぁいいや、とりあえずお試しだからcl-defgenericを書くとして

(cl-defgeneric myshape-scale (shape sx &optional (sy sx) &key (ox 0) (oy 0)))

sxは2、syもとりあえず2でいいかな。原点は……

(let ((rect (myshape-rect :x-min 100 :x-max 200 :y-min 1000 :y-max 1100)))
  (myshape-scale rect 2 2

ん? なんでsxの部分がハイライトされてるの?

2022-12-30-issue-divided-arg.png

キーワードも全然ダメじゃないか。

2022-12-30-issue-keyword.png

この部分ってアレでしょ? cl-defunってやつと同じ。Common Lispの。そもそもあれよく知らないんだよね……(cl-defunのお勉強へ)


なるほどね。

cl-defunで定義した関数でも同様の問題が発生します。一つの引数が複数の要素を含むリストになっている場合は必ず問題が生じます。単純に空白で分割しているだけのようです。キーワードも対応するものをハイライトするなんてことにはなっていないようです。

ミニバッファに情報を表示しているのはeldoc。特に関数呼び出し時の表示は elisp-eldoc-funcall の仕事です。

というわけでこの辺りを修正すべく作成したのがこちら。

my-elisp-eldoc-funcall.el

まず関連するメソッドは全て表示します。出し惜しみせず知ってることは素直に全て出せば良いんです。

2022-12-30-fixed-method.png

ちゃんとひとまとまりの部分をハイライトします。

2022-12-30-fixed-divided-arg.png

キーワードも対応する場所をハイライトします。

2022-12-30-fixed-keyword.png

cl-defunで定義した関数にも対応しました。通常の関数と区別が付かないのでちょっと心配ではあるのですが。

&keyと&restは同時にハイライトします。どちらにも入りますからね。

2022-12-30-fixed-cl-defun.png

こういうこともできますが、本当はC++みたいに多重定義を静的に解決してくれたら最高なんですけどね。型推論とか入ってくれてもいいのよ?

2022-12-30-edraw-to-string.png

はぁ、LSPでコードの解析が出来ると騒がしい昨今に何で自分でこんなことやってるんだろう。それも年末に。もう12月30日じゃないですか。

良いお年を。