;;; lookup-types.el --- Lookup various data types ;; Copyright (C) 1999 Lookup Development Team ;; Author: Keisuke Nishida ;; Version: $Id: lookup-types.el,v 1.3 1999/05/23 17:27:21 knishida Exp $ ;; This file is part of Lookup. ;; Lookup is free software; you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2 of the License, or ;; (at your option) any later version. ;; Lookup is distributed in the hope that it will be useful, but ;; WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with Lookup; if not, write to the Free Software Foundation, ;; Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ;;; Code: (require 'evi) (require 'lookup-utils) (require 'lookup-vars) (put 'lookup-defstruct 'lisp-indent-function 2) (defmacro lookup-defstruct (name slots &rest args) ;; Lookup で用いるデータ構造を定義するためのマクロ。 ;; NAME はデータの名前。SLOTS はデータが保持する情報のリスト。 ;; ARGS は追加の設定。下記の実例を参照。 ;; これにより定義されたデータタイプは、以下の関数を持つことになる。 ;; ;; lookup-make-NAME - データを生成する ;; lookup-NAME-p - データタイプをチェックする ;; lookup-NAME-SLOT - SLOT のデータを参照する ;; lookup-NAME-set-SLOT - SLOT にデータをセットする ;; ;; ARGS に :with-properties オプションを付けた場合、更に次の関数が作られる。 ;; ;; lookup-NAME-get-property - オブジェクトの属性を得る ;; lookup-NAME-put-property - オブジェクトに属性を設定する ;; lookup-NAME-PROP - PROP のデータを参照する ;; lookup-NAME-set-PROP - PROP にデータをセットする ;; (let* ((str (symbol-name name)) (n 0) (prefix (concat "lookup-" str "-")) (tag (list 'quote (intern (concat ":" str)))) (with-properties (memq ':with-properties args)) (properties (eval (plist-get args ':with-properties))) ;; function names (f-make (intern (concat "lookup-make-" str))) (f-p (intern (concat prefix "p"))) (f-plist (intern (concat prefix "plist"))) (f-get-prop (intern (concat prefix "get-property"))) (f-put-prop (intern (concat prefix "put-property")))) (nconc (list 'progn ;; Constructor: ;; ;; (defsubst lookup-make-NAME (SLOT...) ;; (vector :NAME SLOT... nil)) (let ((plist (if lookup-debug-mode '(lookup-new-plist)))) (list 'defsubst f-make slots (cons 'vector (cons tag (if with-properties (append slots (list plist)) slots))))) ;; Predicate: ;; ;; (defun lookup-NAME-p (NAME) ;; (and (vectorp NAME) (eq (aref NAME 0) :NAME))) (list 'defun f-p (list name) (list 'and (list 'vectorp name) (list 'eq (list 'aref name 0) tag)))) ;; Accessors: ;; (apply 'nconc (mapcar (lambda (slot) (let* ((str (symbol-name slot)) (f-ref (intern (concat prefix str))) (f-set (intern (concat prefix "set-" str)))) (list ;; (defsubst lookup-NAME-SLOT (NAME) ;; (aref NAME n)) (list 'defsubst f-ref (list name) (list 'aref name (setq n (1+ n)))) ;; (defsubst lookup-NAME-set-SLOT (NAME SLOT) ;; (aset NAME n SLOT)) (list 'defsubst f-set (list name slot) (list 'aset name n slot))))) slots)) ;; Properties: ;; (when with-properties (setq n (1+ n)) (list ;; (defmacro lookup-NAME-plist (NAME &optional plist) ;; (if plist (list 'aset NAME n plist) (list 'aref NAME n))) (list 'defmacro f-plist (list name '&optional 'plist) (list 'if 'plist (if lookup-debug-mode (` (list 'set (list 'aref (, name) (, n)) plist)) (list 'list ''aset name n 'plist)) (if lookup-debug-mode (` (list 'symbol-value (list 'aref (, name) (, n)))) (list 'list ''aref name n)))) ;; (defun lookup-NAME-get-property (NAME prop) ;; (plist-get (lookup-NAME-plist NAME) prop)) (list 'defun f-get-prop (list name 'prop) (list 'plist-get (list f-plist name) 'prop)) ;; (defun lookup-NAME-put-property (NAME prop value) ;; (lookup-NAME-plist NAME ;; (plist-put (lookup-NAME-plist NAME) prop value))) (list 'defun f-put-prop (list name 'prop 'value) (list f-plist name (list 'plist-put (list f-plist name) 'prop 'value))))) (when with-properties (apply 'nconc (mapcar (lambda (prop) (let* ((str (symbol-name prop)) (ref (intern (concat prefix str))) (set (intern (concat prefix "set-" str)))) (list ;; (defsubst lookup-NAME-PROP (NAME) ;; (lookup-NAME-get-property NAME 'PROP)) (list 'defsubst ref (list name) (list f-get-prop name (list 'quote prop))) ;;(defsubst lookup-NAME-set-PROP (NAME PROP) ;; (lookup-NAME-put-property NAME 'PROP PROP)) (list 'defsubst set (list name prop) (list f-put-prop name (list 'quote prop) prop))))) properties)))))) (defun lookup-new-plist () ;; plist を保持するためのシンボルを生成する。 ;; plist をそのままデータ構造に組み込むと、デバッグの際に出力が大きく ;; なり過ぎて見づらくなる。そのため、変数 `lookup-debug-mode' が non-nil ;; の場合には、オブジェクトにはこのシンボルを放り込んでおき、その値として ;; plist を処理する。 (let ((plist (make-symbol "plist"))) (set plist nil) plist)) ;;;;;;;;;;;;;;;;;;;; ;: Search Method ;;;;;;;;;;;;;;;;;;;; ;; Description: ;; ;; 各種検索方式。それぞれの検索方式は単一のシンボルによって表現される。 ;; 現在、次のものが有効。 ;; ;; exact - 完全一致検索 ;; prefix - 前方一致検索 ;; suffix - 後方一致検索 ;; substring - 部分一致検索 ;; regexp - 正規表現検索 ;; keyword - キーワード検索 ;; text - 全文検索 ;; reference - リファレンス ;; ;; 与えられた検索方式から実際にどのような検索を行なうかは各 agent に ;; 任されており、厳密な規定はない。それぞれの検索方式についての一般的な ;; 定義は、マニュアルの "Search Methods" を参照。 (defconst lookup-word-search-methods '(exact prefix suffix substring regexp keyword stemming text)) (defconst lookup-search-methods (cons 'default (cons 'reference lookup-word-search-methods))) (defconst lookup-method-key-alist '((exact . "=") (prefix . "<") (suffix . ">") (substring . "-") (regexp . "r") (keyword . "@") (stemming . "^") (text . "/") (default . "*") (reference . "%"))) (defun lookup-method-key (method) (or (lookup-assq-ref lookup-method-key-alist method) (error "Unknown search method: %s" method))) ;;;;;;;;;;;;;;;;;;;; ;: Search Query ;;;;;;;;;;;;;;;;;;;; ;; Description: ;; ;; 検索するパターンを格納するためのデータタイプ。Lookup はユーザの入力を ;; 解析し、検索方式に応じてこのタイプのオブジェクトを生成する。生成された ;; データは最終的に検索エージェントに渡され、エージェント毎にその内容を ;; 判断して検索が実行される。 ;; Structure: ;; ;; [:query METHOD STRING] ;; ;; METHOD は検索方式。変数 `lookup-search-methods' にあるシンボルが有効。 ;; STRING は検索文字列。METHOD と対にして、検索するパターンが決定される。 ;; この文字列はあらかじめ、不要な文字を取り除く等、ある程度の正規化を ;; しておくことが望ましい。 (lookup-defstruct query (method string)) ;; Functions: ;; ;; lookup-parse-pattern - 引数の文字列を解析し、query を生成する。 ;; lookup-query-to-regexp - query を regexp パターンに変換する。 (defun lookup-parse-pattern (pattern) ;; 入力文字列 PATTERN を解析し、query を生成して返す。 (let (method) (cond ;; 'word' -> match exactly ((string-match "^'\\(.*\\)'$" pattern) (setq method 'exact pattern (match-string 1 pattern))) ;; /word/ -> match regexp ((string-match "^/\\(.*\\)/$" pattern) (setq method 'regexp pattern (match-string 1 pattern))) ;; /word -> search text ((string-match "^/" pattern) (setq method 'text pattern (substring pattern 1))) ;; @word -> match keyword ((string-match "^@" pattern) (setq method 'keyword pattern (substring pattern 1))) ;; *word* -> match substring ((string-match "^\\*\\([^*?]*\\)\\*$" pattern) (setq method 'substring pattern (match-string 1 pattern))) ;; word* -> match prefixes ((string-match "^\\([^*?]*\\)\\*$" pattern) (setq method 'prefix pattern (match-string 1 pattern))) ;; *word -> match suffixes ((string-match "^\\*\\([^*?]*\\)$" pattern) (setq method 'suffix pattern (substring pattern 1))) ;; w*o?d -> "w.*o.d" ((string-match "[*?]" pattern) (setq pattern (if (string-match "^\\*" pattern) (substring pattern 1) (concat "^" pattern))) (setq pattern (if (string-match "\\*$" pattern) (substring pattern 0 (match-beginning 0)) (concat pattern "$"))) (let ((start 0)) (while (string-match "*" pattern start) (setq pattern (replace-match ".*" t t pattern) start (+ (match-end 0) 1)))) (while (string-match "?" pattern) (setq pattern (replace-match "." t t pattern))) (setq method 'regexp pattern pattern)) ;; default (t (setq method 'default))) (lookup-make-query method pattern))) (defun lookup-query-to-regexp (query) ;; QUERY を regexp パターンに変換する。 (let* ((method (lookup-query-method query)) (string (lookup-query-string query)) (quote (regexp-quote string))) (cond ((eq method 'keyword) (concat "\\<" quote "\\>")) ((eq method 'prefix) (concat "^" quote)) ((eq method 'suffix) (concat quote "$")) ((eq method 'exact) (concat "^" quote "$")) ((eq method 'substring) quote) ((eq method 'text) string) ((eq method 'regexp) string) (t (error "Illegal search method for regexp: %s" method))))) ;;;;;;;;;;;;;;;;;;;; ;: Search Module ;;;;;;;;;;;;;;;;;;;; ;; Description: ;; ;; 検索モジュールを表わすデータタイプ。各モジュールはこのタイプの ;; オブジェクトとして生成される。 ;; ;; 検索モジュールとは、一回の検索の単位となる辞書のまとまりのこと。 ;; 複数のモジュールを用意することで、対象の異なる複数の検索コマンドを ;; 定義することが出来る。下記の `Setup' の項を参照。 ;; Structure: ;; ;; [:module NAME DICTIONARIES PLIST] ;; ;; NAME は、モジュールの名称。 ;; DICTIONARIES は、モジュールに含まれる辞書のリスト。 ;; PLIST は、属性リストの保持に使われる。 ;; Properties: ;; ;; history - モジュールの履歴。 ;; agents - モジュールの辞書が属するエージェントのリスト。 ;; id-list - モジュールの定義に含まれる辞書ID のリスト。 ;; initialized - モジュールが初期化されていれば `t'。 (lookup-defstruct module (name dictionaries) :with-properties '(history)) ;; Initialize: ;; ;; モジュールの初期化は二つの段階で行なわれる。 ;; ;; 1) オブジェクトの生成 ;; モジュール・オブジェクトを生成する。これはデータを生成するだけで、 ;; 一切の初期化は行なわない。これを行なう関数は `lookup-new-module'。 ;; 2) エージェントの初期化 ;; 設定に従って検索エージェントを生成し、初期化を行なう。これは ;; 関数 `lookup-module-init' で行なわれる。 ;; ;; これらを分けるのは、ユーザの使い勝手をよくするためである。モジュールの ;; 生成は Lookup の起動後すぐに行なわれるが、M-x lookup-pattern などで ;; Lookup を起動したとき、検索語を入力する前にエージェントのセットアップ ;; まで行なわれたのでは入力まで時間が掛かり、ややストレスとなる。そこで ;; 初期化を後回しにすることで少しでも起動を早め、入力が行なわれてから ;; 初期化を始めるようにしている。 (defun lookup-new-module (spec) (let ((name (car spec)) (id-list (cdr spec)) module agents) ;; get agent list (lookup-foreach (lambda (id) ;; get the list of agents matched with ID (let ((match (concat "^" (regexp-quote id))) (list (lookup-agent-list)) (start agents)) (while list (if (string-match match (lookup-agent-id (car list))) (setq agents (cons (car list) agents))) (setq list (cdr list))) (if (eq start agents) (error "No match agent: %s" id)))) ;; get a list of agent-IDs (lookup-nunique (mapcar (lambda (id) (string-match "^[^:]*" id) (substring id 0 (match-end 0))) id-list))) (setq agents (nreverse (lookup-nunique agents 'eq))) ;; construct module (setq module (lookup-make-module name nil)) (lookup-module-put-property module 'agents agents) (lookup-module-put-property module 'id-list id-list) (lookup-module-init module))) (defun lookup-module-init (module) (lookup-module-set-history module (lookup-new-history)) module) (defun lookup-module-setup (module) (unless (lookup-module-get-property module 'setup) (let ((agents (lookup-module-get-property module 'agents)) dicts dictionary-list) ;; setup agents (lookup-foreach 'lookup-agent-setup agents) ;; get dictionary list (setq dictionary-list (apply 'append (mapcar 'lookup-agent-dictionaries agents))) (lookup-foreach (lambda (id) (let ((match (concat "^" (regexp-quote id))) (list dictionary-list) (start dicts)) (while list (if (string-match match (lookup-dictionary-id (car list))) (setq dicts (cons (car list) dicts))) (setq list (cdr list))) (if (eq start agents) (error "No match dictionary: %s" id)))) (lookup-module-get-property module 'id-list)) (lookup-module-set-dictionaries module (nreverse dicts)) (lookup-module-put-property module 'setup t)))) (defun lookup-module-clear (module) (lookup-module-set-dictionaries module nil) (lookup-module-set-history module nil) (lookup-module-put-property module 'setup nil)) ;;;;;;;;;;;;;;;;;;;; ;: Search Agent ;;;;;;;;;;;;;;;;;;;; ;; Description: ;; ;; 検索エージェントを表わすデータタイプ。各種エージェントはこのタイプの ;; オブジェクトとして生成される。 ;; Structure: ;; ;; [:agent CLASS LOCATION OPTIONS PLIST] ;; ;; CLASS はエージェントのクラス。LOCATION は辞書の所在。 ;; OPTIONS はオプションのリスト。変数 `lookup-search-agents' を参照。 ;; PLIST は、属性リストの保持に使われる。 ;; Properties: ;; ;; id - エージェントID。("CLASS+LOCATION") ;; title - エージェントのタイトル。 ;; dictionaries - エージェントに含まれる辞書を保持する。 ;; defaults - エージェントのデフォルトの設定を保持する。 (lookup-defstruct agent (class location options) :with-properties '(id title dictionaries defaults)) ;; Definitions: ;; ;; 各エージェントは同名の elisp ファイルによって定義され、require により ;; 読み込まれる。つまり各エージェントは provide されなければならない。 ;; ;; エージェントは検索実行のためにいくつかのコマンドを持つ。各コマンドは ;; 定義ファイルの中で (put AGENT COMMAND FUNCTION) のように定義される。 ;; 現在、次のコマンドが意味を持つ。(大文字のは引数) ;; ;; setup AGENT ;; エージェントのセットアップ・コマンド。これはエージェントの初期化を ;; 行ない、利用可能な辞書オブジェクトのリストを返す。 ;; clear AGENT ;; エージェントの終了コマンド。これはエージェントの後片付けを行ない、 ;; Lookup の終了に備える。 ;; ;; エージェントはいくつかの固有の標準設定を持つ。これは定義ファイルの中で ;; (put AGENT KEY VALUE) のように定義され、ユーザ・オプションにより上書き ;; 可能となっている。次の `Options' の項を参照。 (defun lookup-agent-require (agent) ;; エージェントの定義ファイルをロードする。 (require (lookup-agent-class agent))) (defun lookup-agent-ref (agent key) ;; エージェントの標準設定を参照する。 (get (lookup-agent-class agent) key)) (defun lookup-agent-command-p (agent command &rest args) ;; エージェント・コマンドをチェックする。 (lookup-agent-ref agent command)) (defun lookup-agent-command (agent command &rest args) ;; エージェント・コマンドを実行する。 (apply (lookup-agent-ref agent command) agent args)) ;; Options: ;; ;; エージェントはいくつかのオプションを持つことが出来る。オプションは ;; ユーザにより設定されるが、プログラムでそのデフォルト値を設定することも ;; 出来る。設定には次の種類がある。 ;; ;; 設定時オプション - エージェントを設定するときに指定されるオプション。 ;; 変数 `lookup-search-agents' を参照。 ;; 変数 `lookup-agent-options-alist', `lookup-default-agent-options' - ;; ユーザ指定のオプション設定。 ;; デフォルト - プログラムによる設定。`lookup-agent-set-default' を参照。 ;; 標準設定 - 定義ファイルに固定された設定。 (defun lookup-agent-set-default (agent key value) ;; エージェントのデフォルトの設定を行なう。これはユーザによるオプション ;; 指定がない場合に利用される。 (let ((defaults (plist-put (lookup-agent-defaults agent) key value))) (lookup-agent-set-defaults agent defaults))) (defun lookup-agent-option (agent key) ;; エージェントのオプションを参照する。設定時に指定されたオプション、 ;; 変数 `lookup-agent-options-alist'、`lookup-default-agent-options'、 ;; デフォルト、標準設定の順に探索する。 (or (plist-get (lookup-agent-options agent) key) (lookup-assq-ref (lookup-assoc-ref lookup-agent-options-alist (lookup-agent-id agent)) key) (lookup-assq-ref lookup-default-agent-options key) (plist-get (lookup-agent-defaults agent) key) (lookup-agent-ref agent key))) ;; Initialize: ;; ;; 検索エージェントの初期化は四つの段階で行なわれる。 ;; ;; 1) `lookup-new-agent' ;; 変数 `lookup-search-agents' で設定された内容を解析し、エージェント・ ;; オブジェクトを生成する。続いて `lookup-agent-init' を呼び初期化する。 ;; 2) `lookup-agent-init' ;; エージェントID やタイトルなどを設定する。辞書のセットアップはまだ。 ;; 3) `lookup-agent-setup' ;; エージェント毎の setup コマンドを実行し、辞書の初期化を行なう。 ;; 4) 各 setup 関数 ;; エージェント毎のセットアップを行ない、辞書のリストを返す。 (defun lookup-new-agent (spec) (let (class location options) (setq class (car spec) spec (cdr spec)) (if (stringp (car spec)) (setq location (car spec) spec (cdr spec))) (setq options spec) (lookup-agent-init (lookup-make-agent class location options)))) (defun lookup-agent-init (agent) ;; set the agent ID (let ((class (symbol-name (lookup-agent-class agent))) (location (or (plist-get (lookup-agent-options agent) ':alias) (lookup-agent-location agent)))) (lookup-agent-set-id agent (if location (concat class "+" location) class))) ;; set the agent title (lookup-agent-set-title agent (or (lookup-agent-option agent ':title) (lookup-agent-id agent))) agent) (defun lookup-agent-setup (agent) (unless (lookup-agent-get-property agent 'setup) (let ((lookup-proceeding-message (format "Setting up %s" (lookup-agent-id agent))) dicts enable disable select unselect name) (lookup-proceeding-message nil) ;; Setup agent by calling the setup function of each agent. (lookup-agent-require agent) (setq dicts (lookup-agent-command agent 'setup)) ;; Restrict dictionaries by the options `:enable' and `:disable'. (setq enable (lookup-agent-option agent ':enable) disable (lookup-agent-option agent ':disable)) (when (or enable disable) (setq dicts (lookup-grep (lambda (dict) (setq name (lookup-dictionary-name dict)) (and (or (not enable) (member name enable)) (not (member name disable)))) dicts))) ;; Rearrange the order of dictionaries by the option `:enable'. (when enable (setq dicts (sort dicts (lambda (d1 d2) (let ((name1 (lookup-dictionary-name d1)) (name2 (lookup-dictionary-name d2))) (> (length (member name1 enable)) (length (member name2 enable)))))))) ;; Select dictionaries by the options `:select' and `:unselect'. (setq select (lookup-agent-option agent ':select) unselect (lookup-agent-option agent ':unselect)) (lookup-foreach (lambda (dict) (setq name (lookup-dictionary-name dict)) (if (and (or (not select) (member name select)) (not (member name unselect))) (lookup-dictionary-set-selected dict t))) dicts) ;; Initialize dictionaries. (lookup-foreach 'lookup-dictionary-init dicts) (lookup-agent-set-dictionaries agent dicts) (lookup-proceeding-message t) (lookup-agent-put-property agent 'setup t)))) (defun lookup-agent-clear (agent) (when (lookup-agent-get-property agent 'setup) (let ((lookup-proceeding-message (concat "Clearing " (lookup-agent-id agent)))) (lookup-proceeding-message nil) (lookup-agent-command agent 'clear) (lookup-proceeding-message t)))) ;;;;;;;;;;;;;;;;;;;; ;: Dictionary ;;;;;;;;;;;;;;;;;;;; ;; Description: ;; ;; 辞書を表わすデータタイプ。全ての辞書はこのタイプのオブジェクトとして ;; 生成される。 ;; Structure: ;; ;; [:dictionary AGENT CODE NAME TITLE PLIST] ;; ;; AGENT - 辞書が属する検索エージェントのオブジェクト。 ;; CODE - 辞書を特定するための任意のオブジェクト。AGENT と CODE の組により ;; 辞書が一意に決定される。 ;; NAME - 辞書名。 ;; TITLE - 辞書のタイトル。 ;; PLIST - 属性リストの保持。 ;; Properties: ;; ;; id - 辞書ID。 ;; selected - 辞書が選択されている場合に non-nil。 ;; defaults - 辞書のデフォルトの設定を保持する。 ;; methods, gaiji-table, headings - オプションの保持。 (lookup-defstruct dictionary (agent code name title) :with-properties '(id methods headings gaiji-table defaults)) (defun lookup-new-dictionary (agent code name &optional title) (let ((dictionary (lookup-make-dictionary agent code name nil))) (if title (lookup-dictionary-set-default dictionary ':title title)) dictionary)) (defsubst lookup-dictionary-selected-p (dictionary) (lookup-dictionary-get-property dictionary 'selected)) (defsubst lookup-dictionary-set-selected (dictionary value) (lookup-dictionary-put-property dictionary 'selected value)) ;; Definitions: ;; ;; 辞書は検索のためのいくつかのコマンドを持つ。コマンドは、それが属する ;; 検索エージェントと同ファイルにて定義される。辞書コマンドはエージェント・ ;; コマンドと同様にして定義され、現在、次のものが意味を持つ。 ;; ;; search DICTIONARY QUERY ;; 辞書から QUERY を検索する。QUERY の解釈は辞書に一任される。 ;; 上記 `Query' の節を参照。検索の結果見付かった entry のリストを返す。 ;; content DICTIONARY ENTRY ;; ENTRY の内容を文字列として返す。特に整形は行なわずに、辞書から得られる ;; ままそのままを返せばよい。整形は別のプロセスによって行なわれる。関数 ;; `lookup-insert-content' を参照。 ;; open DICTIONARY ENTRY ;; ENTRY を特別な関数でオープンする。例えば ENTRY が URL の情報を保持して ;; いるなら、ブラウザでそれをオープンすることが考えられる。 ;; gaiji DICTIONARY CODE ;; 辞書が外字(通常の文字セットに含まれない文字)を持つ場合、その情報を返す。 ;; CODE は外字を特定するための文字列。返却値は、xbm 形式のビットマップを ;; 表わす文字列とする。 ;; ;; 辞書はいくつかの固有の標準設定を持つ。これはエージェントの標準設定と ;; 同様に定義され、ユーザ・オプションにより上書き可能となっている。次の ;; `Options' の項を参照。 (defun lookup-dictionary-command-p (dictionary command) ;; 辞書コマンドをチェックする。 (let ((agent (lookup-dictionary-agent dictionary))) (lookup-agent-ref agent command))) (defun lookup-dictionary-command (dictionary command &rest args) ;; 辞書コマンドを実行する。 (let ((agent (lookup-dictionary-agent dictionary))) (apply (lookup-agent-ref agent command) dictionary args))) ;; Options: ;; ;; 辞書はいくつかのオプションを持つことが出来る。オプションはユーザにより ;; 設定されるが、プログラムでそのデフォルト値を設定することも出来る。 ;; 設定には次の種類がある。 ;; ;; 変数 `lookup-dictionary-options-alist', `lookup-default-dictionary-options' ;; - ユーザ指定のオプション設定。 ;; デフォルト - プログラムによる設定。`lookup-dictionary-set-default' を参照。 ;; エージェント・オプション - 辞書が属するエージェントのオプションを継承する。 (defun lookup-dictionary-set-default (dictionary key value) ;; 辞書のデフォルトの設定を行なう。これはユーザによるオプション指定が ;; ない場合に利用される。 (let ((defaults (plist-put (lookup-dictionary-defaults dictionary) key value))) (lookup-dictionary-set-defaults dictionary defaults))) (defun lookup-dictionary-option (dictionary key &optional inherit) ;; 辞書のオプションを参照する。変数 `lookup-dictionary-options-alist'、 ;; `lookup-default-dictionary-options'、デフォルトの順に探索する。 ;; オプション引数 INHERIT が non-nil の場合、引き続いてエージェント・ ;; オプションからも探索する. (or (lookup-assq-ref (lookup-assoc-ref lookup-dictionary-options-alist (lookup-dictionary-id dictionary)) key) (lookup-assq-ref lookup-default-dictionary-options key) (plist-get (lookup-dictionary-defaults dictionary) key) (if inherit (lookup-agent-option (lookup-dictionary-agent dictionary) key)))) ;; Initialize: ;; ;; 辞書はエージェントの :setup コマンドにより生成され、エージェントと同時に ;; 初期化される。いくつかの代表的な属性がセットされる。 (defsubst lookup-dictionary-default-method (dictionary) (or (lookup-dictionary-option dictionary ':default-method t) lookup-default-method)) (defsubst lookup-dictionary-stemmer (dictionary) (lookup-dictionary-option dictionary ':stemmer t)) (defun lookup-dictionary-init (dictionary) (let ((id (concat (lookup-agent-id (lookup-dictionary-agent dictionary)) ":" (lookup-dictionary-name dictionary)))) (lookup-dictionary-set-id dictionary id)) (let ((title (or (lookup-dictionary-option dictionary ':title) (lookup-dictionary-name dictionary)))) (lookup-dictionary-set-title dictionary title)) (let ((methods (lookup-dictionary-option dictionary ':methods t))) (if (lookup-dictionary-stemmer dictionary) (setq methods (append methods '(stemming)))) (lookup-dictionary-set-methods dictionary methods)) (let ((gaiji-table (or (lookup-dictionary-option dictionary ':gaiji-table) (lookup-make-gaiji-table)))) (lookup-dictionary-set-gaiji-table dictionary gaiji-table)) ;; Keep some options as properties to save search step. (let ((headings (lookup-dictionary-option dictionary ':headings t))) (lookup-dictionary-set-headings dictionary headings)) dictionary) ;;;;;;;;;;;;;;;;;;;; ;: Entry ;;;;;;;;;;;;;;;;;;;; ;; Description: ;; ;; エントリの情報を表わすデータタイプ。全てのエントリはこのタイプの ;; オブジェクトとして生成される。 ;; Structure: ;; ;; [:entry DICTIONARY CODE HEADING PLIST] ;; ;; DICTIONARY は、エントリが属する辞書のオブジェクト。 ;; CODE は、エントリを特定するための任意のオブジェクト。DICTIONARY と CODE ;; の組によってエントリは一意に決定される。 ;; HEADING は、エントリの見出し語。これは表示の際に用いられる。 ;; PLIST は、属性リストの保持に使われる。 ;; Properties: ;; (lookup-defstruct entry (dictionary code heading) :with-properties '(prefix compound reference jump)) (defun lookup-new-entry (dictionary code heading) (let ((entry (lookup-make-entry dictionary code heading))) (lookup-arrange-heading entry))) (defsubst lookup-entry-refered-p (entry) (lookup-contents-cache-get entry lookup-enable-format)) ;; Functions: (defun lookup-entry-id (entry) (concat (lookup-dictionary-id (lookup-entry-dictionary entry)) "/" (lookup-entry-heading entry))) (defun lookup-copy-entry (entry) (lookup-make-entry (lookup-entry-dictionary entry) (lookup-entry-code entry) (lookup-entry-heading entry))) (defun lookup-entry-compare (e1 e2) (and (eq (lookup-entry-dictionary e1) (lookup-entry-dictionary e2)) (equal (lookup-entry-code e1) (lookup-entry-code e2)))) (defsubst lookup-unique-entries (entries) (lookup-nunique entries 'lookup-entry-compare)) ;;;;;;;;;;;;;;;;;;;; ;: Reference ;;;;;;;;;;;;;;;;;;;; (defun lookup-make-reference (dictionary code heading) (let ((entry (lookup-make-entry dictionary code heading))) (aset entry 0 ':reference) entry)) (defun lookup-reference-p (object) (and (vectorp object) (eq (aref object 0) ':reference))) (defsubst lookup-reference-dynamic-p (reference) (lookup-entry-get-property reference 'dynamic-search)) (defsubst lookup-reference-make-dynamic (reference function) (lookup-entry-put-property reference 'dynamic-search function)) (defun lookup-reference-refer (reference) (let ((function (lookup-entry-get-property reference 'dynamic-search))) (when function (lookup-reference-set-entries reference (funcall function reference)) (lookup-reference-make-dynamic reference nil) (lookup-entry-put-property reference 'refered t)))) (defsubst lookup-reference-refered-p (reference) (or (lookup-entry-get-property reference 'refered) (unless (lookup-entry-get-property reference 'dynamic-search) (let ((entries (lookup-reference-entries reference))) (catch 'result (while entries (unless (lookup-entry-refered-p reference) (throw 'result nil)) (setq entries (cdr entries))) t))))) (defsubst lookup-reference-entries (reference) (lookup-entry-get-property reference 'linked-entries)) (defsubst lookup-reference-set-entries (reference entries) (lookup-entry-put-property reference 'linked-entries entries)) ;; link: (defun lookup-set-link (start end reference) ;; バッファの START から END までのリージョンを REFERENCE へのリンクにする。 (add-text-properties start end (list 'face 'lookup-reference-face 'mouse-face 'highlight 'lookup-reference reference))) (defun lookup-get-link (position) ;; バッファの POSITION 位置からリンクされている entry を返す。 (get-text-property position 'lookup-reference)) (defun lookup-goto-next-link () ;; バッファの次のリンク位置にジャンプする。なければ nil を返す。 (let ((p (point))) (and (setq p (next-single-property-change p 'lookup-reference)) (or (get-text-property p 'lookup-reference) (setq p (next-single-property-change p 'lookup-reference))) (goto-char p)))) (defun lookup-goto-previous-link () ;; バッファの前のリンク位置にジャンプする。なければ nil を返す。 (let ((p (point))) (and (setq p (previous-single-property-change p 'lookup-reference)) (or (get-text-property p 'lookup-reference) (setq p (previous-single-property-change p 'lookup-reference))) (goto-char p)))) ;;;;;;;;;;;;;;;;;;;; ;: Session ;;;;;;;;;;;;;;;;;;;; ;; Description: ;; ;; 検索結果を保持するためのデータタイプ。一回の検索が行なわれる度にこの ;; タイプのデータが生成され、検索履歴の参照等のために用いられる。 ;; Structure: ;; ;; [:session QUERY ENTRIES PLIST] ;; ;; QUERY は、検索パターンのオブジェクト。`Search Query' を参照。 ;; ENTRIES は、検索の結果見付かったエントリのリスト。 ;; PLIST は、属性リストの保持に使われる。 ;; Properties: ;; ;; excursion - カーソル位置等の実行状態の保持。 (lookup-defstruct session (module type) :with-properties '(query entries excursion)) (defsubst lookup-session-ref (session key) (get (lookup-session-type session) key)) (defun lookup-session-display (session) (if lookup-last-session (lookup-session-save-excursion lookup-last-session)) (funcall (lookup-session-ref session 'display) session) (setq lookup-current-session session lookup-last-session session)) (defun lookup-session-save-excursion (session) (let ((func (lookup-session-ref session 'excursion))) (when func (lookup-session-set-excursion session (funcall func))))) ;;;;;;;;;;;;;;;;;;;; ;: History ;;;;;;;;;;;;;;;;;;;; ;; Description: ;; ;; 検索履歴を保持するためのデータタイプ。これは内部にオブジェクトのリスト ;; を持ち、それを追加・参照することが出来る。履歴はリストの位置(position) ;; も記憶しており、履歴へのオブジェクトの追加は、リストの最後に参照された ;; オブジェクトの直後へと行なわれる。(つまり、履歴を戻ったところでオブジェ ;; クトを追加すると、以前の繋がりは失なわれる) ;; Structure: ;; ;; (STACK . POSITION) ;; ;; STACK は、履歴を積んでいくオブジェクトのリスト。 ;; POSITION は、現在参照されている履歴の位置。 ;; ;; 新しいオブジェクトほど、STACK の先頭に置かれる。履歴の位置は 1 から ;; 始まる。ただし、履歴へのアクセスは下記の専用関数にて行ない、これらの ;; 変数は直接操作しない方がよい。 (defalias 'lookup-make-history 'cons) (defalias 'lookup-history-stack 'car) (defalias 'lookup-history-set-stack 'setcar) (defalias 'lookup-history-position 'cdr) (defalias 'lookup-history-set-position 'setcdr) (defsubst lookup-new-history () ;; 新しい history オブジェクトを生成して返す。 (lookup-make-history nil 0)) ;; Interface: ;; ;; 履歴へのオブジェクト追加は、関数 `lookup-history-push' により行なう。 ;; 追加されたオブジェクトは、関数 `lookup-history-ref' により参照すること ;; が出来る。関数 `lookup-history-length' により、履歴の長さを知ることが ;; 出来る。 (defun lookup-history-push (history object) ;; 現在の参照位置にオブジェクトを追加する。もしその位置に同じオブジェクト ;; が既に追加されていれば何もしない。 (let ((stack (lookup-history-stack history)) (position (lookup-history-position history))) (setq stack (nthcdr (- (length stack) position) stack)) (unless (eq object (car stack)) (lookup-history-set-stack history (cons object stack)) (lookup-history-set-position history (1+ position))))) (defun lookup-history-ref (history &optional n do-not-move) ;; 現在の参照位置から N 番目にあるオブジェクトを返す。 ;; 負の数が指定された場合には、履歴を過去に戻る。正の数だと、その逆。 ;; 省略された場合には現在の参照位置のものを返す。 ;; 履歴がない場合には `no-object'、最初の位置から前に戻ろうとしたら ;; `first'、最後から先に進もうとしたら `last' を返す。 ;; DO-NOT-MOVE に non-nil を指定した場合、参照後の移動を行なわない。 (let* ((stack (lookup-history-stack history)) (position (lookup-history-position history)) (length (length stack))) (setq n (or n 0)) (cond ((eq length 0) 'no-object) ((and (= position 1) (< n 0)) 'first) ((and (= position length) (> n 0)) 'last) (t (setq position (+ position n) position (if (< position 1) 1 (if (> position length) length position))) (unless do-not-move (lookup-history-set-position history position)) (car (nthcdr (- length position) stack)))))) (defun lookup-history-length (history) ;; 履歴の長さを返す。 (length (lookup-history-stack history))) (defun lookup-history-clear (history) ;; 履歴をクリアする。 (lookup-history-set-stack history nil) (lookup-history-set-position history 0)) ;;;;;;;;;;;;;;;;;;;; ;: Gaiji ;;;;;;;;;;;;;;;;;;;; ;; Description: ;; ;; 外字を扱うための関数群。これには次の三つのデータタイプがある。 ;; ;; 外字 - 外字情報を保持するためのオブジェクト ;; 外字グリフ - 外字そのものであるオブジェクト ;; 外字テーブル - 外字を記録・参照するためのオブジェクト ;; ;; 外字の扱いは、Emacs の種類や環境によって異なる。 (cond ((featurep 'xemacs) (defun lookup-glyph-compose (xbm) (let (width height data) (with-temp-buffer (insert xbm) (goto-char (point-min)) (if (re-search-forward "width[ \t]+\\([0-9]+\\)") (setq width (string-to-int (match-string 1)))) (if (re-search-forward "height[ \t]+\\([0-9]+\\)") (setq height (string-to-int (match-string 1)))) (while (re-search-forward "0x\\(..\\)" nil t) (setq data (cons (string-to-int (match-string 1) 16) data))) (setq data (concat (nreverse data)))) (make-glyph (vector 'xbm :data (list width height data))))) (defun lookup-glyph-paste (start end glyph) (set-extent-property (extent-at start nil 'lookup-gaiji) 'invisible t) (let (extent extents) (while (setq extent (extent-at start nil nil extent 'at)) (if (eq (extent-start-position extent) (extent-end-position extent)) (setq extents (cons extent extents)))) (while extents (set-extent-endpoints (car extents) end end) (setq extents (cdr extents))) (set-extent-begin-glyph (make-extent end end) glyph))) ) ((featurep 'mule) (defun lookup-bitmap-compose (xbm) (require 'bitmap) (with-temp-buffer (insert xbm) (let ((cmp (bitmap-decode-xbm (bitmap-read-xbm-buffer (current-buffer))))) (bitmap-compose (aref cmp 0))))) (defun lookup-bitmap-paste (start end glyph) (let ((overlay (make-overlay start end))) (overlay-put overlay 'invisible t) (overlay-put overlay 'evaporate t) (overlay-put overlay 'after-string glyph))) )) ;; Gaiji: ;; ;; 外字オブジェクトは次の構造を持つ。 ;; ;; (GLYPH . ALTERNATE) ;; ;; GLYPH は、下記の外字グリフ。 ;; ALTERNATE は、外字に対応する代替文字列。 ;; ;; GLYPH は、外字の表示の際に用いられる。しかし外字は、読むのには適しているが ;; 検索やコピーには向いていない。そのため、それらを行なう際には ALTERNATE が ;; 代わりに利用される。 (defalias 'lookup-make-gaiji 'cons) (defalias 'lookup-gaiji-glyph 'car) (defalias 'lookup-gaiji-set-glyph 'setcar) (defalias 'lookup-gaiji-alternate 'cdr) (defalias 'lookup-gaiji-set-alternate 'setcdr) (defun lookup-gaiji-insert (gaiji) (let ((glyph (lookup-gaiji-glyph gaiji)) (alter (lookup-gaiji-alternate gaiji)) (start (point))) (insert alter) (if (not (eq glyph alter)) (put-text-property start (point) 'lookup-gaiji glyph)))) ;; Gaiji Glyph: ;; ;; 外字グリフは、外字の外字たるゆえん、任意のビットマップを示すオブジェクト。 ;; これは GNU Emacs と XEmacs とで扱いが異なる。 ;; ;; 1) GNU Emacs / Mule の場合 ;; ;; 外字は bitmap-mule パッケージを利用して生成される。内部的にはこれは、 ;; `compose-string' により生成される文字列である。 ;; ;; 2) XEmacs の場合 ;; ;; 組み込みの glyph の機能を利用して生成する。つまり、外字を一種の画像 ;; データとして扱う。 ;; ;; 外字を生成する関数は、変数 `lookup-gaiji-compose-function' により指定 ;; される。これは bitmap-mule を利用する場合には `lookup-bitmap-compose' ;; となり、XEmacs の場合には `lookup-glyph-compose' となる。これらの関数は ;; "lookup-gaiji.el" で定義している。 ;; ;; また、同様に外字を挿入する関数は、変数 `lookup-gaiji-insert-function' ;; により与えられる。これはそれぞれ、`insert' 及び `lookup-glyph-insert' ;; となる。 (defun lookup-gaiji-glyph-possible-p () ;; 外字グリフを表示可能な状態にあるかどうかチェックする。 (and window-system lookup-gaiji-compose-function)) (defun lookup-gaiji-glyph-compose (xbm) ;; 外字グリフを生成する。データは XBM 形式の文字列として与える。 (funcall lookup-gaiji-compose-function xbm)) (defun lookup-gaiji-glyph-paste (start end glyph) ;; 外字グリフを貼り付ける。 (funcall lookup-gaiji-paste-function start end glyph)) ;; Gaiji Table: ;; ;; 外字テーブルは、外字オブジェクトを記録・参照するためのオブジェクト。 ;; 外字オブジェクトは数が多い上に頻繁に参照されるため、ハッシュ(obarray)を ;; 用いて高速に参照出来るようにしている。 (defsubst lookup-make-gaiji-table () ;; 外字テーブルを生成する。 (make-vector 377 0)) (defun lookup-gaiji-table-set (table code gaiji) ;; 外字テーブルに外字オブジェクトをセットする。 (set (intern code table) gaiji)) (defun lookup-gaiji-table-ref (table code) ;; 外字テーブルから外字オブジェクトを参照する。 (let ((symbol (intern code table))) (if (boundp symbol) (symbol-value symbol)))) ;; Table Format: ;; ;; 関数 `lookup-new-gaiji-table' により、外字テーブルの初期値を定義することが ;; 出来る。これにより、外字を特定の文字列で置き換えるようなことが可能となる。 ;; これは次のようにして行なう。 ;; ;; (lookup-new-gaiji-table ;; '((CODE1 GLYPH1 [ALTERNATE1]) (CODE2 GLYPH2 [ALTERNATE2]) ...)) ;; ;; CODE は辞書における外字コード。GLYPH は外字グリフオブジェクトか任意の ;; 文字列、あるいはそのどちらかを返す評価可能な式。ALTERNATE は省略可能で、 ;; 外字の代替文字列を指定する。 ;; ;; 同じ文字列を設定する場合にも、GLYPH と ALTERNATE とでは意味が異なる。 ;; GLYPH は画面出力に用いられるため、アクセント文字などを含む任意の文字 ;; 列を指定出来るが、ALTERNATE は検索等に用いられるため、一般的な ASCII ;; コードなどを指定した方がよい。 (defun lookup-new-gaiji-table (spec) ;; SPEC の指定に従って外字テーブルを作成する。 (let ((table (lookup-make-gaiji-table)) form glyph alter) (while spec (setq form (car spec) glyph (cadr form) alter (or (nth 2 form) glyph)) (if (and (featurep 'xemacs) glyph) (setq glyph (make-glyph (vector 'string ':data glyph)))) (lookup-gaiji-table-set table (car form) (lookup-make-gaiji glyph alter)) (setq spec (cdr spec))) table)) (provide 'lookup-types) ;;; lookup-types.el ends here