├── .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で出力すると ~

~ /SGFテキスト/ ~

~ の形で出力されます。 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 | ("