[comp.sys.next] emacs mode for objective-C

hitt@neon.Stanford.EDU (Daniel Hitt) (04/08/91)

Is there a pre-defined mode or minor mode for editing objective-C files
in emacs?  Objective-C comments seem to confuse the indentation in
a file.

If this is an FAQ or RTFM please give a reference . . .

dan

melling@cs.psu.edu (Michael D Mellinger) (04/08/91)

In article <1991Apr8.042756.19974@neon.Stanford.EDU> hitt@neon.Stanford.EDU (Daniel Hitt) writes:

   Is there a pre-defined mode or minor mode for editing objective-C files
   in emacs?  Objective-C comments seem to confuse the indentation in
   a file.

   If this is an FAQ or RTFM please give a reference . . .

   dan

An Objective C mode does not exist.  You could try using a C++ mode;
the comments are the same.  Try looking at tut.cis.ohio-state.edu
pub/gnu/emacs/elisp-archive.

-Mike

preston@LL.MIT.EDU (Steven Preston) (04/11/91)

>>>>> In article <sx5G!_6k1@cs.psu.edu>, melling@cs.psu.edu (Michael D Mellinger) writes:
> Nntp-Posting-Host: sunws5.sys.cs.psu.edu


> In article <1991Apr8.042756.19974@neon.Stanford.EDU> hitt@neon.Stanford.EDU (Daniel Hitt) writes:

>    Is there a pre-defined mode or minor mode for editing objective-C files
>    in emacs?  Objective-C comments seem to confuse the indentation in
>    a file.

>    If this is an FAQ or RTFM please give a reference . . .

>    dan

> An Objective C mode does not exist.  You could try using a C++ mode;
> the comments are the same.  Try looking at tut.cis.ohio-state.edu
> pub/gnu/emacs/elisp-archive.

I wrote one long ago that works fairly well.  The main bug is that you
can't have certain syntax characters in a // type comment.  This bug is
most often tickled by having apostrophes (like in a contraction) in
the comment.  Such comments can be enclosed in ordinary /* */ comments;
doing so will allow indentation to work, but will confuse some other
functions, like backward-sexp (ESC C-b).  So I try to avoid apostrophes
in comments altogether.

