TOP

■メモ|OEdit関連■

OEditの起動シーケンス

・図中の通し番号が進むに連れて、使用可能な関数が増えて行く。(代表的なものだけ列挙しときます)
 Bの時点〜
  ・R5RS準拠関数の殆ど。(ファイル入出力も可能)
  ・ハンドラ系。(app-set-event-handler、app-get-event-handler、app-clear-event-handler)
  ・キー割り当て系。(app-set-key、app-set-cancel-key、app-get-all-keys)
  ・app-get-tool-dir
  ・app-get-command-line-option
 Dの直後〜
  ・表示系関数が使える。(app-input-boxのみBの時点から使える)
  ・タイマー系が使える。(app-set-timer、app-clear-timer)
 Eの直後〜
  ・この時点で初めて、エディタウィンドウ(OEditメインウィンドウ)が現れる。
  ・editor-系関数が使える。
・『init.scm』『oedit.scm』は、OEditプロセス1つについて1度切りしか実行されない仕組みになってる風。
・app-get-command-line-option関数が取得するコマンドライン引数は、
 OEditプロセス毎に割り当てられた固有の値で、
 プロセスがキルされる迄、その値を保持し続ける。(途中で値が変わったりする事はない風)
OEditの起動オプション処理
・オプション処理ルール概要。
・起動オプションは、必ず、ファイル名が先でオプションが後で指定する。
 →『/d』オプションだけはやや例外的。(ファイル名指定がない場合にのみ機能する風)
  その理由は、ファイル名指定がある場合は、
  『そのファイルの所在フォルダが、起動ディレクトリとして優先される』
  仕様にあるようだ。(推測)
・ファイル名には、ワイルドカードが使える。
 →但し、マッチした最初のファイルのみが開かれる。(=使えないも同然)
・オプション名とオプションパラメータとの間には、空白を挟んでも挟まなくても良い。
 →例えば、『/m html』でも『/mhtml』でも同じ。
・オプション名は、英大小文字を区別する。
 →例えば、『/r10』は機能するが、『/R10』は機能しない。(未知のオプションとみなされる)
・2文字以上のオプション名を考慮していない。
 →例えば、『/rc10.15』とかは、オプション『/r』+パラメータ『c10.15』とみなされ、
  『起動オプションエラー:/rのあとは数字の指定が必要です』エラーで弾かれる。
・知らないオプションは、単に無視される。
 →例えば、『oedit readme.txt /x10』は『oedit readme.txt』と等価。(エラーとならない)
・オプションとして有効なのは『/〜』系のみ。
 →『-〜』系のワードは、ファイルパスの一部として扱われる。
 →例えば、
  @『oedit readme.txt -x10』は、意味的に『oedit "readme.txt -x10"』と同等。
  A『oedit "readme.txt" -x10』は、意味的に『oedit "readme.txt" "-x10"』と同等。
・起動オプションの効用は、既に(別プロセスのOEditで)開いているファイルに迄及ぶ。
 →例えば、『oedit.exe readme.txt /mtext』でreadme.txtを開いた状態で、続けて
      『oedit.exe readme.txt /mhtml』を行うと、
  既に開いているOEdit側にちゃんと『/mhtml』が効く。
・起動オプションの効用は、『ファイル→開く』で開くファイルには波及しない。
 →例えば、『oedit.exe readme.txt /mhtml』でreadme.txtを開いた状態で、続けて
  『ファイル→開く』から『ChangeLog.txt』を開くと、『/mhtml』が再度適用されたりはしない。
