;;; hiki-mode.el -- Major mode for Hiki editing -*- coding: euc-jp -*- ;; Copyright (C) 2003 Hideaki Hori ;; Author: Hideaki Hori ;; $Id: hiki-mode.el,v 1.10 2005-08-24 06:43:04 fdiary Exp $ ;; ;; This program 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, or (at ;; your option) any later version. ;; This program 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 this program; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, ;; Boston, MA 02111-1307, USA. ;;; Commentary: ;; usage: ;; ;; Put the following in ~/.emacs or ~/.hiki ;; ;; (setq hiki-site-list '(("my 1st Hiki" "http://example.com/hiki/hiki.cgi") ;; ("my 2nd Hiki" "http://example.com/hiki2/"))) ;; (setq hiki-browser-function 'browse-url) ;; (autoload 'hiki-edit "hiki-mode" nil t) ;; (autoload 'hiki-edit-url "hiki-mode" nil t) ;; ;;; Variable: (require 'pces) (defvar hiki-http-proxy-server nil "Proxy server for HTTP.") (defvar hiki-http-proxy-port nil "Proxy port for HTTP.") (defvar hiki-http-timeout 10 "Timeout for HTTP.") (defvar hiki-http-cookie nil) (defvar hiki-freeze nil) (if (or (featurep 'xemacs) (not (boundp 'emacs-major-version)) (< emacs-major-version 21)) (progn (require 'poe) (require 'poem))) (require 'derived) (defconst hiki-mode-version (let ((revision "$Revision: 1.10 $")) (string-match "\\([0-9.]+\\)" revision) (match-string 1 revision))) (defvar hiki-site-list nil "List of Hiki list. Each element looks like (NAME URL STYLE). STYLE is optional.") (defvar hiki-list nil "`hiki-list' is OBSOLETE; use hiki-site-list.") (defvar hiki-site-info nil) (defvar hiki-index-page-info-list nil) (defvar hiki-index-sort-key nil) (defvar hiki-coding-system 'euc-japan-dos) (defvar hiki-pagename nil) (defvar hiki-pagetitle nil) (defvar hiki-md5hex nil) (defvar hiki-session-id nil) (defvar hiki-update-timestamp nil) (defvar hiki-edit-newpage nil) (defvar hiki-password-alist nil) (defvar hiki-browser-function nil "Function to call browser. If non-nil, `hiki-edit-save-page' calls this function. The function is expected to accept only one argument(URL).") (defvar hiki-init-file "~/.hiki" "Init file for hiki-mode.") (defvar hiki-non-wikiname-regexp-string "[^A-Za-z0-9]") (defvar hiki-wikiname-regexp-string "\\([A-Z][a-z0-9]+\\([A-Z][a-z0-9]+\\)+\\)") (defvar hiki-wikiname-regexp-list (list (cons (concat hiki-non-wikiname-regexp-string hiki-wikiname-regexp-string hiki-non-wikiname-regexp-string) 1) (cons (concat "^" hiki-wikiname-regexp-string hiki-non-wikiname-regexp-string) 1) (cons (concat hiki-non-wikiname-regexp-string hiki-wikiname-regexp-string "$") 1) (cons (concat "^" hiki-wikiname-regexp-string "$") 1))) (defvar hiki-bracket-name-regexp '("\\[\\[\\([^]:|]+\\)\\]\\]" . 1) ) (defvar hiki-rd+-bracket-name-regexp '("((<\\([^>:|]+\\)>))" . 1)) (defvar hiki-style-anchor-regexp-alist (list (cons 'default (cons hiki-bracket-name-regexp hiki-wikiname-regexp-list)) (cons 'rd+ (list hiki-rd+-bracket-name-regexp))) "Alist of regexp for anchor.") (defvar hiki-anchor-regexp-alist (cdr (assoc 'default hiki-style-anchor-regexp-alist))) (defvar hiki-anchor-face (copy-face 'underline 'hiki-anchor-face) "Face for Hiki anchor." ) (defvar hiki-site-name-history nil "History of Hiki site name." ) (defvar hiki-pagename-history nil "History of Hiki page name." ) (defvar hiki-diff-buffer-name "*Hiki diff*") (defvar hiki-page-buffer-alist nil) (defvar hiki-init nil) ;;; Code: (defun hiki-mode-version () (interactive) (message (format "hiki-mode (Revision: %s)" hiki-mode-version))) (defun hiki-initialize () (unless hiki-init (hiki-load-init-file) (setq hiki-init t))) ;;; 編集モード (hiki-edit-*) (define-derived-mode hiki-edit-mode text-mode "Hiki Edit" "Major mode for Hiki editing. \\{hiki-edit-mode-map}" (make-local-variable 'require-final-newline) (make-local-variable 'hiki-site-info) (make-local-variable 'hiki-newpage) (make-local-variable 'hiki-pagename) (make-local-variable 'hiki-md5hex) (make-local-variable 'hiki-session-id) (make-local-variable 'hiki-update-timestamp) (setq require-final-newline t indent-tabs-mode nil) (hiki-edit-setup-keys) (set-buffer-file-coding-system hiki-coding-system) (setq hiki-anchor-regexp-alist (cdr (assoc (hiki-site-style hiki-site-info) hiki-style-anchor-regexp-alist))) (when (and (featurep 'font-lock) (fboundp 'font-lock-add-keywords)) (let ((case-fold-search nil)) ;;(font-lock-set-defaults) (font-lock-add-keywords 'hiki-edit-mode (mapcar (lambda (cell) (list (car cell) (cdr cell) 'hiki-anchor-face t)) hiki-anchor-regexp-alist))) (put 'hiki-edit-mode 'font-lock-defaults '(text-font-lock-keywords nil t)) (turn-on-font-lock)) (run-hooks 'hiki-edit-mode-hook)) (defun hiki-edit-setup-keys () "Set up keymap for hiki-edit-mode. If you want to set up your own key bindings, use `hiki-edit-mode-hook'." (define-key hiki-edit-mode-map "\C-c\C-i" 'hiki-edit-next-anchor) (define-key hiki-edit-mode-map "\C-c\C-r" 'hiki-edit-reload) (define-key hiki-edit-mode-map "\C-c\C-e" 'hiki-edit) (define-key hiki-edit-mode-map "\C-c\C-c" 'hiki-edit-save-page) (define-key hiki-edit-mode-map "\C-c\C-q" 'hiki-edit-quit) ) (defun hiki-load-init-file () "Load init file." (when hiki-init-file (let ((init-file (expand-file-name hiki-init-file))) (when (file-readable-p init-file) (load init-file t t)) (hiki-obsolete-check)))) (defun hiki-obsolete-check () (when hiki-list (message "hiki-list is OBSOLETE. Use hiki-site-list.") (sit-for 5) (setq hiki-site-list hiki-list))) (defun hiki-read-site-name (&optional string) "サイト名をミニバッファから読み、サイト情報のリストを返す。 STRING が non-nil なら、それをサイト名とする。" (let* ((selected (car hiki-site-list)) (default (or (hiki-site-name) (car selected)))) (assoc (or (completing-read (format "Select SITE (%s): " default) hiki-site-list nil t nil 'hiki-site-name-history default) default) hiki-site-list))) (defun hiki-password-read (sitename pagename) (cdr (assoc (cons sitename pagename) hiki-password-alist))) (defun hiki-password-store (sitename pagename password) (let (key unit) (setq key (cons sitename pagename) unit (assoc key hiki-password-alist)) (if unit (if password (setcdr unit password) (setq hiki-password-alist (delete unit hiki-password-alist))) (if password (setq hiki-password-alist (cons (cons (cons sitename pagename) password) hiki-password-alist)))))) (defun hiki-read-pagename (arg site-name) (completing-read (format "Page name for %s: " site-name) (cdr (assoc site-name hiki-pagename-history)) nil nil arg nil arg)) ;;; navi2ch-read-char を参考にしてます。 (defun hiki-read-char (prompt) "PROMPT (non-nil の場合) を表示して `read-char' を呼び出す。" (let ((cursor-in-echo-area t) c) (if prompt (message "%s" prompt)) (setq c (read-char)) (if prompt (message "%s%c" prompt c)) c)) ;;; navi2ch-read-char-with-retry を参考にしてます。 (defun hiki-read-char-with-retry (prompt retry-prompt list) (let ((retry t) c) (while retry (setq c (hiki-read-char prompt)) (cond ((memq c list) (setq retry nil)) ((eq c 12) (recenter)) (t (ding) (setq prompt (or retry-prompt prompt))))) c)) (defun hiki-http-request (mode cmd pagename site-url &optional post-data) (let* ((url (if (eq mode 'get) (concat (format "%s?c=%s" site-url cmd) (if pagename (format ";p=%s" (hiki-http-url-hexify-string pagename hiki-coding-system)))) site-url)) (buf (hiki-http-fetch url mode nil nil (hiki-http-url-hexify-alist post-data hiki-coding-system)))) (if (bufferp buf) (save-excursion (set-buffer buf) (decode-coding-region (point-min) (point-max) hiki-coding-system) (goto-char (point-min)) buf) (error (format "hiki get: %s - %s" (car buf) (cdr buf)))))) (defun hiki-current-anchor-string () "Return anchor string at current point." (let (str result pos (point (point))) (save-excursion (beginning-of-line) (setq pos (point)) (while (and (setq result (hiki-search-anchor pos)) (<= (cdr result) point)) (setq pos (cdr result))) (when (and result (<= (car result) point)) (setq str (buffer-substring-no-properties (car result) (cdr result))))) str)) (defun hiki-edit-next-anchor (&optional prev) "次のアンカーへ移動する。 PREV が non-nil ならば、前のアンカーへ移動する。" (interactive "P") (goto-char (or (car (hiki-search-anchor (point) prev)) (point)))) (defun hiki-search-anchor (point &optional prev) "POINT から最も近いアンカーを探す。 見つかったら (beginning . end) を、見つからなかったら nil を 返す" (let ((case-fold-search nil) (alist hiki-anchor-regexp-alist) result) (save-excursion (while alist (goto-char point) (if (if prev (re-search-backward (car (car alist)) nil t nil) (re-search-forward (car (car alist)) nil t nil)) (when (or (null result) (> (car result) (match-beginning (cdr (car alist))))) (setq result (cons (match-beginning (cdr (car alist))) (match-end (cdr (car alist))))))) (setq alist (cdr alist)))) result)) (defun hiki-edit-rename-buffer (sitename pagename pagetitle frozenp) (let ((name (format "[%s%s] %s%s" sitename (if (string= pagename pagetitle) "" (concat ":" pagename)) pagetitle (if frozenp " (frozen)" "")))) (or (string= name (buffer-name)) (rename-buffer name t)))) (defun hiki-edit-url (str &optional url-encoded) "URL を指定して編集する。" (interactive "sURL: ") (let (url pagename site-info) (or (string-match "^\\(http://[^?]+\\)\\?\\(.+\\)$" str) (error "Illegal URL. (%s)" str)) (setq url (match-string 1 str)) (setq pagename (match-string 2 str)) (when (string-match "=" pagename) (if (string-match "\\(^\\|[?&;]\\)p=\\(.+\\)" pagename) (setq pagename (match-string 2 pagename)) (error "Illegal URL. (%s)" str))) (setq site-info (list url url)) (hiki-edit-page (hiki-http-url-unhexify-string pagename hiki-coding-system) site-info))) (defun hiki-edit-quit () (interactive) (let ((site-info hiki-site-info) (pagename hiki-pagename) win cancelled) (setq buffer-read-only t) (when (buffer-modified-p) (if (y-or-n-p "Buffer is modified. Really quit?") (progn (kill-buffer (current-buffer)) (setq hiki-page-buffer-alist (remassoc (list (hiki-site-name site-info) pagename) hiki-page-buffer-alist)) (delete-other-windows)) (setq cancelled t))) (when (not cancelled) (cond ((setq win (get-buffer-window (hiki-index-buffer-name site-info))) (select-window win)) (t (delete-other-windows))) (hiki-index site-info t pagename)))) (defun hiki-edit-reload () "現在編集中のページをリロードする。" (interactive) (let ((selected-pagename hiki-pagename)) (hiki-edit))) (defun hiki-edit (&optional select-site) "ページ名を指定して編集する。 SELECT-SITE が non-nil の時は、SITE名も指定する。" (interactive "P") (hiki-initialize) (let ((point (point)) (start (window-start)) site-info pagename (same-site t) same-page) ;; site-name input (if required) (cond ((and (hiki-site-name) (not select-site)) (setq site-info hiki-site-info)) (t (setq site-info (hiki-read-site-name)) (when (not (string= (hiki-site-name site-info) (hiki-site-name))) (setq same-site nil)))) ;; pagename input (setq pagename (if (boundp 'selected-pagename) selected-pagename (hiki-read-pagename (or (hiki-current-anchor-string) hiki-pagename "FrontPage") (hiki-site-name site-info)))) (if (string= pagename hiki-pagename) (setq same-page t)) ;; edit (hiki-edit-page pagename site-info) ;; restore point (if required) (when (and same-site same-page) (set-window-start (selected-window) start) (goto-char point)))) ;;; 一覧モード(hiki-index-*) (define-derived-mode hiki-index-mode text-mode "Hiki Index" "Major mode for Hiki index. \\{hiki-index-mode-map}" (make-local-variable 'hiki-site-info) (make-local-variable 'hiki-index-page-info-list) (make-local-variable 'hiki-index-sort-key) (hiki-index-setup-keys) (run-hooks 'hiki-index-mode-hook)) (defun hiki-index-setup-keys () "Set up keymap for hiki-index-mode. If you want to set up your own key bindings, use `hiki-index-mode-hook'." (define-key hiki-index-mode-map "\r" 'hiki-index-edit-page-current-line) (define-key hiki-index-mode-map "e" 'hiki-index-edit-page) (define-key hiki-index-mode-map "." 'hiki-index-display-page) (define-key hiki-index-mode-map " " 'hiki-index-display-page-next) (define-key hiki-index-mode-map "S" 'hiki-index-sort) (define-key hiki-index-mode-map "R" 'hiki-index-refetch-index) (define-key hiki-index-mode-map "q" 'hiki-index-suspend) (define-key hiki-index-mode-map "Q" 'hiki-index-quit) (define-key hiki-index-mode-map "I" 'hiki-index-login) (define-key hiki-index-mode-map "O" 'hiki-index-logout) ) (defun hiki-index (&optional site-info refetch pagename) "一覧モードに入る。 SITE-INFO が指定されていなければ、ミニバッファから読み込む。 REFETCH が nil ですでにバッファが存在するなら、HTTP GET しない。" (interactive "P") (hiki-initialize) (let (buf) ;; site-name input (if required) (when (null site-info) (setq site-info (hiki-read-site-name))) (setq buf (hiki-display-index site-info refetch pagename)) (switch-to-buffer buf) (unless pagename (delete-other-windows)))) (defun hiki-display-index (site-info &optional refetch pagename) "一覧を表示し、バッファを返す。 REFETCH が nil で既にバッファが存在するなら、HTTP GET しない。 PAGENAME に対応した行があれば、カーソルをそこに移動する。 " (let ((old-buf (current-buffer)) (buf (hiki-index-get-buffer-create site-info))) (switch-to-buffer buf) (setq buffer-read-only nil) (erase-buffer) (when (or refetch (null hiki-index-page-info-list)) (message "Loading...") (setq hiki-index-page-info-list (hiki-fetch-index site-info)) (message "Loading... done.")) (mapcar (lambda (page-info) (insert (hiki-index-page-info-string page-info site-info))) hiki-index-page-info-list) (set-buffer-modified-p nil) (hiki-index-sort-by) (setq buffer-read-only t) (goto-char (point-min)) (when pagename (dolist (elm hiki-index-page-info-list) (when (string= (nth 1 elm) pagename) (re-search-forward (format "^%4d" (nth 0 elm))) (beginning-of-line) (recenter)))) (switch-to-buffer old-buf) buf)) (defun hiki-index-get-buffer-create (site-info) "一覧表示用のバッファを返す。" (let ((buf-name (hiki-index-buffer-name site-info))) (or (get-buffer buf-name) (progn (save-excursion (get-buffer-create buf-name) (set-buffer buf-name) (hiki-index-mode) (setq hiki-site-info site-info) (get-buffer buf-name)))))) (defun hiki-index-page-info-string (page-info site-info) (let ((num (nth 0 page-info)) (name (nth 1 page-info)) (title (nth 2 page-info)) (extra (nth 3 page-info))) (format "%4d %s %s %s\n" num (if (hiki-page-buffer-name name site-info) "V" " ") (hiki-prefix (concat title (if (string= title name) "" (format " <%s>" name))) 35) extra))) (defun hiki-index-display-page (&optional refetch) "現在行のページを表示する。 REFETCH が nil ですでにバッファが存在するなら、HTTP GET しない。" (interactive) (let ((point (point)) (page-info (hiki-index-page-info-current-line)) pagename) (when page-info (setq pagename (nth 1 page-info)) (delete-other-windows) (split-window nil 10) (recenter t) (other-window 1) (hiki-display-page pagename hiki-site-info refetch) (setq buffer-read-only t) (other-window 1)) (hiki-index hiki-site-info nil pagename) (goto-char point))) (defun hiki-index-display-page-next (&optional refetch) "現在行のページを表示する。すでに表示されている時はスクロールする。 REFETCH が nil ですでにバッファが存在するなら、HTTP GET しない。" (interactive) (let ((page-info (hiki-index-page-info-current-line)) (old-win (selected-window)) pagename buf win) (when page-info (setq pagename (nth 1 page-info)) (setq buf (cdr (assoc (list (hiki-site-name hiki-site-info) pagename) hiki-page-buffer-alist))) (if (or (null buf) (null (setq win (get-buffer-window buf)))) (hiki-index-display-page refetch) (let ((other-window-scroll-buffer buf) (start (window-start win))) (scroll-other-window) ;;; スクロール出来ない時は次行に移る。 ;;; (when (= (window-start win) start) ;;; (forward-line) ;;; (hiki-index-display-page refetch)) ))))) (defun hiki-index-edit-page-current-line () "現在行のページを編集する。" (interactive) (hiki-index-edit-page (nth 1 (hiki-index-page-info-current-line)))) (defun hiki-index-edit-page (&optional pagename) "ページを編集する。" (interactive) (let ((index-buf (current-buffer)) edit-buf start) (unless pagename (setq pagename (hiki-read-pagename (nth 1 (hiki-index-page-info-current-line)) (hiki-site-name)))) (when (and pagename (setq edit-buf (hiki-edit-page pagename hiki-site-info))) (switch-to-buffer index-buf) (delete-other-windows) (split-window nil 10) (setq start (window-start)) (recenter) (hiki-display-index hiki-site-info nil pagename) (set-window-start (selected-window) start) (other-window 1) (switch-to-buffer edit-buf)))) (defun hiki-index-page-info-current-line () "現在行のページ情報(list)を返す。" (let (num) (save-excursion (beginning-of-line) (re-search-forward "\\([0-9]+\\)" nil t nil) (setq num (match-string 1))) (cond (num (nth (1- (string-to-number num)) hiki-index-page-info-list)) (t nil)))) (defun hiki-index-page-info (pagename) "PAGENAME の page-info を返す。知らなければ nil。" (let (result) (dolist (page-info hiki-index-page-info-list) (when (string= (nth 1 page-info) pagename) (setq result page-info))) result)) (defun hiki-index-refetch-index () "一覧の再読み込みを行う。" (interactive) (hiki-index hiki-site-info t (nth 1 (hiki-index-page-info-current-line)))) (defun hiki-index-sort (&optional rev) "一覧のソートを行う" (interactive "P") (message "Sorting...") (hiki-index-sort-by (hiki-read-char-with-retry "Sort by n)umber or d)ate? " nil '(?n ?d)) rev) (message "Sorting... done.")) (defun hiki-index-suspend () "hiki-index を一時中断する。" (interactive) (delete-other-windows) (dolist (elm hiki-page-buffer-alist) (bury-buffer (cdr elm))) (replace-buffer-in-windows (current-buffer))) (defun hiki-index-quit () "hiki-index を終了する。" (interactive) (let ((tmp hiki-page-buffer-alist)) (delete-other-windows) (dolist (elm hiki-page-buffer-alist) (when (string= (nth 0 (car elm)) (hiki-site-name)) (kill-buffer (cdr elm)) (setq tmp (remassoc (car elm) tmp)))) (setq hiki-page-buffer-alist tmp) (kill-buffer (current-buffer)))) (defun hiki-index-login () (interactive) (let (username password post-data buf) (sit-for 0.1) (setq username (read-from-minibuffer (format "Username for %s: " (car hiki-site-info)))) (setq password (read-passwd (format "Password for %s: " (car hiki-site-info)))) (add-to-list 'post-data (cons "c" "login")) (add-to-list 'post-data (cons "name" username)) (add-to-list 'post-data (cons "password" password)) (setq buf (hiki-http-request 'post nil nil (hiki-site-url) post-data)) (set-buffer buf) (goto-char (point-min)) (if (re-search-forward "HTTP/1.[01] \\([0-9][0-9][0-9]\\) \\(.*\\)" nil t) (let ((code (match-string 1)) (desc (match-string 2))) (cond ((equal code "302") (message "Logged in.")) (t (message "Username and/or Password is wrong!!."))))))) (defun hiki-index-logout () (interactive) (let (post-data buf) (add-to-list 'post-data (cons "c" "logout")) (setq buf (hiki-http-request 'post nil nil (hiki-site-url) post-data)) (message "Logged out."))) ;;; func (defun hiki-display-page (pagename site-info &optional refetch) "ページのを表示する REFETCH が nil ですでにバッファが存在するなら、HTTP GET しない。" (let ((not-cancelled t) result body keyword pagetitle password history point buf new-page) (setq buf (cdr (assoc (list (hiki-site-name site-info) pagename) hiki-page-buffer-alist))) (if (and buf (buffer-name buf) (not refetch)) (progn (switch-to-buffer buf) (goto-char (point-min))) (and buf (kill-buffer buf)) (message "Loading...") (setq result (hiki-fetch-source pagename (hiki-site-url site-info))) (setq body (cdr (assoc 'body result))) (setq pagetitle (cdr (assoc 'pagetitle result))) (setq password (cdr (assq 'password result))) (setq keyword (cdr (assoc 'keyword result))) (when (and body (or (> (length body) 0) (progn (setq not-cancelled (y-or-n-p (format "Page %s is not exist. Create new page?" pagename))) (if not-cancelled (setq new-page t) (setq result nil)) not-cancelled))) (setq buf (generate-new-buffer "*hiki tmp*")) (switch-to-buffer buf) (hiki-edit-rename-buffer (hiki-site-name site-info) pagename pagetitle password) (save-excursion (when keyword (insert (hiki-propertize "Keywords:" 'read-only t 'front-sticky t 'rear-nonsticky t 'hiki-special t)) (insert (hiki-propertize "\n" 'hiki-special t)) (insert (hiki-propertize (format "%s\n" keyword) 'hiki-special t)) (insert (hiki-propertize "----\n" 'read-only t 'front-sticky t 'rear-nonsticky t 'hiki-special t))) (setq point (point)) (insert body) (goto-char (point-min)) (hiki-replace-entity-refs) (hiki-edit-mode) (setq hiki-edit-new-page new-page) (set-buffer-modified-p nil) (add-to-list 'hiki-page-buffer-alist (cons (list (hiki-site-name site-info) pagename) (current-buffer))) (message "Loading... done.")) (goto-char point)) result))) (defun hiki-edit-page (pagename site-info) "PAGENAME の編集モードに入る。バッファを返す。" (let ((result (hiki-display-page pagename site-info t))) (when result (setq hiki-md5hex (cdr (assq 'md5hex result))) (setq hiki-session-id (cdr (assq 'session-id result))) (setq hiki-update-timestamp (cdr (assq 'update-timestamp result))) (setq hiki-pagename pagename) (setq hiki-pagetitle (or (cdr (assq 'pagetitle result)) pagename)) (setq hiki-site-info site-info) (or (setq history (assoc (hiki-site-name) hiki-pagename-history)) (setq hiki-pagename-history (cons (setq history (cons (hiki-site-name) nil)) hiki-pagename-history))) (or (member hiki-pagename (cdr history)) (setcdr history (cons (cons hiki-pagename nil) (cdr history)))) (set-buffer-modified-p nil) (current-buffer)))) (defun hiki-fetch-index (site-info) "ページ一覧を取得する。" (let (indexes history (i 1) (buf (hiki-http-request 'get "index" nil (hiki-site-url site-info)))) (when (bufferp buf) (save-excursion (set-buffer buf) (re-search-forward "