Anyway, here is the code.  It's not really a mode, but rather a
redefinition of a few c-mode functions.  You can load this in
your .emacs file.
--------------------8<----------------8<--------------------------
;;(defun c-mode-fix-apostrophe ()
;;  (modify-syntax-entry ?\' "/" c-mode-syntax-table)
  ;;the above makes an apostrophe an escape character to emacs
  ;;DANGER -- this makes emacs ignore unmatched single quotes
  ;;          so they can be used as apostrophe's in //-style
  ;;          comments.
;;  )
;;(setq c-mode-hook 'c-mode-fix-apostrophe)

(defun skip-objc-comment-backward (lim)
  "If point is in middle of objective-c style comment,
then skip backward to first slash (/), but don't skip
past LIM"
  (let ((cur (point))
	(in-quote nil)
	line-beg line-end)
    (end-of-line)
    (setq line-end (point))
    (beginning-of-line)
    (setq line-beg (point))
    (goto-char cur)
    (search-backward "//" line-beg t)
    ;;
    ;;If we found a // that is in a string
    ;;we must ignore it. We assume it is in
    ;;a string if there are an odd number of
    ;;double quotes between point and end of line.
    ;;    NOTE: this will fail if unmatched double quotes are
    ;;    placed in //-style comments.
    ;;    Such quotes will confuse (parse-partial-sexp) anyway
    ;;    and since that function is defined in the emacs
    ;;    kernal and not in emacs-lisp, I don't want to
    ;;    change it.
    ;;
    (setq cur (point))
    (while (search-forward "\"" line-end t)
      (setq in-quote (null in-quote)))
    (if in-quote (end-of-line) (goto-char cur))
    )
  (goto-char (max (point) lim))
  )

(defun at-beginning-of-line ()
  (let ((cur (point))
	result)
    (beginning-of-line)
    (setq result (= (point) cur))
    (goto-char cur)
    result))

(defun skip-white-space-and-objc-comment-backward (lim)
  "Skips backward over sequence of white space and objective-c comments"
  (interactive)
  (skip-objc-comment-backward lim)
  (skip-chars-backward " \t\f" lim)
  (while (and (> (point) lim) (at-beginning-of-line))
    (forward-char -1)
    (skip-objc-comment-backward lim)
    (skip-chars-backward " \t\f" lim))
  )

;; this replaces a c-mode function
(defun c-backward-to-noncomment (lim)
  (let (opoint stop)
    (while (not stop)
      ;; instead of (skip-chars-backward " \t\n\f" lim)
      (skip-white-space-and-objc-comment-backward lim)
      (setq opoint (point))
      (if (and (>= (point) (+ 2 lim))
	       (save-excursion
		 (forward-char -2)
		 (looking-at "\\*/")))
	  (search-backward "/*" lim 'move)
	(beginning-of-line)
	(skip-chars-forward " \t")
	(setq stop (or (not (looking-at "#")) (<= (point) lim)))
	(if stop (goto-char opoint)
	  (beginning-of-line))))))

;; this replaces a c-mode function
(defun calculate-c-indent (&optional parse-start)
  "Return appropriate indentation for current line as C code.
In usual case returns an integer: the column to indent to.
Returns nil if line starts inside a string, t if in a comment."
  (save-excursion
    (beginning-of-line)
    (let ((indent-point (point))
	  (case-fold-search nil)
	  state
	  containing-sexp)
      (if parse-start
	  (goto-char parse-start)
	(beginning-of-defun))
      (while (< (point) indent-point)
	(setq parse-start (point))
	(setq state (parse-partial-sexp (point) indent-point 0))
	(setq containing-sexp (car (cdr state))))
      (cond ((or (nth 3 state) (nth 4 state))
	     ;; return nil or t if should not change this line
	     (nth 4 state))
	    ((null containing-sexp)
	     ;; Line is at top level.  May be data or function definition,
	     ;; or may be function argument declaration.
	     ;; Indent like the previous top level line
	     ;; unless that ends in a closeparen without semicolon,
	     ;; in which case this line is the first argument decl.
	     (goto-char indent-point)
	     (skip-chars-forward " \t")
	     (if (= (following-char) ?{)
		 0   ; Unless it starts a function body
	       (c-backward-to-noncomment (or parse-start (point-min)))
	       ;; Look at previous line that's at column 0
	       ;; to determine whether we are in top-level decls
	       ;; or function's arg decls.  Set basic-indent accordinglu.
	       (let ((basic-indent
		      (save-excursion
			(re-search-backward "^[^ \^L\t\n#]" nil 'move)
			(if (and (looking-at "\\sw\\|\\s_")
				 (looking-at ".*(")
				 (progn
				   (goto-char (1- (match-end 0)))
				   (forward-sexp 1)
				   (and (< (point) indent-point)
					(not (memq (following-char)
						   '(?\, ?\;))))))
			    c-argdecl-indent 0))))
		 ;; Now add a little if this is a continuation line.
		 (+ basic-indent (if (or (bobp)
					 (memq (preceding-char) '(?\) ?\; ?\})))
				     0 c-continued-statement-offset)))))
	    ((/= (char-after containing-sexp) ?{)
	     ;; line is expression, not statement:
	     ;; indent to just after the surrounding open.
	     (goto-char (1+ containing-sexp))
	     (current-column))
	    (t
	     ;; Statement level.  Is it a continuation or a new statement?
	     ;; Find previous non-comment character.
	     (goto-char indent-point)
	     (c-backward-to-noncomment containing-sexp)
	     ;; Back up over label lines, since they don't
	     ;; affect whether our line is a continuation.
	     (while (or (eq (preceding-char) ?\,)
			(and (eq (preceding-char) ?:)
			     (or (eq (char-after (- (point) 2)) ?\')
				 (memq (char-syntax (char-after (- (point) 2)))
				       '(?w ?_)))))
	       (if (eq (preceding-char) ?\,)
		   (c-backward-to-start-of-continued-exp containing-sexp))
	       (beginning-of-line)
	       (c-backward-to-noncomment containing-sexp))
	     ;; Now we get the answer.
	     (if (not (memq (preceding-char) '(nil ?\, ?\; ?\} ?\{)))
		 ;; This line is continuation of preceding line's statement;
		 ;; indent  c-continued-statement-offset  more than the
		 ;; previous line of the statement.
		 (progn
		   (c-backward-to-start-of-continued-exp containing-sexp)
		   (+ c-continued-statement-offset (current-column)
		      (if (save-excursion (goto-char indent-point)
					  (skip-chars-forward " \t")
					  (eq (following-char) ?{))
			  c-continued-brace-offset 0)))
	       ;; This line starts a new statement.
	       ;; Position following last unclosed open.
	       (goto-char containing-sexp)
	       ;; Is line first statement after an open-brace?
	       (or
		 ;; If no, find that first statement and indent like it.
		 (save-excursion
		   (forward-char 1)
		   (let ((colon-line-end 0))
		     (while (progn (skip-chars-forward " \t\n")
				   (looking-at "#\\|/\\*\\|case[ \t\n].*:\\|[a-zA-Z0-9_$]*:\\|//"))
		       ;; Skip over comments and labels following openbrace.
		       ;; The additional case of "//" is for objective-c comments
		       (cond ((= (following-char) ?\#)
			      (forward-line 1))
			     ((looking-at "//")
			      (forward-line 1))
			     ((= (following-char) ?\/)
			      (forward-char 2)
			      (search-forward "*/" nil 'move))
			     ;; case or label:
			     (t
			      (save-excursion (end-of-line)
					      (setq colon-line-end (point)))
			      (search-forward ":"))))
		     ;; The first following code counts
		     ;; if it is before the line we want to indent.
		     (and (< (point) indent-point)
			  (if (> colon-line-end (point))
			      (- (current-indentation) c-label-offset)
			    (current-column)))))
		 ;; If no previous statement,
		 ;; indent it relative to line brace is on.
		 ;; For open brace in column zero, don't let statement
		 ;; start there too.  If c-indent-level is zero,
		 ;; use c-brace-offset + c-continued-statement-offset instead.
		 ;; For open-braces not the first thing in a line,
		 ;; add in c-brace-imaginary-offset.
		 (+ (if (and (bolp) (zerop c-indent-level))
			(+ c-brace-offset c-continued-statement-offset)
		      c-indent-level)
		    ;; Move back over whitespace before the openbrace.
		    ;; If openbrace is not first nonwhite thing on the line,
		    ;; add the c-brace-imaginary-offset.
		    (progn (skip-chars-backward " \t")
			   (if (bolp) 0 c-brace-imaginary-offset))
		    ;; If the openbrace is preceded by a parenthesized exp,
		    ;; move to the beginning of that;
		    ;; possibly a different line
		    (progn
		      (if (eq (preceding-char) ?\))
			  (forward-sexp -1))
		      ;; Get initial indentation of the line we are on.
		      (current-indentation))))))))))
--------------------8<----------------8<--------------------------
--
Steve Preston