App-Chart

 view release on metacpan or  search on metacpan

emacs/chartprog.el  view on Meta::CPAN

;;; chartprog.el --- stock quotes using Chart.

;; Copyright 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012, 2013, 2014, 2015, 2016, 2017, 2018 Kevin Ryde

;; Author: Kevin Ryde <user42_kevin@yahoo.com.au>
;; Keywords: comm, finance
;; URL: http://user42.tuxfamily.org/chart/index.html

;; This file is part of Chart.
;;
;; Chart 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 3, or (at your option) any later
;; version.
;;
;; Chart 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 Chart.  If not, see <http://www.gnu.org/licenses/>.


;;; Commentary:
;;
;; See section "Emacs" in the Chart manual for usage.


;;; Code:

(require 'timer) ;; for xemacs21

(eval-when-compile ;; for macros in emacs20
  (unless (and (fboundp 'declare)
               (fboundp 'dolist)
               (fboundp 'push))
    (require 'cl)))

(defvar bookmark-make-record-function)  ;; in bookmark.el


;;-----------------------------------------------------------------------------
;; customizations

;;;###autoload
(defgroup chartprog nil
  "Chart program interface."
  :prefix "chartprog-"
  :group 'applications
  :link '(custom-manual "(chart)Emacs"))

(defface chartprog-up
  `(;; plain "green" is too light to see against a white background
    (((class color) (background light))
     (:foreground "green4"))
    (((class color))
     (:foreground "green")))
  "Face for a Chart quote which is up."
  :group 'chartprog)

(defface chartprog-down
  `((((class color))
     (:foreground "red")))
  "Face for a Chart quote which is down."
  :group 'chartprog)

(defface chartprog-in-progress
  `((((class color) (background dark))
     (:foreground "cyan"))
    (((class color) (background light))
     (:foreground "blue")))
  "Face for Chart quote fetch in progress."
  :group 'chartprog)

(defcustom chartprog-watchlist-hook nil
  "*Hook called by `chart-watchlist'."
  :type  'hook
  :group 'chartprog)

;;-----------------------------------------------------------------------------

(defvar chartprog-debug nil)

(defun chartprog-debug-message (&rest args)
  (when chartprog-debug
    (let ((buffer (get-buffer-create "*chartprog-debug*")))
      (save-selected-window
        (let ((window (get-buffer-window buffer)))
          (if window (select-window window)))

        (with-current-buffer buffer

emacs/chartprog.el  view on Meta::CPAN

              (window-rows (mapcar (lambda (window)
                                     (cons window
                                           (count-lines (point-min)
                                                        (window-start window))))
                                   (get-buffer-window-list (current-buffer)))))
         (prog1 (progn ,@body)
           (dolist (pair window-rows)
             (goto-char (point-min))
             (forward-line (cdr pair))
             (set-window-start (car pair) (point)))

           (goto-char (point-min))
           (forward-line mark-row)
           (move-to-column mark-col)
           (and mark-pos
                (set-marker (mark-marker) (point)))

           (goto-char (point-min))
           (forward-line point-row)
           (move-to-column point-col))))))


(defun chartprog-intersection (x y)
  "An internal part of chartprog.el.
Return the intersection of lists X and Y, ie. elements common to both.
Elements are compared with `equal' and returned in the same order as they
appear in X.

This differs from the cl.el `intersection' in preserving the order of
elements for the return.  cl.el package doesn't preserve the order."
  (let (ret)
    (dolist (elem x) (if (member elem y) (push elem ret)))
    (nreverse ret)))

(defun chartprog-copy-tree-no-properties (obj)
  "An internal part of chartprog.el.
Return a copy of OBJ with no text properties on strings.
OBJ can be a list or other nested structure understood by
copy-sequence and mapcar."
  (cond ((stringp obj)
         (setq obj (copy-sequence obj))
         (set-text-properties 0 (length obj) nil obj)
         obj)
        ((sequencep obj)
         (mapcar 'chartprog-copy-tree-no-properties obj))
        (t
         obj)))


;;-----------------------------------------------------------------------------
;; subprocess

(defconst chartprog-protocol-version 102
  "An internal part of chartprog.el.
This must be the same as the number in App::Chart::EmacsMain, to
ensure cooperation with the sub-process.")

(defvar chartprog-process nil
  "An internal part of chartprog.el.
The running chart subprocess, or nil if not running.")
(defvar chartprog-process-timer nil
  "An internal part of chartprog.el.
Idle timer to kill chart subprocess when it's unused for a while.")

;; forward references
(defvar chartprog-completion-symbols-alist)
(defvar chartprog-latest-cache)
(defvar chartprog-symlist-alist)
(defvar chartprog-watchlist-map)
(defvar chartprog-watchlist-menu)
(defvar chartprog-quote-symbol)
(defvar chartprog-quote-changed)

(defun chartprog-exec (proc &rest args)
  "An internal part of chartprog.el.
Send the chart subprocess PROC (a symbol) with ARGS (lists,
strings, etc).  This is for an asynchronous or a no-reply message
to the subprocess.  See `chartprog-exec-synchronous' for
executing with reply."

  (unless (memq 'utf-8 (coding-system-list))
    (message "Loading mule-ucs for `utf-8' in XEmacs")
    (require 'un-define))

  ;; startup subprocess if not already running
  (unless chartprog-process
    (when (get-buffer " *chartprog subprocess*") ;; possible old buffer
      (kill-buffer " *chartprog subprocess*"))

    (setq chartprog-process
          (let ((process-connection-type nil)) ;; pipe
            (funcall 'start-process
                     "chartprog"
                     (get-buffer-create " *chartprog subprocess*")
                     "chart" "--emacs")))
    (set-process-coding-system chartprog-process 'utf-8 'utf-8)
    (set-process-filter chartprog-process 'chartprog-process-filter)
    (set-process-sentinel chartprog-process 'chartprog-process-sentinel)

    (if (eval-when-compile (fboundp 'set-process-query-on-exit-flag))
        (set-process-query-on-exit-flag chartprog-process nil) ;; emacs22
      (process-kill-without-query chartprog-process)) ;; emacs21

    (buffer-disable-undo (process-buffer chartprog-process)))

  ;; send this command
  (let ((str (concat (prin1-to-string
                      (chartprog-copy-tree-no-properties (cons proc args)))
                     "\n")))
    (chartprog-debug-message "\noutgoing " str)
    (process-send-string chartprog-process str))

  ;; start or restart idle timer
  (when chartprog-process-timer
    (cancel-timer chartprog-process-timer))
  (setq chartprog-process-timer (run-at-time "5 min" nil 'chartprog-process-kill)))

(defun chartprog-incoming-init (codeset protocol-version)
  "An internal part of chartprog.el.
Handle chart subprocess init message.
CODESET is always \"UTF-8\".
PROTOCOL-VERSION is the protocol number the subprocess is speaking, to be
matched against `chartprog-protocol-version'."

  (unless (= protocol-version chartprog-protocol-version)
    (when (get-buffer "*chartprog-watchlist*") ;; ignore if gone
      (with-current-buffer "*chartprog-watchlist*"
        (let ((inhibit-read-only t))
          (erase-buffer)
          (insert (format "Chart program doesn't match this chartprog.el.

  chartprog.el protocol:  %s
  chart program protocol: %s

Check your installation.
" chartprog-protocol-version protocol-version)))))

    (chartprog-process-kill)
    (error "Chart subprocess protocol version mismatch, got %s want %s"
           chartprog-protocol-version protocol-version)))

(defun chartprog-process-filter (proc str)
  "An internal part of chartprog.el.
The filter function for the chart subprocess per
`set-process-filter'.
STR is more text from the subprocess.
An incoming message is a Lisp form like (FOO (\"ABC\")).
When a complete form has arrived the corresponding
`chart-incoming-FOO' function is called."

  (with-current-buffer (process-buffer proc)
    (goto-char (point-max))
    (insert str)
    (while (progn
             ;; form begins with "(", ignore other diagnostics or whatever
             (goto-char (point-min))
             (skip-chars-forward "^(")
             (delete-region (point-min) (point))

             ;; see if a complete form has arrived
             (let ((form (condition-case nil
                             (read (process-buffer proc))
                           (error nil))))
               (when form
                 (delete-region (point-min) (point))

                 (chartprog-debug-message "incoming " form)
                 (apply (intern (concat "chartprog-incoming-"
                                        (symbol-name (car form))))
                        (cdr form)))

               ;; no more processing after `synchronous', let the result get
               ;; back to the caller before further asynch stuff is
               ;; processed (that further stuff deferred under a timer)
               (when (eq (car form) 'synchronous)
                 (run-at-time 0.0000001 nil
                              (lambda ()
                                (chartprog-process-filter chartprog-process "")))
                 (setq form nil))

               ;; process another form, perhaps
               form)))))

(defun chartprog-process-sentinel (proc event)
  "An internal part of chartprog.el.
The sentinel for the chart subprocess per `set-process-sentinel'.

The subprocess should stay alive forever, until we ask it to stop
by the `chartprog-process-timer, so any termination is
unexpected."
  (when (get-buffer "*chartprog-watchlist*")
    (with-current-buffer "*chartprog-watchlist*"
      (let ((inhibit-read-only t))
        (save-excursion
          (goto-char (point-min))
          (when (looking-at "\\s-*Starting")
            (erase-buffer))
          (insert (format "\nSubprocess died: %s\n\n" event))))))
  (chartprog-process-kill)
  (message "Chart subprocess died: %s" event))

(defun chartprog-process-kill ()
  "An internal part of chartprog.el.
Kill chart subprocess."
  (when chartprog-process-timer
    (cancel-timer chartprog-process-timer)
    (setq chartprog-process-timer nil))
  (when chartprog-process
    ;; clear chartprog-process variable immediately, xemacs recurses to here
    (let ((p chartprog-process))
      (setq chartprog-process nil)
      (set-process-sentinel p nil)
      (set-process-filter p nil)
      (delete-process p)
      (kill-buffer (process-buffer p)))
    ;; go back to uninitialized to force a re-read on the contents if the
    ;; subprocess is restarted, that way any additions while we were away
    ;; will appear
    (setq chartprog-completion-symbols-alist 'uninitialized)
    (setq chartprog-symlist-alist 'uninitialized)
    (setq chartprog-latest-cache (make-hash-table :test 'equal))))


;;-----------------------------------------------------------------------------
;; synchronous commands

(defvar chartprog-exec-synchronous-seq 0)
(defvar chartprog-exec-synchronous-got 0)
(defvar chartprog-exec-synchronous-result nil)

(defun chartprog-incoming-synchronous (got result)
  "An internal part of chartprog.el.
Receive synchronize number GOT from Chart subprocess."
  (setq chartprog-exec-synchronous-got got)
  (setq chartprog-exec-synchronous-result result))

(defun chartprog-exec-synchronous (proc &rest args)
  "An internal part of chartprog.el.
Call chart PROC (a symbol) with ARGS (lists, strings, etc).
Return the return value from that call, when it completes."

  (setq chartprog-exec-synchronous-seq (1+ chartprog-exec-synchronous-seq))
  (apply 'chartprog-exec 'synchronous chartprog-exec-synchronous-seq proc args)

  (while (not (= chartprog-exec-synchronous-seq
                 chartprog-exec-synchronous-got)) ;; ignore old abandoned calls
    (if (not (eq 'run (process-status chartprog-process)))
        (error "Chart subprocess died"))
    (accept-process-output chartprog-process))

  chartprog-exec-synchronous-result)


;;-----------------------------------------------------------------------------
;; incoming from subprocess

(defun chartprog-incoming-update (symbol-list)
  "An internal part of chartprog.el.
Receive advice from Chart subprocess that the symbols (strings)
in SYMBOL-LIST have updated.
Any cached data for these symbols in `chartprog-latest-cache' is
discarded.
Any of these symbols in the watchlist are re-read."
  (chartprog-latest-cache-remove symbol-list)

  (when (member chartprog-quote-symbol symbol-list)
    (setq chartprog-quote-changed t))



( run in 0.621 second using v1.01-cache-2.11-cpan-85f18b9d64f )