・コマンドライン引数として、複数のファイル名を指定可能。
 →その場合は、↓なまとまり毎に各オプションが効く。
  『oedit ファイル名1 オプション群ファイル名n オプション群』
  例えば、
  @『oedit readme.txt /r10 oedit.ini /r20』は、『readme.txtに/r10』『oedit.iniに/r20』で効く。
  A『oedit readme.txt /r10 oedit.ini』は、『readme.txtに/r10』『oedit.iniオプションなし』で効く。
 →オプションなしの際の複数ファイル名指定には制約が掛かる。
  (オプションが上手くファイル名区切りとして機能していた事に起因する)
  例えば、
  @『oedit readme.txt /r10 oedit.ini』は、『readme.txt』『oedit.ini』(2個のファイル)が開かれる。
  A『oedit readme.txt oedit.ini』は、『readme.txt oedit.ini』(1個のファイル)が開かれる。
 →オプションなしの場合でも、明示的にクォートする事で制約を回避可能。
  例えば、『oedit "readme.txt" "oedit.ini"』は、ちゃんと2個のファイルで開かれる。
 →ファイル名複数指定の際は、先頭ファイルから順に開かれる。(プロセスの並び上は)
  ・その際は、コマンドライン引数は先頭プロセスに引き継がれる。(複数ファイル名指定の状態で)
  ・その他のプロセスには、各々『"自ファイル名フルパス"』(クォート含む)が引き継がれる。
  ・実は、見た目上は『2番目〜最後、先頭』の順で開かれて見える。
   これは、↓手順で複数ファイル起動処理が行われているからだと推測される。
   1)空OEditプロセスが起動。
   2)2番目から順にファイルを『oedit "当ファイル名フルパス"』にて新規プロセス起動して行く。
   3)空OEditプロセスが、自プロセス上で1番目のファイルを開く。
Schemeで自作起動オプション処理に挑戦
前述のOEditの起動シーケンスOEditの起動オプション処理を踏まえて、幾つかのポイントを列挙してみた。

【ポイント】
・起動オプションは、app-get-command-line-option関数で拾えばよい。
 →起動オプションは1個の生文字列として取得されるので、それを意味的に分解する関数を作る必要。
・自作オプションは、↓ルールで設計する。
 @『/〜』型を採用。(『-〜』型は、ファイルパスの一部とみなされるので不可)
 AOEdit標準オプションと誤認されぬ様、オプション名は、先頭がd/m/rの3種以外で始まる必要。
・どのタイミングで処理するか?
 案@『oedit.scm』中で処理する。
  →シーケンス図によると、『oedit.scm』時点では未だファイルが開かれておらず、
   エディタ系の操作の殆どが行えない。
   だから、テキスト操作に関わるオプション処理(カーソル位置を指定して起動とか)には向かない。
  →一方で、エディタの環境操作に関わるオプション処理(行番号ON/OFFを指定して起動とか)なら出来そうだ。
   ただ、現状では、エディタ環境を操作する様な関数群は用意されていないので将来に期待と言った所。
 案A『oedit.scm』中でハンドラ設定して、on-open-fileイベント捕捉時に処理する。
  →これなら、既にファイルが開かれており、テキスト操作に関わるオプション処理もこなせそうだ。
   今回はこれを採用。

