;;; -*- Mode: Emacs-Lisp -*-

;;; ilisp-mov.el --
;;;
;;; This file is part of ILISP.
;;; Please refer to the file COPYING for copyrights and licensing
;;; information.
;;; Please refer to the file ACKNOWLEGDEMENTS for an (incomplete) list
;;; of present and past contributors.
;;;
;;; $Id: ilisp-mov.el,v 1.3 2001/07/02 09:40:47 youngs Exp $


;;;%%Movement
(defun bol-ilisp (arg)
  "Goes to the beginning of line, then skips past the prompt, if any.
If a prefix argument is given (\\[universal-argument]), then no prompt skip 
-- go straight to column 0.

The prompt skip is done by skipping text matching the regular expression
comint-prompt-regexp or ilisp-other-prompt, both buffer local variables."
  (interactive "P")
  (beginning-of-line)
  (if (null arg) 
      (or (comint-skip-prompt)
	  (if ilisp-other-prompt
	      (let ((comint-prompt-regexp ilisp-other-prompt))
		(comint-skip-prompt))))))

;;;
(defun beginning-of-defun-lisp (&optional stay)
  "Go to the next left paren that starts at the left margin or after a
prompt in an ILISP buffer.  If optional STAY, then do not move to
prior defun if at the start of one in an ilisp mode."
  (interactive)
  (if (memq major-mode ilisp-modes)
      (let ((point (point)))
	(if (and (not stay) (= point (lisp-input-start)))
	    (progn (forward-line -1) (lisp-input-start))))
      (beginning-of-defun)))

;;;
(defun end-of-defun-lisp ()
  "Go to the next left paren that starts at the left margin or after a
prompt in an ILISP buffer and go to the end of the expression."
  (interactive)
  (let ((point (point)))
    (if (memq major-mode ilisp-modes)
	(beginning-of-defun-lisp t)
	(if (or (lisp-in-string)
		(progn (beginning-of-line)
		       (re-search-forward "^[ \t\n]*[^; \t\n]" nil t)
		       (back-to-indentation)
		       (not (bolp))))
	    (beginning-of-defun-lisp t)))
    (lisp-end-defun-text t)
    (if (= point (point))		;Already at end so move to next end
	(progn
	  (if (memq major-mode ilisp-modes)
	      (re-search-forward comint-prompt-regexp (point-max) t)
	      (lisp-skip (point-max)))
	  (if (not (or (eobp)
		       (= (char-after (point)) ?\n)))
	      (lisp-end-defun-text t))))))

;;;
(defun lisp-defun-begin ()
  "Go to the start of the containing defun and return point."
  (let (begin)
    (if (memq major-mode ilisp-modes)
	(lisp-input-start)
	(if (or (eobp) (not (and (bolp) (= (char-after (point)) ?\())))
	    (beginning-of-defun))
	(point))))

;;;
(defun lisp-defun-end (&optional no-errorp at-beginp)
  "Go to the end of the containing defun and return point or nil if
there is no end."
  (if (not at-beginp) (lisp-defun-begin))
  (condition-case ()
      (progn
	(lisp-skip (point-max))		;To skip comments on defun-end
	(forward-sexp)
	(point))
    (error (if no-errorp nil (error "Unbalanced parentheses")))))

;;;
(defun lisp-find-next-start ()
  "Find the start of the next line at the left margin that starts with
a character besides whitespace, a \) or ;;; and return the
point."
  (if (eobp)
      (point-max)
      (save-excursion
	(forward-char)
	(if (re-search-forward "^\\(\\(;;;\\)\\|\\([^ \t\n\);]\\)\\)" nil t)
	    (match-beginning 0)
	    (point-max)))))

;;;
(defun lisp-end-defun-text (&optional at-start)
  "Go the end of the text associated with the current defun and return
point.  The end is the last character before whitespace leading to
a left paren or ;;; at the left margin unless it is in a string."
  (if (not at-start) (lisp-defun-begin))
  (let ((point (point))
	(boundary (lisp-find-next-start))
	(final (save-excursion
		 (condition-case ()
		     (progn (forward-sexp) (point))
		   (error (point-max))))))
    ;; Find the next line starting at the left margin and then check
    ;; to see if it is in a string. 
    (while (progn
	     (skip-chars-forward "^\"" boundary) ;To the next string
	     (if (= (point) boundary)	
		 nil			;No quote found and at limit
		 (let ((string-boundary ;Start of next defun
			(save-excursion
			  (if (re-search-forward "^\(\\|^;;;" nil t)
			      (match-beginning 0)
			      (point-max)))))
		   (if (condition-case ()
			   (progn (forward-sexp) t)
			 (error (goto-char string-boundary) nil))
		       (if (>= (point) boundary)
			   ;; Boundary was in string
			   (if (> (point) string-boundary)
			       (progn	;String ended in next defun
				 (goto-char string-boundary)
				 nil)
			       (if (> (setq boundary
					    (lisp-find-next-start))
				      final)
				   ;; Normal defun
				   (progn (goto-char final) nil)
				   t))
			   t)
		       ;; Unclosed string
		       nil)))))
    (re-search-backward  "^[^; \t\n]\\|^[^;\n][ \t]*[^ \t\n]" point t)
    (end-of-line)
    (skip-chars-backward " \t")
    (if (< (point) point)
	(goto-char point)
	(if (save-excursion
	      (let ((point (point)))
		(beginning-of-line)
		(if comment-start (search-forward comment-start point t))))
	    (progn (next-line 1) (indent-line-ilisp)))
	(point))))

;;;
(defun lisp-in-comment (test)
  "Return T if you are in a comment."
  (beginning-of-line)
  (and (looking-at test)
       (not (= (match-end 0)
	       (progn (end-of-line) (point))))))

;;;
(defun lisp-in-string (&optional begin end)
  "Return the string region that immediately follows/precedes point or
that contains point in optional region BEGIN to END.  If point is in
region, T will be returned as well."
  (save-excursion
    (if (not begin)
	(save-excursion
	  (setq end (lisp-end-defun-text)
		begin (lisp-defun-begin))))
    (let* ((point (progn (skip-chars-forward " \t") (point)))
	   (done nil))
      (goto-char begin)
      (while (and (< (point) end) (not done))
	(skip-chars-forward "^\"" end)
	(setq begin (point))
	(if (< begin end)
	    (if (and (not (bobp)) (= (char-after (1- begin)) ??))
		(forward-char)
		(if (condition-case () (progn (forward-sexp) (<= (point) end))
		      (error nil))
		    (progn		;After string
		      (skip-chars-forward " \t")
		      (if (or (= begin point) (= point (point)))
			  (setq done (list begin (point) nil))
			  (if (and (< begin point) (< point (point)))
			      (setq done (list begin (point) t)))))
		    ;; In string at end of buffer
		    (setq done (list begin end t))))))
      done)))
