├── .gitignore
├── .gitattributes
├── screenshot
├── igo-org-20201205.gif
└── igo-org-20201227.gif
├── igo-sgf-mode.el
├── igo-org.el
├── README.org
├── igo-sgf-parser.el
├── igo-org-export-html.el
├── igo-view.el
└── igo-model.el
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | *.elc
3 | README.html
4 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.el encoding=utf-8
2 | *.org encoding=utf-8
3 |
--------------------------------------------------------------------------------
/screenshot/igo-org-20201205.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/misohena/el-igo/master/screenshot/igo-org-20201205.gif
--------------------------------------------------------------------------------
/screenshot/igo-org-20201227.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/misohena/el-igo/master/screenshot/igo-org-20201227.gif
--------------------------------------------------------------------------------
/igo-sgf-mode.el:
--------------------------------------------------------------------------------
1 | ;;; igo-sgf-mode.el --- SGF Editing Mode -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2020 AKIYAMA Kouhei
4 |
5 | ;; Author: AKIYAMA Kouhei
6 | ;; Keywords: games
7 |
8 | ;; This program is free software; you can redistribute it and/or modify
9 | ;; it under the terms of the GNU General Public License as published by
10 | ;; the Free Software Foundation, either version 3 of the License, or
11 | ;; (at your option) any later version.
12 |
13 | ;; This program is distributed in the hope that it will be useful,
14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | ;; GNU General Public License for more details.
17 |
18 | ;; You should have received a copy of the GNU General Public License
19 | ;; along with this program. If not, see .
20 |
21 | ;;; Commentary:
22 |
23 | ;; (add-to-list 'auto-mode-alist '("\\.sgf$" . igo-sgf-mode))
24 |
25 | ;;; Code:
26 |
27 | (require 'igo-editor)
28 |
29 | (defvar-local igo-sgf-mode-editor nil)
30 |
31 | (defun igo-sgf-mode ()
32 | "Major mode for editing SGF files.
33 |
34 | The following commands are available:
35 |
36 | \\{igo-sgf-mode-map}"
37 | (interactive)
38 |
39 | ;; Create new igo-editor
40 | (setq igo-sgf-mode-editor (igo-editor (point-min) (point-max) nil nil t))
41 | (igo-sgf-mode-transfer-overlay-keymap-to-local-map igo-sgf-mode-editor)
42 | (igo-sgf-mode-track-buffer-read-only igo-sgf-mode-editor)
43 |
44 | ;; Set major mode
45 | (setq mode-name "SGF(Go Game)")
46 | (setq major-mode 'igo-sgf-mode)
47 | (setq font-lock-defaults '(igo-sgf-mode-font-lock-keywords t))
48 |
49 |
50 | (run-mode-hooks 'igo-sgf-mode-hook))
51 |
52 | ;; Font Lock
53 |
54 | (defvar igo-sgf-mode-font-lock-keywords
55 | '((igo-sgf-mode-fontify)))
56 |
57 | (defun igo-sgf-mode-fontify (_limit)
58 | (let ((editor igo-sgf-mode-editor))
59 | ;; Cover whole buffer
60 | (igo-editor-set-region editor (point-min) (point-max))
61 | ;; Update editor state from buffer text.
62 | (igo-editor-update editor)
63 | ;; Set font-lock properties
64 | (add-text-properties (point-min) (point-max)
65 | '(font-lock-fontified t font-lock-multiline t))
66 | ;; Highlight error place
67 | (if (igo-editor-last-error editor)
68 | (put-text-property (igo-editor-last-error-begin editor)
69 | (igo-editor-last-error-end editor)
70 | 'face 'igo-org-error-face))))
71 |
72 | (defun igo-sgf-mode-fontify-buffer ()
73 | (save-excursion
74 | (goto-char (point-min))
75 | (igo-sgf-mode-fontify (point-max))))
76 |
77 |
78 | ;; Keymap
79 |
80 | (defvar-local igo-sgf-mode-map nil)
81 |
82 | (defun igo-sgf-mode-transfer-overlay-keymap-to-local-map (editor)
83 | (let* ((ov (igo-editor-overlay editor))
84 | (keymap (overlay-get ov 'keymap)))
85 | ;; Transfer overlay's keymap property to local-map.
86 | (setq-local igo-sgf-mode-map keymap)
87 | (use-local-map keymap)
88 | (overlay-put ov 'keymap nil) ;; remove keymap property
89 | ;; Track keymap changes.
90 | (igo-editor-add-hook editor 'keymap-change #'igo-sgf-mode-on-keymap-change)
91 |
92 | editor))
93 |
94 | (defun igo-sgf-mode-on-keymap-change (_editor keymap)
95 | (setq-local igo-sgf-mode-map keymap)
96 | (use-local-map keymap)
97 | ;; Return t. EDITOR does not change the overlay's keymap.
98 | t)
99 |
100 | ;; Read Only
101 |
102 | (defun igo-sgf-mode-track-buffer-read-only (editor)
103 | (igo-sgf-mode-update-buffer-read-only editor)
104 | (igo-editor-add-hook editor 'text-mode
105 | #'igo-sgf-mode-update-buffer-read-only)
106 | (igo-editor-add-hook editor 'graphical-mode
107 | #'igo-sgf-mode-update-buffer-read-only))
108 |
109 | (defun igo-sgf-mode-update-buffer-read-only (editor)
110 | (setq buffer-read-only (not (igo-editor-text-mode-p editor))))
111 |
112 |
113 |
114 | (provide 'igo-sgf-mode)
115 | ;;; igo-sgf-mode.el ends here
116 |
--------------------------------------------------------------------------------
/igo-org.el:
--------------------------------------------------------------------------------
1 | ;;; igo-org.el --- SGF Editor for org-mode -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2020 AKIYAMA Kouhei
4 |
5 | ;; Author: AKIYAMA Kouhei
6 | ;; Keywords: games
7 |
8 | ;; This program is free software; you can redistribute it and/or modify
9 | ;; it under the terms of the GNU General Public License as published by
10 | ;; the Free Software Foundation, either version 3 of the License, or
11 | ;; (at your option) any later version.
12 |
13 | ;; This program is distributed in the hope that it will be useful,
14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | ;; GNU General Public License for more details.
17 |
18 | ;; You should have received a copy of the GNU General Public License
19 | ;; along with this program. If not, see .
20 |
21 | ;;; Commentary:
22 |
23 | ;;
24 |
25 | ;;; Code:
26 |
27 | (require 'org)
28 |
29 | ;;(require 'igo-editor)
30 | (autoload 'igo-editor "igo-editor")
31 | (declare-function igo-editor-set-region "igo-editor")
32 | (declare-function igo-editor-update "igo-editor")
33 | (declare-function igo-editor-last-error "igo-editor")
34 | (declare-function igo-editor-last-error-begin "igo-editor")
35 | (declare-function igo-editor-last-error-end "igo-editor")
36 | (declare-function igo-editor-set-status-bar-visible "igo-editor")
37 | (declare-function igo-editor-set-move-number-visible "igo-editor")
38 | (declare-function igo-editor-set-branch-text-visible "igo-editor")
39 | (declare-function igo-editor-set-editable "igo-editor")
40 | (declare-function igo-editor-find-by-queries "igo-editor")
41 | (declare-function igo-editor-set-grid-interval "igo-editor")
42 |
43 | (defface igo-org-error-face
44 | '((((supports :underline (:style wave)))
45 | :underline (:style wave :color "Red1"))
46 | (t
47 | :underline t :inherit error))
48 | "SGF face for errors."
49 | :group 'igo-org-faces)
50 |
51 |
52 | (defun igo-org-setup ()
53 | (interactive)
54 | (with-eval-after-load "org"
55 | (add-hook 'org-mode-hook #'igo-org-startup)
56 | (igo-org-hook-fontify-block)))
57 |
58 |
59 | ;;
60 | ;; Variables
61 | ;;
62 |
63 | (defvar-local igo-org-block-defaults nil)
64 |
65 |
66 | ;;
67 | ;; Hook Fontify
68 | ;;
69 |
70 | (defun igo-org-hook-fontify-block ()
71 | ;;(advice-add #'org-src-font-lock-fontify-block :around #'igo-org--fontify-src-block-advice)
72 | (advice-add #'org-fontify-meta-lines-and-blocks-1 :around #'igo-org--fontify-igo-block-advice)
73 | (advice-add #'org-unfontify-region :around #'igo-org--unfontify-region-advice))
74 |
75 | (defun igo-org-unhook-fontify-block ()
76 | (advice-remove #'org-src-font-lock-fontify-block #'igo-org--fontify-src-block-advice)
77 | (advice-remove #'org-fontify-meta-lines-and-blocks-1 #'igo-org--fontify-igo-block-advice)
78 | (advice-remove #'org-unfontify-region #'igo-org--unfontify-region-advice))
79 |
80 |
81 | ;; Fontify
82 |
83 | (defun igo-org-fontify-block (start end &optional options)
84 | ;; Skip leading&trailing line break
85 | ;; (#+begin_src sgf[start]\n ... \n[end]#+end_src)
86 | (if (and (< start end) (= (char-after start) ?\n))
87 | (setq start (1+ start)))
88 | (if (and (< start end) (= (char-before end) ?\n))
89 | (setq end (1- end)))
90 |
91 | (let ((editor (seq-some
92 | (lambda (o) (overlay-get o 'igo-editor))
93 | (overlays-in start end))))
94 |
95 | (if (null editor)
96 | ;; Create a new editor
97 | (setq editor (igo-org-create-editor start end options))
98 | ;; Cover region.
99 | (igo-editor-set-region editor start end)
100 | ;; Update editor state from region text.
101 | (igo-editor-update editor))
102 |
103 | ;; Highlight error place
104 | (if (igo-editor-last-error editor)
105 | (put-text-property (igo-editor-last-error-begin editor)
106 | (igo-editor-last-error-end editor)
107 | 'face 'igo-org-error-face))))
108 |
109 | ;; Fontify src block (#+begin_src sgf ~ #+end_src)
110 |
111 | (defun igo-org--fontify-src-block-advice (old-func lang start end)
112 | (if (string= lang "sgf")
113 | (igo-org-fontify-block start end)
114 | (funcall old-func lang start end)))
115 |
116 | ;; Fontify igo block (#+begin_igo ~ #+end_igo)
117 |
118 | (defun igo-org--fontify-igo-block-advice (old-func limit)
119 | (let* ((p (point))
120 | (org-protecting-blocks (cons "igo" org-protecting-blocks))
121 | ;; Fontify meta and blocks
122 | (ret-val (funcall old-func limit)))
123 | (if ret-val
124 | ;; Overwrite between #+begin_igo and #+end_igo if exists
125 | (save-match-data
126 | (save-excursion
127 | (goto-char p)
128 | (igo-org-fontify-igo-block limit))))
129 | ret-val))
130 |
131 | (defun igo-org-fontify-igo-block (limit)
132 | "Fontify #+begin_igo block. Call after
133 | org-fontify-meta-lines-and-blocks-1. Overwrite text property
134 | between #+begin_igo and #+end_igo."
135 |
136 | ;; The following code is derived from org-fontify-meta-lines-and-blocks-1
137 | (let ((case-fold-search t))
138 | (when (re-search-forward
139 | (rx bol (group (zero-or-more (any " \t")) "#"
140 | (group (group (or (seq "+" (one-or-more (any "a-zA-Z")) (optional ":"))
141 | (any " \t")
142 | eol))
143 | (optional (group "_" (group (one-or-more (any "a-zA-Z"))))))
144 | (zero-or-more (any " \t"))
145 | ;; options
146 | (group (zero-or-more any))))
147 | limit t)
148 | (let ((block-start (match-end 0)) ; includes the \n at end of #+begin line
149 | (block-end nil) ; will include \n after end of block content
150 | (dc3 (downcase (match-string 3)))
151 | (options (match-string 6))
152 | block-type)
153 | (cond
154 | ((and (match-end 4) (equal dc3 "+begin"))
155 | ;; Truly a block
156 | (setq block-type (downcase (match-string 5)))
157 | (when (re-search-forward
158 | (rx-to-string `(group bol (or (seq (one-or-more "*") space)
159 | (seq (zero-or-more blank)
160 | "#+end"
161 | ,(match-string 4)
162 | word-end
163 | (zero-or-more any)))))
164 | nil t) ;; on purpose, we look further than LIMIT
165 | ;; We do have a matching #+end line
166 | (setq block-end (match-beginning 0)) ; includes the final newline.
167 |
168 | ;;
169 | ;; Fontify begin_igo block
170 | ;;
171 | (when (string= block-type "igo")
172 | (igo-org-fontify-block block-start block-end options)
173 | t))))))))
174 |
175 | ;; Unfontify
176 |
177 | (defun igo-org--unfontify-region-advice (old-func beg end &optional maybe_loudly)
178 | (funcall old-func beg end maybe_loudly)
179 | (igo-org-unfontify-region beg end))
180 |
181 | (defun igo-org-unfontify-region (beg end)
182 | (dolist (ov (overlays-in beg end))
183 | (when (overlay-get ov 'igo-editor)
184 | ;; keep overlay if src block still exists
185 | ;; (surrounded by begin_src and end_src)
186 | (when (not
187 | (and
188 | (save-excursion
189 | (goto-char (overlay-start ov))
190 | (forward-line -1)
191 | (looking-at "[ \t]*#\\+begin_\\(src +sgf\\|igo\\)"))
192 | (save-excursion
193 | (goto-char (overlay-end ov))
194 | (forward-line 1)
195 | (looking-at "[ \t]*#\\+end_\\(src\\|igo\\)"))))
196 | ;;(message "delete overlay %s" ov)
197 | (delete-overlay ov)))))
198 |
199 | ;;
200 | ;; Create Editor
201 | ;;
202 |
203 | (defun igo-org-create-editor (start end options-str)
204 | (let ((editor (igo-editor start end nil nil t))
205 | (options (org-babel-parse-header-arguments options-str t)))
206 | ;; Merge in-buffer settings to options
207 | (dolist (default-opt igo-org-block-defaults) ;;@todo Is (org-babel-parse-header-arguments (igo-org-get-buffer-option "igo_block_defaults")) better? But slow for big files
208 | (if (not (seq-some (lambda (o) (eq (car o) (car default-opt))) options))
209 | (push default-opt options)))
210 | ;; Apply options to editor
211 | (dolist (opt options)
212 | (let ((key (car opt))
213 | (value (cdr opt)))
214 | (cond
215 | ((eq key :status-bar)
216 | (igo-editor-set-status-bar-visible editor (igo-org-opt-bool value)))
217 | ((eq key :move-number)
218 | (igo-editor-set-move-number-visible editor (igo-org-opt-bool value)))
219 | ((eq key :branch-text)
220 | (igo-editor-set-branch-text-visible editor (igo-org-opt-bool value)))
221 | ((eq key :editable)
222 | (igo-editor-set-editable editor (igo-org-opt-bool value)))
223 | ((eq key :read-only)
224 | (igo-editor-set-editable editor (not (igo-org-opt-bool value))))
225 | ((eq key :path)
226 | (igo-editor-find-by-queries editor (igo-org-opt-split-path value)))
227 | ((eq key :grid-interval)
228 | (igo-editor-set-grid-interval editor (igo-org-opt-int value)))
229 | )))
230 | editor))
231 |
232 | (defun igo-org-opt-split-path (value)
233 | (cond
234 | ((null value) nil)
235 | ((numberp value) (list value))
236 | ((stringp value) (split-string value "[/ \f\t\n\r\v]+" t "[ \f\t\n\r\v]+"))))
237 |
238 | (defun igo-org-opt-bool (value)
239 | (null (member value '("no" "nil" nil))))
240 |
241 | (defun igo-org-opt-int (value)
242 | (cond
243 | ((integerp value) value)
244 | ((numberp value) (round value))
245 | ((stringp value) (string-to-number value))
246 | (t nil)))
247 |
248 | (defun igo-org-opt-value (key options &optional default-value)
249 | (cdr (or (assq key options) (cons key default-value))))
250 |
251 | (defun igo-org-opt-bool-value (key options &optional default-value)
252 | (igo-org-opt-bool
253 | (igo-org-opt-value key options default-value)))
254 |
255 | ;; #+IGO_BLOCK_DEFAULTS:
256 |
257 | (defun igo-org-startup ()
258 | (if-let ((block-defaults-str (igo-org-get-buffer-option "igo_block_defaults")))
259 | (setq igo-org-block-defaults
260 | (org-babel-parse-header-arguments block-defaults-str t))))
261 |
262 | (defun igo-org-get-buffer-option (keyword)
263 | (save-excursion
264 | (goto-char (point-min))
265 | (let ((re-keyword (org-make-options-regexp (list keyword))))
266 | (when (re-search-forward re-keyword nil t 1)
267 | (match-string-no-properties 2)))))
268 |
269 |
270 | (provide 'igo-org)
271 | ;;; igo-org.el ends here
272 |
--------------------------------------------------------------------------------
/README.org:
--------------------------------------------------------------------------------
1 | #+TITLE: el-igo - Emacs Go Game(SGF) Editor
2 | #+OPTIONS: toc:nil num:nil ^:nil
3 |
4 | Emacs内で囲碁の盤面を表示し、棋譜(SGF形式)を編集します。
5 |
6 | [[file:./screenshot/igo-org-20201227.gif]]
7 |
8 | * 動作環境
9 | Emacs 27.1
10 |
11 | * org-mode文書内に棋譜を埋め込む(igo-org.el)
12 | ** 設定
13 | init.elに次のコードを追加してください。
14 |
15 | #+begin_src emacs-lisp
16 | (with-eval-after-load "org"
17 | (require 'igo-org)
18 | (igo-org-setup))
19 | #+end_src
20 |
21 | また、HTMLとしてエクスポートしたときに盤面を表示したい場合は[[#org-export][後述]]の設定も追加してください。
22 |
23 | ** 使い方
24 | org文書内にigo特殊ブロックを挿入します。(操作例: C-c C-, SPC i g o RET)
25 |
26 | #+begin_src org
27 | ,#+begin_igo
28 | ,#+end_igo
29 | #+end_src
30 |
31 | ブロックの中に (;SZ[9]) と入力すると9路盤が表示されます。またはブロック内で C-c i を押すと盤面サイズを指定して初期化できるようになっています。
32 |
33 | 後は適当に石を置いたり、フリー編集モードで初期盤面を設定したり、コメントを設定したりすると、その操作毎にバッファのテキストが書き換わります。
34 |
35 | ** ブロック毎のオプション
36 |
37 | =#+begin_igo= の後にブロック毎のオプションを指定できます。
38 |
39 | 例えば次のようにするとブロックが盤面化されたとき(orgファイルを開いたとき等)に最初から手順番号が表示されます。
40 |
41 | #+begin_src org
42 | ,#+begin_igo :move-number t
43 | (;FF[4]GM[1]SZ[9];B[fd];W[df];B[ff];W[dd])
44 | ,#+end_igo
45 | #+end_src
46 |
47 | 有効なオプションには次のものがあります。
48 |
49 | | オプション名 | 意味 | 値 |
50 | |----------------+-----------------------------+---------------------------------------------------------------|
51 | | :status-bar | ステータスバーの表示状態 | nil, noのとき非表示 |
52 | | :move-number | 手順番号の表示状態 | nil, noのとき非表示 |
53 | | :branch-text | 分岐文字(A,B,...)の表示状態 | nil, noのとき非表示 |
54 | | :editable | 編集可能 | nil, noのとき編集不可能 |
55 | | :read-only | 編集禁止(:editableの逆) | nil, noのとき編集可能 |
56 | | :path | 最初に表示する盤面(ノード) | 盤面へたどり着く経路(後述) |
57 | | :display | エクスポート時の表示方法 | 表示方法を示す値([[#org-export-options][後述]]) |
58 | | :grid-interval | 格子線の間隔 | ピクセル数(デフォルトは igo-board-view-grid-interval-default) |
59 |
60 | orgファイル内に ~#+IGO_BLOCK_DEFAULTS:~ を記述することで、ファイル内でのオプションのデフォルト値を設定できます。(注意:現在の所、変更時はファイルを開き直す必要があります)
61 |
62 | 例:
63 | #+begin_src org
64 | ,#+IGO_BLOCK_DEFAULTS: :grid-interval 24 :move-number t
65 | #+end_src
66 |
67 | *** :pathの指定方法
68 |
69 | :path オプションは最初に表示する盤面(ゲーム木のノード)を指定します。
70 |
71 | パス(盤面へたどり着く経路)は次の要素を並べたもので、最初の盤面(ルート)から各要素が指定する盤面を順番にたどり最終的に到達した盤面が選択されます。
72 |
73 | | 数字 | 現在の盤面から数字で指定しただけ後の盤面(途中分岐がある場合は最初の分岐(A)を選択)(無い場合は最後の盤面) |
74 | | -数字 | 現在の盤面から数字で指定しただけ前の盤面(無い場合は最初の盤面) |
75 | | アルファベット二文字 | SGF座標とみなし、その場所に打つ現在の盤面から最も近い盤面(幅優先探索) |
76 | | 大文字アルファベット一文字 | 現在の盤面以降の最初の分岐での分岐先(A:0, B:1, ...)を指定したものと見なし、その分岐直後の盤面 |
77 | | \under{} (アンダーバー) | 現在の盤面以降の最初の分岐がある盤面(分岐が無ければ最後の盤面) |
78 |
79 | 例:
80 | - 0 : 最初の盤面
81 | - 12 : 最初から12手目の盤面
82 | - cd : 最初に3の4に着手する盤面
83 | - cc/fc : 最初に左上三々(cc)に着手した後、次に最初に二間開き(fc)した盤面
84 | - cc/2 : 最初に着手した後の2手目
85 | - A/B/A : 分岐をA、B、Aの順に選択した盤面
86 | - _ : 最初の分岐(無ければ最後の盤面)
87 | - _/-1 : 最初の分岐(無ければ最後の盤面)の一つ前
88 |
89 | * SGFファイルの編集(igo-sgf-mode.el)
90 | ** 設定
91 | init.elに次のコードを追加してください。
92 |
93 | #+begin_src emacs-lisp
94 | (autoload 'igo-sgf-mode "igo-sgf-mode")
95 | (add-to-list 'auto-mode-alist '("\\.sgf$" . igo-sgf-mode))
96 | #+end_src
97 |
98 | sgfファイルを開くと自動的に盤面が表示されます。
99 |
100 | 注意: sgfファイルはオセロ、チェス、バックギャモンなど囲碁以外のゲームの棋譜も表現できます。あなたがそれらを扱う場合は、GMプロパティが1以外のときにigo-sgf-modeを起動しないようにする必要があります(未実装)。
101 |
102 | * バッファ内の任意の部分を盤面にする(igo-editor.el)
103 |
104 | 任意のSGFテキストをリージョンで囲った上で M-x igo-edit-region を実行すると、その範囲がエディタ化されます。
105 |
106 | igo-sgf-mode.el も igo-org.el も igo-editor.el を使って実装されています。igo-sgf-mode.elはバッファ全体を、igo-org.elはbegin_igo~end_igoの間を自動的にエディタ化します。
107 |
108 | * エディタの使い方
109 | ** モード
110 | エディタは大きく分けて次のようなモードを持っています。
111 |
112 | - テキストモード
113 | - 固定モード(エラーが無くなってもテキストのまま)
114 | - 自動回復モード(エラーが無くなったときに自動的にグラフィカルモードに移行する)
115 | ** キー操作
116 | 各モードで使えるキー操作は次の通りです。
117 |
118 | *** テキストモード
119 |
120 | | 操作 | 説明 | 関数 |
121 | |-------+--------------------------+---------------------------|
122 | | C-c q | エディタの終了 | igo-editor-quit |
123 | | C-c g | グラフィカルモードへ移行 | igo-editor-graphical-mode |
124 | | C-c i | 盤面の初期化 | igo-editor-init-board |
125 |
126 | *** グラフィカルモード共通
127 |
128 | | 操作 | 説明 | 関数 |
129 | |---------------------------+----------------------------------------+-------------------------------|
130 | | C-c g | テキストモードへ移行 | igo-editor-text-mode |
131 | | C-x c-q | 編集可能状態切り替え | igo-editor-toggle-editable |
132 | |---------------------------+----------------------------------------+-------------------------------|
133 | | a, \vert{}<ボタンクリック | 最初へ | igo-editor-first-node |
134 | | e, >\vert{}ボタンクリック | 最後へ(デフォルト選択でたどれる所まで) | igo-editor-last-node |
135 | | b, <ボタンクリック | 前へ | igo-editor-previous-node |
136 | | f, >ボタンクリック | 次へ(デフォルト選択でたどれる場合) | igo-editor-next-node |
137 | | M-b | 前の分岐地点へ | igo-editor-previous-fork |
138 | | M-f | 次の分岐地点へ | igo-editor-next-fork |
139 | | n | 次の盤面を分岐の中から選択して表示 | igo-editor-select-next-node |
140 | |---------------------------+----------------------------------------+-------------------------------|
141 | | Q | 着手モード | igo-editor-move-mode |
142 | | F | フリー編集モード | igo-editor-free-edit-mode |
143 | | M | マーク編集モード | igo-editor-mark-edit-mode |
144 | |---------------------------+----------------------------------------+-------------------------------|
145 | | s s | ステータスバー表示切り替え | igo-editor-toggle-status-bar |
146 | | s n | 手順番号表示切り替え | igo-editor-toggle-move-number |
147 | | s b | 分岐表示切り替え | igo-editor-toggle-branch-text |
148 | |---------------------------+----------------------------------------+-------------------------------|
149 | | c | コメントの編集 | igo-editor-edit-comment |
150 | | N | 手順番号の編集 | igo-editor-edit-move-number |
151 | | g | 対局情報の編集 | igo-editor-edit-game-info |
152 | |---------------------------+----------------------------------------+-------------------------------|
153 | | x | 盤面のSVG画像出力 | igo-editor-export-image |
154 | |---------------------------+----------------------------------------+-------------------------------|
155 | | C-c i | 盤面の初期化 | igo-editor-init-board |
156 |
157 | *** 着手モード
158 |
159 | | 操作 | 説明 | 関数 |
160 | |-----------------------+--------------------------------------+------------------------------------|
161 | | P, Passボタンクリック | パス | igo-editor-pass |
162 | | p | 石を置く | igo-editor-put-stone |
163 | | 盤面をクリック | 石を置く | igo-editor-move-mode-board-click |
164 | | Passボタン右クリック | 着手「パス」に対するメニューを表示 | igo-editor-pass-click-r |
165 | | 盤面を右クリック | 交点(石や空点)に対するメニューを表示 | igo-editor-move-mode-board-click-r |
166 | | \dollar | 現在の盤面をルートにする | igo-editor-make-current-node-root |
167 |
168 | *** フリー編集モード
169 |
170 | (現在の所、一番最初の盤面でのみ使用できます)
171 |
172 | | 操作 | 説明 | 関数 |
173 | |------------------------+--------------------------+----------------------------------|
174 | | Quitボタンクリック | 着手モードへ切り替え | igo-editor-move-mode |
175 | | p | 交点を選択中の状態にする | igo-editor-free-edit-put |
176 | | 盤面をクリック | 交点を選択中の状態にする | igo-editor-free-edit-board-down |
177 | | B, Blackボタンクリック | 黒石を選択する | igo-editor-free-edit-black |
178 | | W, Whiteボタンクリック | 白石を選択する | igo-editor-free-edit-white |
179 | | E, Emptyボタンクリック | 空点を選択する | igo-editor-free-edit-empty |
180 | | T, Turnボタンクリック | 次の手番を反転させる | igo-editor-free-edit-toggle-turn |
181 |
182 | *** マーク編集モード
183 |
184 | | 操作 | 説明 | 関数 |
185 | |-----------------------+--------------------------+----------------------------------|
186 | | Quitボタンクリック | 着手モードへ切り替え | igo-editor-move-mode |
187 | | p | 交点を選択中の状態にする | igo-editor-mark-edit-put |
188 | | 盤面をクリック | 交点を選択中の状態にする | igo-editor-mark-edit-board-down |
189 | | X, Xボタンクリック | ×マークを選択する | igo-editor-mark-edit-cross |
190 | | O, Oボタンクリック | ○マークを選択する | igo-editor-mark-edit-circle |
191 | | S, SQボタンクリック | □マークを選択する | igo-editor-mark-edit-square |
192 | | T, TRボタンクリック | △マークを選択する | igo-editor-mark-edit-triangle |
193 | | E, Textボタンクリック | テキストを選択する | igo-editor-mark-edit-text |
194 | | D, Delボタンクリック | 消去を選択する | igo-editor-mark-edit-del |
195 |
196 | ** 分岐の編集
197 |
198 | 前の手に戻って別の場所に打つと自動的に分岐が作られます。エディタは全ての分岐をツリー構造で記録しています。
199 |
200 | 分岐は分岐直前の盤面でAから始まるアルファベットで表示されます。
201 |
202 | 分岐箇所を示すアルファベットを「左クリック」すると、その分岐に進みます。
203 |
204 | 「次へ進む」ボタンは最後に選んだ分岐へ進みますが、もしまだ選択していない場合は明示的に指定する必要があります。
205 |
206 | 分岐を削除したい場合や分岐の(アルファベットの)順番を変更したい場合は、アルファベットを *「右クリック」* してください。分岐に対する操作がポップアップメニューで表示されます。
207 |
208 | * org-modeでHTMLエクスポートする
209 | :PROPERTIES:
210 | :CUSTOM_ID: org-export
211 | :END:
212 |
213 | org-mode文書中に棋譜を埋め込んだのならエクスポート後の文書にも盤面が表示されて欲しいと思うことでしょう。ここではHTMLでのエクスポート時に盤面を出力する方法について説明します。
214 |
215 | ** org-mode標準機能だけで処理する方法
216 | 標準のHTMLバックエンドは特殊ブロックをdivタグで囲んだ形で出力します。 ~#+begin_igo~ ~ ~#+end_igo~ も特殊ブロックなので、HTMLで出力すると ~~ の形で出力されます。
217 |
218 | 盤面の形で表示させたい場合は、ページ読み込み完了時にJavaScriptで一括変換すると良いでしょう。
219 |
220 | 例えば拙作のJavaScript碁盤を使用するならば [[https://github.com/misohena/js_igo][misohena/js_igo: JavaScript Go Game Board]] より igo.js, igo_view.js, igo.css をダウンロードした上で、次のようにします。
221 |
222 | #+begin_src org
223 | ,#+HTML_HEAD:
224 | ,#+HTML_HEAD:
225 | ,#+HTML_HEAD:
226 | ,#+HTML_HEAD:
227 |
228 | お互いに二連星したところ。
229 |
230 | ,#+begin_igo
231 | (;FF[4]GM[1]SZ[9];B[fd];W[df];B[ff];W[dd])
232 | ,#+end_igo
233 | #+end_src
234 |
235 | ** エクスポータをカスタマイズする
236 |
237 | igo特殊ブロックの出力(変換結果)自体を変える方法もあります。
238 |
239 | igo特殊ブロックの出力を変えるためにはorg-modeのエクスポータをカスタマイズしなければなりませんが、その方法として次の三つを用意しました。
240 |
241 | - 既存のHTMLバックエンドを修正する
242 | - HTMLバックエンドから派生した新しいバックエンドを定義する
243 | - バッファローカル変数を書き替える
244 |
245 | ** 既存のHTMLバックエンドを修正する
246 |
247 | 最も使い勝手の良い方法は既存のHTMLバックエンドを修正することです。一度設定してしまえばファイル毎に設定する必要はありませんし、普段通りの操作でエクスポートできます。
248 |
249 | 次のコードを init.el に追加してください。
250 |
251 | #+begin_src emacs-lisp
252 | (with-eval-after-load "ox-html"
253 | (require 'igo-org-export-html)
254 | (igo-org-modify-html-backend))
255 | #+end_src
256 |
257 | すると org-mode のHTMLバックエンドが読み込まれた直後にそれを書き替えてigo特殊ブロック( ~#+begin_igo~ ~ ~#+end_igo~ 部分)を特別に処理するようにします。
258 |
259 | デフォルトは拙作のJavaScript碁盤を使うHTMLを出力するので、 [[https://github.com/misohena/js_igo][misohena/js_igo: JavaScript Go Game Board]] より igo.js, igo_view.js, igo.css をダウンロードしてhtmlと同じ場所に配置してください。
260 |
261 | あとは通常通りHTMLとしてエクスポートするだけです。
262 |
263 | ** HTMLバックエンドから派生した新しいバックエンドを定義する
264 |
265 | 何らかの理由でHTMLバックエンドを直接修正して欲しくない場合、新しく専用のバックエンドを定義することも出来ます。
266 |
267 | 次のコードを実行すると igo-html という名前のバックエンドが登録されます。
268 |
269 | #+begin_src emacs-lisp
270 | (require 'igo-org-export-html)
271 | (igo-org-define-html-backend t)
272 | #+end_src
273 |
274 | ~igo-org-define-html-backend~ の引数に ~t~ を指定するとメニューに項目を追加します。
275 |
276 | =C-c C-e= でエクスポートのメニューを出した後、 g を押すことでこのバックエンドを指定できます。
277 |
278 | ** 個別のファイルにフィルタを設定する
279 |
280 | バックエンドをカスタマイズしなくても、ファイルごとにバッファローカル変数を設定することで変換処理を変更できます。
281 |
282 | この方法を使うには、org-mode文書の中に次のコードを追加してください。
283 |
284 | #+begin_src org
285 | ,#+begin_src emacs-lisp :exports results :results none
286 | (require 'igo-org-export-html)
287 | (igo-org-set-html-filters-local)
288 | ,#+end_src
289 | #+end_src
290 |
291 | ~:exports results~ を指定することでエクスポートのたびにソースブロックを評価させ、 ~:results none~ を指定することで結果の出力を抑制しています。
292 |
293 | 後は通常通りHTMLとしてエクスポートしてください。
294 |
295 | org-modeのデフォルト設定ではエクスポートするたびにソースブロックを評価するか聞いてくるので yes を入力してください。
296 |
297 | するとバッファローカル変数に変換フィルタが設定され、igo特殊ブロックが特別に変換されます。
298 |
299 | ** オプション
300 | :PROPERTIES:
301 | :CUSTOM_ID: org-export-options
302 | :END:
303 |
304 | igo-org-export-html.el を使用して変換する場合、次のオプションが使用できます。
305 |
306 | | オプション | 意味 | デフォルト値 |
307 | |----------------------------------------------+---------------------------------------------------------------------+------------------------------|
308 | | ~#+IGO_JS_PATH:~ // | スクリプトのあるディレクトリへのパス | igo-org-js-pathの値 (./) |
309 | | ~#+IGO_JS_TEMPLATE:~ // | HTMLのHEADに挿入するテキストの雛形(displayがjsのとき) | igo-org-js-templateの値 |
310 | | ~#+IGO_LAZY_JS_TEMPLATE:~ // | igo特殊ブロックの中に挿入するテキストの雛形(displayがlazy-jsのとき) | igo-org-lazy-js-templateの値 |
311 | | ~#+IGO_HEADER_TEMPLATE:~ // | HTMLのHEADに挿入するテキストの雛形(displayがcustomのとき) | igo-org-header-templateの値 |
312 | | ~#+IGO_BLOCK_TEMPLATE:~ // | igo特殊ブロックの中に挿入するテキストの雛形(displayがcustomのとき) | igo-org-block-templateの値 |
313 | | ~#+IGO_ENABLE:~ // | igo特殊ブロックを変換するかどうか | t |
314 | | ~#+IGO_DISPLAY:~ // | igo特殊ブロックをどのように変換するか | js |
315 |
316 | ~#+IGO_ENABLE: nil~ を指定するとファイル内で一切の変換を抑制できます。
317 |
318 | ~#+IGO_DISPLAY:~ には次のいずれかを指定できます。
319 |
320 | | // | 表示方法 |
321 | |------------------+-----------------------------------------------------------------------------------|
322 | | ~none~ | 非表示 |
323 | | ~noconv~ | 変換しない(SGFテキストのまま) |
324 | | ~js~ | js_igoによるJavaScript碁盤 |
325 | | ~lazy-js~ | js_igoによるJavaScript碁盤(遅延読み込み型。にスクリプトを挿入しない) |
326 | | ~svg~ | SVG画像埋め込み |
327 | | ~custom~ | ~#+IGO_HEADER_TEMPLATE:~ と ~#+IGO_BLOCK_TEMPLATE:~ を使用 |
328 |
329 | また、表示方法はブロック毎に ~#+begin_igo :display ~ の形でも指定できます。
330 |
331 | 例:
332 |
333 | #+begin_src org
334 | ,#+IGO_JS_PATH: ./js_igo/
335 | ,#+IGO_DISPLAY: svg
336 |
337 | ,#+begin_igo :move-number t :path A/A/B
338 | ....省略(SVGで表示)....
339 | ,#+end_igo
340 |
341 | ,#+begin_igo :read-only t :move-number t :path 5 :display js
342 | ....省略(js_igoで表示)....
343 | ,#+end_igo
344 | #+end_src
345 |
346 | 表示方法によってはテンプレート文字列を指定できます。
347 |
348 | オプションの // の部分は、HTMLのHEAD要素内に挿入するテキストの雛形です。表示方法が js であるブロックが存在する場合、 ~#+IGO_JS_TEMPLATE:~ オプションで指定した雛形が使われます。また、表示方法が custom であるブロックが存在する場合、 ~#+IGO_HEADER_TEMPLATE:~ オプションで指定した雛形も使われます。
349 |
350 | // の中では次の記法が使用できます。
351 |
352 | | % // % | 置き換え先 |
353 | |------------------+---------------------------------|
354 | | ~%PATH%~ | ~#+IGO_JS_PATH:~ オプションの値 |
355 |
356 | オプションの // の部分は、igo特殊ブロックの中に挿入するテキストの雛形です。表示方法が lazy-js のブロックには ~#+IGO_LAZY_JS_TEMPLATE:~ オプションで指定した雛形が使われます。また、表示方法が custom であるブロックには ~#+IGO_BLOCK_TEMPLATE:~ オプションで指定した雛形が使われます。
357 |
358 | // の中では次の記法が使用できます。
359 |
360 | | % // % | 置き換え先 |
361 | |------------------+---------------------------------|
362 | | ~%PATH%~ | ~#+IGO_JS_PATH:~ オプションの値 |
363 | | ~%SGF%~ | SGFテキスト |
364 | | ~%OPT_JSON%~ | js_igo用のオプション |
365 | | ~%SVG%~ | 盤面をSVG化したテキスト |
366 |
367 | また、%の後に次のいずれかを指定することでテキスト内の一部の文字をエスケープできます。
368 |
369 | | 記法 | 置き換える文字 | 置き換え後の文字 |
370 | |------------------------+--------------------------------+-----------------------------------|
371 | | ~%HTML_%~ | & < > | & < > |
372 | | ~%ATTR_%~ | & < > " | & < > " |
373 | | ~%LITERAL_%~ | \ " 改行 "
79 | "The js_igo template for lazy loading."
80 | :group 'el-igo
81 | :type 'string)
82 |
83 | (defcustom igo-org-header-template
84 | ""
85 | "The header template used when display option is custom."
86 | :group 'el-igo
87 | :type 'string)
88 |
89 | (defcustom igo-org-block-template
90 | "%HTML_SGF%"
91 | "The block template used when display option is custom."
92 | :group 'el-igo
93 | :type 'string)
94 |
95 | (defcustom igo-org-html-display
96 | 'lazy-js
97 | "How to display igo special blocks."
98 | :group 'el-igo
99 | :type '(choice (const :tag "Not display" none)
100 | (const :tag "Not convert(SGF text)" noconv)
101 | (const :tag "SVG image" svg)
102 | (const :tag "Use js_igo" js)
103 | (const :tag "Use js_igo with lazy loading" lazy-js)
104 | (const :tag "Use custom template" custom)))
105 |
106 | (defvar igo-org-js-options-alist
107 | '((:igo-js-path "IGO_JS_PATH" nil igo-org-js-path)
108 | (:igo-js-template "IGO_JS_TEMPLATE" nil igo-org-js-template)
109 | (:igo-lazy-js-template "IGO_LAZY_JS_TEMPLATE" nil igo-org-lazy-js-template)
110 | (:igo-header-template "IGO_HEADER_TEMPLATE" nil igo-org-header-template)
111 | (:igo-block-template "IGO_BLOCK_TEMPLATE" nil igo-org-block-template)
112 | (:igo-enable "IGO_ENABLE" nil "t")
113 | (:igo-display "IGO_DISPLAY" nil igo-org-html-display)))
114 |
115 |
116 | ;; Modify Special Block
117 |
118 | (defun igo-org-html-filter-parse-tree (data backend info)
119 | "A filter function to set to org-export-filter-parse-tree-functions variable.
120 |
121 | Modify all igo special blocks in the DATA tree."
122 | (when (or (eq backend 'html)
123 | (org-export-derived-backend-p
124 | (org-export-get-backend backend) 'html))
125 | (when (and ;;(not (memq 'body-only (plist-get info :export-options)))
126 | (not (member (plist-get info :igo-enable) '(nil "" "nil" "no"))))
127 |
128 | (let ((display-types-used (igo-org-modify-all-special-blocks data info)))
129 |
130 | ;; Add HTML header
131 | (if (memq 'js display-types-used)
132 | (igo-org-add-to-html-head-extra
133 | info
134 | (igo-org-instantiate-header-template
135 | (or (plist-get info :igo-js-template) "")
136 | info)))
137 | (if (memq 'custom display-types-used)
138 | (igo-org-add-to-html-head-extra
139 | info
140 | (igo-org-instantiate-header-template
141 | (or (plist-get info :igo-header-template) "")
142 | info))))))
143 | data)
144 |
145 | (defun igo-org-add-to-html-head-extra (info header-html)
146 | (if (and (stringp header-html) (not (string= header-html "")))
147 | (if-let ((head-extra (plist-get info :html-head-extra)))
148 | (plist-put info :html-head-extra
149 | (concat head-extra "\n" header-html))
150 | ;; Add :html-head-extra property to last of INFO
151 | (if info
152 | (setcdr (last info) (list :html-head-extra header-html))
153 | ;; @todo if info is null
154 | (error "info is null")))))
155 |
156 | (defun igo-org-modify-all-special-blocks (data info)
157 | (let (display-types-used)
158 | (org-element-map data 'special-block
159 | (lambda (special-block)
160 | (let ((display-type (igo-org-modify-special-block special-block info)))
161 | (if (null (memq display-type display-types-used))
162 | (push display-type display-types-used)))))
163 | display-types-used))
164 |
165 | (defun igo-org-modify-special-block (special-block info)
166 | (when (string= "igo" (org-element-property :type special-block))
167 | (let* ((options (igo-org-get-special-block-header-options special-block))
168 | (display
169 | (or
170 | (let ((v (igo-org-opt-value :display options)))
171 | (cond ((stringp v) (intern (downcase v)))
172 | ((symbolp v) v)))
173 | (let ((v (plist-get info :igo-display)))
174 | (cond ((stringp v) (intern (downcase v)))
175 | ((symbolp v) v))))))
176 | (cond
177 | ((eq display 'none)
178 | (org-element-set-contents special-block nil))
179 | ((or (eq display 'noconv)
180 | (eq display 'sgf)))
181 | ((eq display 'js)
182 | (igo-org-modify-special-block-js special-block info))
183 | ((eq display 'lazy-js)
184 | (igo-org-modify-special-block-lazy-js special-block info))
185 | ((eq display 'svg)
186 | (igo-org-modify-special-block-svg special-block info))
187 | ((eq display 'custom)
188 | (igo-org-modify-special-block-custom special-block info)))
189 | display)))
190 |
191 | (defun igo-org-modify-special-block-js (special-block info)
192 | ;; Add attr_html
193 | (igo-org-js-add-html-attr-to-special-block special-block)
194 | ;; Remove
195 | (igo-org-replace-element-contents-to-html
196 | special-block
197 | (igo-org-get-special-block-text special-block info)))
198 |
199 | (defun igo-org-js-add-html-attr-to-special-block (special-block)
200 | (let ((opt-json (igo-org-make-options-for-js_igo
201 | (igo-org-get-special-block-header-options
202 | special-block))))
203 | (if (not (string= opt-json ""))
204 | (org-element-put-property
205 | special-block :attr_html
206 | (list (concat (car (org-element-property :attr_html special-block))
207 | " :data-igo-js t :data-igo-opt " opt-json))))))
208 |
209 | (defun igo-org-modify-special-block-lazy-js (special-block info)
210 | (igo-org-replace-element-contents-to-html
211 | special-block
212 | (igo-org-instantiate-block-template
213 | (or (plist-get info :igo-lazy-js-template) "")
214 | special-block info)))
215 |
216 | (defun igo-org-modify-special-block-svg (special-block info)
217 | (igo-org-replace-element-contents-to-html
218 | special-block
219 | (igo-org-create-svg-from-special-block special-block info)))
220 |
221 | (defun igo-org-modify-special-block-custom (special-block info)
222 | (igo-org-replace-element-contents-to-html
223 | special-block
224 | (igo-org-instantiate-block-template
225 | (or (plist-get info :igo-block-template) "")
226 | special-block info)))
227 |
228 |
229 | (defun igo-org-replace-element-contents-to-html (element html)
230 | "Replace the contents of ELEMENT to HTML."
231 | ;;@todo support replace option? (org-element-set-element element new-elem)
232 | (org-element-set-contents
233 | element
234 | ;; Wrap export block (#+begin_export html ~ #+end_export)
235 | (org-element-create 'export-block
236 | (list :type "HTML"
237 | :value html))))
238 |
239 | ;;
240 | ;; Template Instantiation
241 | ;;
242 |
243 | (defun igo-org-instantiate-template (template vars)
244 | (replace-regexp-in-string
245 | "%[A-Z_]+%"
246 | (lambda (var-spec)
247 | (save-match-data
248 | (string-match "\\`%\\(\\(LITERAL\\|ATTR\\|HTML\\)_\\|\\)\\([A-Z_]+\\)%\\'" var-spec)
249 | (let* ((prefix (match-string 2 var-spec))
250 | (name (match-string 3 var-spec))
251 | (rep (cdr (assoc name vars))))
252 | (cond
253 | ((functionp rep) (igo-org-instantiate-template-escape prefix (funcall rep)))
254 | ((stringp rep) (igo-org-instantiate-template-escape prefix rep))
255 | (t var-spec)))))
256 | template
257 | t t))
258 |
259 | (defun igo-org-instantiate-template-escape (type string)
260 | (save-match-data
261 | (cond
262 | ;; for HTML Attribute
263 | ((string= type "ATTR")
264 | (replace-regexp-in-string
265 | "\"" """
266 | (org-html-encode-plain-text string)
267 | t t))
268 | ;; for JavaScript String Literal
269 | ((string= type "LITERAL")
270 | (dolist (pair '(("\\\\" . "\\\\")
271 | ("\n" . "\\n")
272 | ("\"" . "\\\"")
273 | ("