; -*- mode:lisp; package:steve; readtable:cl; -*- ;Copyright (c) May 1983 by Christopher Eliot ; and Massachusetts Institute of Technology. ;Permission to copy all or part of this material is granted, provided ; that the copies are not made or distributed for resale, the MIT ; copyright notice and reference to the source file and the software ; distribution version appear, and that notice is given that copying ; is by permission of Massachusetts Institute of Technology. ; ;Functions for the editor help system. ; (defvar *editor-command-help-file-pathname* (pathname "nil$disk:[nil.steve]comm.inf")) (defvar *help-buffer* nil) (defun with-help-file-loaded () ;;Load the help file if not loaded already. (cond ((not (null *help-buffer*))) ((null (probe-file *editor-command-help-file-pathname*)) (with-query-line (format terminal-io "Help File not In ~a. New pathname: " (send *editor-command-help-file-pathname* :string-for-editor)) (setq *editor-command-help-file-pathname* (pathname (si:ttyscanner #'readline-with-exit terminal-io terminal-io () () ())))) (with-help-file-loaded)) ((setq *help-buffer* (buffer-load "Editor Help" *editor-command-help-file-pathname*))))) (defun buffer-load (name pathname) (let ((point (make-point name nil))) (setf (buffer-file-name (edit-cursor-buffer point)) pathname) (cond ((probe-file pathname) (read-file-into-buffer pathname (edit-cursor-buffer point)) (edit-cursor-buffer point))))) (defun buffer-block-search (buffer string found-function) (loop with line = (buffer-content buffer) for block = nil while (not (null line)) for flag = nil do (loop until (null line) for chars = (line-chars line) do (setq line (line-next line)) while (not (string-equal chars "")) do (push chars block) if (string-search-for-substring chars string) do (setq flag t)) if (and flag (funcall found-function (nreverse block))) return t)) (defun buffer-block-header-search (buffer string found-function &aux (flag nil)) (loop for block = nil for line first (buffer-content buffer) then (line-next line) while (not (null line)) for chars = (line-chars line) while (null flag) if (string-search-for-substring chars string) do (loop initially (push chars block) do (setq line (line-next line)) until (null line) do (setq chars (line-chars line)) while (not (string-equal chars "")) do (push chars block) finally (setq flag (funcall found-function (nreverse block)))) else do (loop until (or (null (setq line (line-next line))) (string-equal (line-chars line) ""))) while (not (null line))) flag) (defun editor-apropos (&optional (string (mx-prompter #'readline "Apropos: "))) (overwrite-start) (with-no-passall (when (with-help-file-loaded) (with-notify-line (oustr "Searching..." terminal-io) (buffer-block-search *help-buffer* string #'(lambda (block) (loop for line in block do (overwrite-terpri) do (princ line terminal-io)) (overwrite-terpri) (overwrite-terpri) nil))))) (oustr "Done" terminal-io) (overwrite-done) (more-aborter "*done*")) (bindmetax apropos editor-apropos) (editor-bind-key #\meta-? describe-key) (defun describe-key () (with-query-line (oustr "Describe Key: " terminal-io) (let ((key) (command)) (setq key (ed-read-char) command (or (lookup-key-in-table key *editor-bindings*) (lookup-key-in-table (char-upcase key) *editor-bindings*))) (describe-key-command key command) (more-aborter "*done*"))) nil) (defun keyname-string (key) (if (consp key) (format nil "~:@C ~:@C" (car key) (cadr key)) (format nil "~:@C" key))) (defun describe-key-command (key command) (if (null command) (with-notify-line-remaining (format terminal-io "Key ~A is not defined" (keyname-string key))) (with-query-line-remaining (format terminal-io "Documentation for key ~A, ~s" (keyname-string key) command))) (cond ((null command)) ((consp command) (unless (listen terminal-io) (describe-command (car command))) (multiple-value (key command) (read-any-key key)) (describe-key-command key command)) ((describe-command command)) (t (with-notify-line-remaining (format terminal-io "No documentation for ~A, ~s" (keyname-string key) command))))) (defun editor-describe (&optional (string (mx-prompter #'readline "Describe Command: "))) (with-no-passall (cond ((describe-command string) (more-aborter "*done*")) (t (with-notify-line-remaining (oustr "No description for " terminal-io) (princ string terminal-io)))))) (bindmetax describe editor-describe) (defun describe-command (command) (cond ((null command) nil) ((characterp command) (describe-command (or (lookup-key-in-table command *editor-bindings*) (lookup-key-in-table (char-upcase command) *editor-bindings*)))) ((consp command) (describe-command (car command))) ((or (stringp command) (symbolp command)) (unless (fboundp (intern-soft command 'steve)) (with-notify-line-remaining (format terminal-io "Command ~a is not Fbound" command))) (with-no-passall (when (with-help-file-loaded) (with-notify-line (oustr "Searching..." terminal-io) (buffer-block-header-search *help-buffer* (string command) #'(lambda (block) (overwrite-start) (loop for line in block do (overwrite-terpri) do (princ line terminal-io)) (overwrite-terpri) (oustr "Done" terminal-io) (overwrite-done) t)))))) (t nil))) (bindmetax describe-char-syntax describe-char-syntax) (defun describe-char-syntax (&optional (char (mx-prompter #'read "Char: "))) (let ((desc (syntax-description syntax-table (character char)))) (with-notify-line-remaining (if (null desc) (format terminal-io "Char ~:@C has no specified syntax." char) (format terminal-io "Char ~:@C has syntax ~{~A~^, ~}" char desc))))) (defun print-last-60chars () (cursorpos 'c echo-area-window) (format echo-area-window "Last 60 characters typed:~%") (loop for i from 1 to 60 for j first (\\& (+& 60char-index 59) 60) then (\\& (+& 59 j) 60) for char = (char 60char-buffer j) for can-char = (canonicalize-control-characters char) until (char= char #\null) do (format echo-area-window "~a " (case can-char (#\space "#\\SPACE") (#\tab "#\\TAB") (t can-char))))) (defun editor-help () (with-query-line (oustr "Help (type ? for options): " terminal-io) (let ((opt (read-char&save terminal-io))) (case opt (#\? (with-notify-line-remaining (oustr "? Apropos, Describe, Key(or Character), Last 60chars(or 6), Syntax" terminal-io)) (editor-help)) ((#\A #\a) (editor-apropos)) ((#\D #\d) (editor-describe)) ((#\C #\c #\K #\k) (describe-key)) ((#\L #\l #\6) (print-last-60chars)) ((#\S #\s) (describe-char-syntax))))) nil) (editor-bind-key #\control-meta-? editor-help) ;Counting and information commands. ;This returns the number of lines and buffer characters betwen two marks, (defun count-between (bp-one bp2 &aux bp1) (multiple-value (bp1 bp2) (order-bps bp-one bp2)) (loop for line first (bp-line bp1) then (line-next line) while (and (not (null line)) (neq line (bp-line bp2))) for count first (-& (line-char-count line) (bp-position bp1)) then (+& count (line-char-count line) *line-separator-length*) counting line into lines finally (return (values (1+& lines) (if (null count) (-& (bp-position bp2) (bp-position bp1)) (+& count (bp-position bp2) *line-separator-length*)) (eq bp-one bp1))))) (editor-bind-key #\meta-= count-lines-region) (defun count-lines-region () (with-notify-line-remaining (multiple-value-bind (lines chars) (count-between *editor-cursor* (get-mark)) (format terminal-io "Lines ~a; Chars ~a" lines chars))) nil) ;(defun bp-buffer-length (buffer) ; (loop for line first (buffer-content buffer) then (line-next line) ; while (not (null line)) ; summing (line-char-count line))) ;These count the number of FILE characters. This means the line ;separator is counted as two characters (CR/LF). (defun bp-buffer-position (bp &aux (last (bp-line bp))) (plus (loop for line first (buffer-content (bp-buffer bp)) then (line-next line) while (and (not (eq line last)) (not (null line))) summing (+& (line-char-count line) *line-separator-length*)) (bp-position bp))) (defun bp-buffer-remaining (bp) (plus (-& (line-char-count (bp-line bp)) (bp-position bp)) (loop for line first (line-next (bp-line bp)) then (line-next line) until (null line) summing (+& (line-char-count line) *line-separator-length*)))) (editor-bind-key '(#\control-x #\=) what-cursor-position) (defun what-cursor-position () (with-notify-line-remaining (let ((x) (y) (char (send *editor-cursor* :get-char))) (multiple-value (x y) (real-x-y-position *editor-cursor*)) (format terminal-io "X=~a Y=~a Char=~s (ascii ~s)" x y char (char-int char)) (let ((pos (bp-buffer-position *editor-cursor*)) (left (bp-buffer-remaining *editor-cursor*))) (format terminal-io " .=~a(~a% of ~a)" pos (if (and (zerop pos) (zerop left)) '** (fix (times 100 (quotient (float pos) (plus pos left))))) (plus pos left)))))) (editor-bind-key #\control-= what-cursor-position) (defun get-buffer-major-mode (buffer) (or (let ((env (buffer-environment buffer))) (and (not (null env)) (loop for name in (load-environment-binding-vars env) for val in (load-environment-new-values env) if (eq name '*major-mode*) return val))) :fundamental)) (editor-bind-key '(#\control-x #\control-b) list-buffers) (defun list-buffers () (overwrite-start) (format terminal-io " Buffer Mode Filename") (overwrite-terpri) (format terminal-io " (* Indicates buffer needs saving)") (loop for buffer in *all-buffers* do (overwrite-terpri) do (format terminal-io "~a ~12a ~11a ~a " (if (buffer-modified? buffer) "*" " ") (buffer-name buffer) (get-buffer-major-mode buffer) (send (buffer-file-name buffer) :string-for-host)) do (unless (null (buffer-truename buffer)) (format terminal-io "(~a)" (send (buffer-truename buffer) :version)))) (overwrite-done) (more-aborter "*done*") nil) (editor-bind-key '(#\control-x #\L) count-lines-page) (defun count-lines-page () (let ((mark (send *editor-cursor* :get-mark)) (before) (after) (line)) (setq line (bp-line *editor-cursor*)) (send *editor-cursor* :backward-to-char #\page) (setq before (1-& (count-lines (bp-line *editor-cursor*) line))) (send *editor-cursor* :forward-to-char #\page) (setq after (count-lines line (bp-line *editor-cursor*))) (send *editor-cursor* :goto-mark mark) (with-notify-line-remaining (format terminal-io "Page has ~D lines (~D+~D)" (+& before after) before after))) nil) (bindmetax list-redefinitions list-redefinitions) (defun list-redefinitions () (overwrite-start) (loop for (var documentation . args) in *editor-variables* if (variable-redefined var) do (progn (format overwrite-stream "~a ~s~%" var (symeval var)) (apply #'format overwrite-stream documentation (mapcar #'eval args)) (terpri overwrite-stream) (terpri overwrite-stream))) (more-aborter "*done*")) (defun variable-redefined (variable) (let ((env (buffer-environment *editor-buffer*))) (and (not (null env)) (not (null (memq variable (load-environment-binding-vars env))))))) (bindmetax list-options list-options) (defun list-options () (overwrite-start) (loop for (var documentation . args) in *editor-variables* do (format overwrite-stream "~a ~s ~a~%" var (symeval var) (if (variable-redefined var) "(Redefined)" "")) do (apply #'format overwrite-stream documentation (mapcar #'eval args)) do (terpri overwrite-stream) do (terpri overwrite-stream)) (more-aborter "*done*"))