Add simple ncurses command center
[clnl] / src / main / cli.lisp
1 (in-package #:clnl-cli)
2
3 (defvar *cli* nil)
4 (defvar *cli-dims* (list 0 0))
5
6 (defvar *info* nil)
7
8 (defun run ()
9  "RUN => RESULT
10
11 ARGUMENTS AND VALUES:
12
13   RESULT: undefined, should never get here
14
15 DESCRIPTION:
16
17   RUN runs the command line interface in the running terminal.
18
19   This should become the main REPL for a CLNL program.  If you want to use you're
20   own REPL, you should use the rest of the functions in CLNL to recreate it."
21  (initscr)
22  (init-interface)
23  (loop
24   :for str := (cffi:with-foreign-pointer-as-string (str 255) (wgetnstr *cli* str 255))
25   :while str
26   :while (and (string/= str "q") (string/= str "Q"))
27   :do (print-command-and-response str (execute str)))
28  (endwin)
29  (sb-ext:exit :abort t))
30
31 (defun execute (str)
32  (handler-case
33   (with-output-to-string (*standard-output*)
34    (clnl:run-commands str))
35   (error (e) (format nil "Ok, something went wrong: ~A" e))))
36
37 ; for ui, we need to do at a minimum:
38 ; - cli, first pass, read things in, bottom of the screen,
39 ;     - just use getstr with a limit to be something like screen width - 10 for now
40 ;   - print what the user inputted, dont' bomb on error messages, give target for show
41 ;     - for overly long printing, go ahead and truncate with "..."
42 ; - for info, this should put out a simple message about the program, maintainer, purpose, links, etc
43
44 (defun init-interface ()
45  (let
46   ((cli-height (min 10 (floor (* *lines* .3)))))
47   (setup-info (- *lines* cli-height) *cols*)
48   (setup-cli cli-height *cols* (- *lines* cli-height))))
49
50 (defun refresh-cli ()
51  (wmove *cli* (1- (car *cli-dims*)) 0)
52  (whline *cli* (char-code #\Space) (cadr *cli-dims*))
53  (mvwprintw *cli* (1- (car *cli-dims*)) 0 ">")
54  (wmove *cli* (1- (car *cli-dims*)) 2)
55  (wrefresh *cli*))
56
57 (defun print-command-and-response (command response)
58  (loop
59   :for i :from 1 :to (- (car *cli-dims*) 3)
60   :do (wmove *cli* i 0)
61   :do (whline *cli* (char-code #\Space) (cadr *cli-dims*)))
62  (mvwprintw *cli* 1 0 (format nil "> ~A" command))
63  (mvwprintw *cli* 3 0 (format nil "~A" response))
64  (refresh-cli))
65
66 (defun setup-cli (height width top)
67  (setf *cli* (newwin height width top 0))
68  (setf *cli-dims* (list height width))
69  (whline *cli* 0 (cadr *cli-dims*))
70  (wmove *cli* (- (car *cli-dims*) 2) 0)
71  (whline *cli* (char-code #\.) (cadr *cli-dims*))
72  (wmove *cli* (1- (car *cli-dims*)) 2)
73  (wrefresh *cli*)
74  (refresh-cli))
75
76 (defun setup-info (num-tall num-wide)
77  (setf *info* (newwin 20 60 (floor (- num-tall 20) 2) (floor (- num-wide 60) 2)))
78  (mvwprintw *info* 1 1
79   (format nil
80    "
81            .
82           / \\
83          /   \\     Welcome to CLNL version ~A!
84         /     \\
85        /_______\\
86
87      CLNL is an experiment at creating an alternate
88      implementation of NetLogo.
89
90      You can enter in various netlogo commands below,
91      or use q to quit the program.
92
93      See http://github.com/frankduncan/clnl for more
94      information about CLNL and to keep apprised of
95      any updates that may happen."
96    (asdf:component-version (asdf:find-system :clnl))))
97  (box *info* 0 0)
98  (wrefresh *info*))