【試作品】
;▼コマンドライン引数(文字列)をバラして、文字列リストにして返す関数▼
(define (apx-get-command-line-args) (get-args (app-get-command-line-option)))
(define (get-args s)
  (define (skip-space) ;◆半角空白をスキップ。(その次位置へ)
    (define c (peek-char ip))
    (if (eqv? c #\space) (begin (read-char ip) (skip-space)))
  )
  (define (get-inner-quote os) ;◆"〜"内の文字列を取得。(その次位置へ)
    (define c (read-char ip))
    (if (or (eof-object? c) (eqv? c #\"))
      os
      (get-inner-quote (string-append os (string c)))
    )
  )
  (define (get-word os) ;◆単語を取得。(その次位置へ)
    (define c (peek-char ip))
    (cond
      ((or (eof-object? c) (eqv? c #\space)) os)
      ((eqv? c #\") (begin (read-char ip) (string-append os (get-inner-quote ""))))
      (else (get-word (string-append os (string (read-char ip)))))
    )
  )
  (define ip (open-input-string s))
  (let loop ( (ol '()) )
    (define c (read-char ip))
    (cond
      ((eof-object? c)  (close-input-port ip) (reverse ol))
      ((eqv? c #\space) (skip-space) (loop ol))
      ((eqv? c #\") (loop (cons (get-inner-quote "")  ol)))
      (else         (loop (cons (get-word (string c)) ol)))
    )
  )
)

;▼自作起動オプション処理用ハンドラ▼
;・自作オプション一覧。
; @/xa***-***-***-*** 例『oedit.exe ReadMe.txt /xa7-1-10-4』
;  ・指定した範囲を選択した状態で、ファイルを開く。
;  ・***は、順にrow1/col1/row2/col2を示す数値。
; A/xc***-***     例『oedit.exe ReadMe.txt /xc135-24』
;  ・指定した位置にキャレットを移した状態で、ファイルを開く。
;  ・***は、順にrow1/col1を示す数値。
; B/xt***       例『oedit.exe ReadMe.txt /xt"template.txt"』
;  ・指定したテンプレファイルをテキスト先頭に挿入した状態で、ファイルを開く。
;  ・***は、テンプレファイルへのパス。
(define handle-my-option (lambda ()
  (define (option-launcher ops) ;◆起動オプション処理コア関数。
    (cond
      ((rxmatch #/^\/xa(\d+)-(\d+)-(\d+)-(\d+)$/ ops) => (lambda (m)
        (editor-set-select-area
          (- (string->number (rxmatch-substring m 1)) 1)
          (- (string->number (rxmatch-substring m 2)) 1)
          (- (string->number (rxmatch-substring m 3)) 1)
          (- (string->number (rxmatch-substring m 4)) 1)
        )))
      ((rxmatch #/^\/xc(\d+)-(\d+)$/ ops) => (lambda (m)
        (editor-set-row-col
          (- (string->number (rxmatch-substring m 1)) 1)
          (- (string->number (rxmatch-substring m 2)) 1)
        )))
      ((rxmatch #/^\/xt(.*)$/ ops) => (lambda (m)
        (define s (get-template (rxmatch-substring m 1)))
        (editor-set-row-col 0 0)
        (editor-paste-string s)))
    )
  )
  (for-each option-launcher (apx-get-command-line-args))
))
(define (get-template fn) ;◆ファイルを読み込んで、その内容文字列を返す関数。
  (call-with-input-file fn (lambda (inf)
    (define op (open-output-string))
    (define ans (let loop ()
      (define c (read-char inf))
      (if (eof-object? c)
        (get-output-string op)
        (begin (write-char c op) (loop))
      )
    ))
    (close-output-port op)
    ans
  ))
)

;▼on-open-fileイベントハンドラとしてセット▼
(app-set-event-handler "on-open-file" handle-my-option)
・↑を『oedit.scm』中に記述すると、『/xa〜』『/xc〜』『/xt〜』の3自作起動オプションが使えるようになる。
・大体『oedit readme.txt /xa1-3-2-7』みたいに使用する。
 『oedit readme.txt /xa"1-3-2-7"』風に、パラメータをクォートしても等価。
 『oedit readme.txt /xa "1-3-2-7"』風に、パラメータを離すのはダメ。

【問題点】
@ファイル名未指定でOEditを起動した場合に、オプション処理が行われない。
 →on-open-fileイベントが発生しないので、現状では仕方ない。
  例えば、『oedit /xt"template.txt"』な局面とか。
Aパラメータ付きの自作オプションに対して、『半角空白を含む』パラメータを送れない。
 →正確には、送れるんだけども、OEdit標準のコマンドライン処理の副作用が生じてしまう。
  例1『oedit readme.txt /xt"template a.txt"』
   →副作用として、OEditが『a.tx』という余分なファイルを開く。
  例2『oedit readme.txt /xt "template a.txt"』
   →副作用として、OEditが『template a.txt』という余分なファイルを開く。
B既に開いているファイルを別オプションで開き直す際に、新オプションの処理が行われない。
 例.『oedit readme.txt /xc2-5』の状態で、『oedit readme.txt /xc10-2』を行う。
 →既に開いているファイルを開く際には、on-open-fileイベントが発生しない。
 →また、app-get-command-line-option関数の返り値も更新されない。
 →現状では、どうしようもない。
C『ファイル→開く』にて別ファイルに切り替える際に、起動オプションがまた働いてしまう。
 例.『oedit readme.txt /xc2-5』の状態で、『changelog.txt』に切り替える。
 →『ファイル→開く』にて別ファイルに切り替える際に、on-open-fileイベントが発生する。
 →その際にもapp-get-command-line-option関数の返り値は更新されておらず、
  結果として、on-open-fileに触発されて起動時の『/xc2-5』オプションが、また処理される。
 →これについては、2度目以降のon-open-fileイベントをスルーする等の工夫で回避可能。
  ↓回避してみた例。(ハンドラにカウンタを内臓させた)

【一部改良版】
;▼on-open-fileイベントハンドラとしてセット|カウンタ内臓版▼
(app-set-event-handler "on-open-file" (let ((counter 0)) (lambda ()
  (set! counter (+ 1 counter))
  (if (= counter 1) (handle-my-option)) ;◆最初のon-open-fileイベントでのみ発動。
)))