[svn] r6155: nemerle/trunk/misc/nemerle.el

kali svnadmin at nemerle.org
Mon Mar 6 21:30:02 CET 2006


Log:
Big update of nemerle-mode for emacs. Indentation based syntax support
was added.


Author: kali
Date: Mon Mar  6 21:29:59 2006
New Revision: 6155

Modified:
   nemerle/trunk/misc/nemerle.el

Modified: nemerle/trunk/misc/nemerle.el
==============================================================================
--- nemerle/trunk/misc/nemerle.el	(original)
+++ nemerle/trunk/misc/nemerle.el	Mon Mar  6 21:29:59 2006
@@ -60,10 +60,32 @@
 ;; You may use variables nemerle-basic-offset and nemerle-match-case-offset
 ;; to customize indentation levels.
 
+;; By default indentation based syntax is turned on. It is switched off
+;; inside any parens anyway so it should be ok. If you think otherwise
+;; you can use following in your ~/.emacs file:
+
+;; (setq nemerle-indentation-based-syntax nil)
+
 
 
 ;;; Change Log:
 
+;; 2006-03-06 Piotr Kalinowski <pitkali at gmail.com>
+;;   * I have corrected if-else structures handling
+;;     and in-match detection.
+;;   * Added support for indentation based syntax
+;;     - try .. catch and if .. else alignment
+;;     - tab iterates over different indentation possibilities
+;;     - backspace if pressed inside indentation deletes
+;;       until previous indentation level
+;;     - \C-c. to shift region right, \C-c, to shift left
+;;     - \C-c\C-j to indent single line, use region-indent
+;;       to indent multiple lines
+;;     - inside any parens indentation based syntax
+;;       modifications are turned off
+;;     - support for explicit switching syntax type with
+;;       \C-c\C-i and state feedback in mode line.
+
 ;; 2005-05-10 rzyjontko <rzyj at o2.pl>
 ;;   * final fixes of indentation engine and comment handling
 
@@ -151,6 +173,10 @@
 (defvar nemerle-match-case-offset 2
   "Indentation of match case bodies.")
 
+(defvar nemerle-indentation-based-syntax t
+  "Whether we are using indentation based syntax. On by default, because
+it'll get turned off inside any parens anyway.")
+
 (unless nemerle-mode-map
   (let ((map (make-sparse-keymap)))
     (define-key map "\C-c\C-c" 'comment-region)
@@ -159,6 +185,13 @@
     (define-key map "}" 'nemerle-electric-brace-end)
     (define-key map "*" 'nemerle-electric-star)
     (define-key map "e" 'nemerle-electric-e)
+    (define-key map "h" 'nemerle-electric-h)
+    (define-key map "\C-c\C-i" 'nemerle-toggle-indentation-syntax)
+    (define-key map "\C-c\C-j" 'nemerle-indent-line)
+    (define-key map (kbd "TAB") 'nemerle-electric-tab)
+    (define-key map (kbd "DEL") 'nemerle-electric-delete)
+    (define-key map "\C-c." 'nemerle-shift-region-right)
+    (define-key map "\C-c," 'nemerle-shift-region-left)
     (setq nemerle-mode-map map)))
 
 (unless nemerle-font-lock-keywords
@@ -232,6 +265,17 @@
   (modify-syntax-entry ?\^m "> b" nemerle-mode-syntax-table))
 
 
+(defun nemerle-toggle-indentation-syntax (arg)
+  "Toggle the variable controlling type of the syntax used."
+  (interactive "p")
+  (cond (nemerle-indentation-based-syntax
+	 (setq nemerle-indentation-based-syntax nil)
+	 (setq mode-name "Nemerle"))
+	(t
+	 (setq nemerle-indentation-based-syntax t)
+	 (setq mode-name "Nemerle Indent.")))
+  (force-mode-line-update))
+
 
 (defun nemerle-go-up-one-level ()
   "Find an innermost surrounding parenthesis (or brace, or whatever)
@@ -248,6 +292,21 @@
 	   0))))
 
 
+(defun nemerle-is-not-indentation-exception ()
+  "Returns t if indentation-based syntax should be applied."
+  (let* ((here (point))
+	 (beg-buf (point-min))
+	 (state-one (parse-partial-sexp here beg-buf))
+	 (state-two (parse-partial-sexp here beg-buf -1)))
+    (goto-char here)
+    (cond ((or (> (nth 0 state-one) 0)
+	       (nth 3 state-two))
+	   nil)
+	  (t
+	   t))))
+
+
+
 (defun nemerle-skip-sexps (end)
   "Skip all blocks of code (delimited with braces) until END.
 Returns t if inside a comment."
@@ -295,24 +354,18 @@
     (let ((end (point))
 	  (line 'none)
 	  (in-match-case nil)
-	  (depth 0)
 	  (at-end nil)
-	  (brace (nemerle-go-up-one-level)))
+	  (brace (nemerle-go-up-one-level))
+	  (begin (point)))
       (if (not (eq brace ?{))
 	  nil
-	(beginning-of-line)
-	(while (and (not at-end) (<= (point) end))
-	  (cond ((looking-at ".*{[^}]*$")
-		 (setq depth (+ depth 1)))
-		((looking-at ".*}[^{]*$")
-		 (setq depth (- depth 1)))
-		(t
-		 nil))
+	(while (and (not in-match-case) (not at-end) (<= (point) end))
 	  (setq line (nemerle-analyze-line))
-	  (if (and (eq line 'match-case) (eq depth 1))
+	  (if (and (eq line 'match-case) (eq (nth 0 (parse-partial-sexp (point) begin)) 1))
 		 (setq in-match-case t))
 	  (if (> (forward-line 1) 0)
-	      (setq at-end t)))
+	      (setq at-end t))
+	  (beginning-of-line))
 	  in-match-case))))
 
 
@@ -339,8 +392,11 @@
 	   'end-brace)
 	  ((looking-at "[ \t]*|")
 	   'match-case)
-	  ((looking-at "[ \t]*else[ \t]*")
+	  ((looking-at "[ \t]*else\\b")
 	   'else-clause)
+	  ((or (looking-at "[ \t]*catch\\b")
+	       (looking-at "[ \t]*finally\\b"))
+	   'catch-clause)
 	  (t
 	   'none))))
 
@@ -357,16 +413,36 @@
 ;; Go backwards, skipping comments, but no further than begin.
 ;; After calling this  function you end up at most at the beginning
 ;; of the line containing begin.
-(defun nemerle-go-backwards (begin)
+(defun nemerle-go-backwards (begin &optional indent-syntax)
   ;; checks if currrent line is a comment line
   ;; such situation will be noticed either by nemerle in comment in case
   ;; of being in the middle of multi line comment or two regexps checking
   ;; for the beginning of a comment.
-  (defun is-comment-line ()
-    (or (looking-at "[ \t]*//")	(looking-at "[ \t]*/\\*") (nemerle-in-comment)))
-
-  (while (and (eq (forward-line -1) 0) (<= begin (point)) (is-comment-line))
-    nil))
+  (if indent-syntax
+      (defun should-be-skipped ()
+	(or (looking-at "[ \t]*/\\*") (nemerle-in-comment) (nemerle-on-empty-line) ))
+    (defun should-be-skipped ()
+      (or (looking-at "[ \t]*//") (looking-at "[ \t]*/\\*") (nemerle-in-comment)
+	  (nemerle-on-empty-line))))
+
+  (let ((lvl (nth 0 (parse-partial-sexp (point) begin))))
+    (while (and (eq (forward-line -1) 0) (< begin (point))
+		(should-be-skipped))
+      nil)
+    (if (save-excursion (beginning-of-line) (looking-at "[ \t]*}"))
+	nil
+      (cond ((and (< begin (point))
+		  (> (nth 0 (parse-partial-sexp (point) begin)) lvl))
+	     (nemerle-go-up-one-level)
+	     (beginning-of-line)
+	     (while (and (< begin (point))
+			 (> (nth 0 (parse-partial-sexp (point) begin)) lvl))
+	       (nemerle-go-up-one-level)
+	       (beginning-of-line))
+	     (goto-char (max begin (point))))
+	    (t
+	     nil)))
+    (beginning-of-line)))
  
 
 (defun nemerle-check-if (end line offset)
@@ -388,16 +464,16 @@
     (let ((here (point))
 	  (type 'none))
        (setq type (cond
-		   ((looking-at "[ \t]*\\(if\\|for\\|foreach\\|while\\|when\\|unless\\)[ \t]*(")
+		   ((looking-at ".*\\b\\(if\\|for\\|foreach\\|while\\|when\\|unless\\)[ \t]*(")
 		    'if)
-		   ((looking-at "[ \t]*else[ \t]*")
+		   ((looking-at "[ \t]*else\\b")
 		    'else)
 		   (t
 		    'none)))
        (goto-char begin)
        (if (eq type 'else)
 	   ;; skip to corresponding if - we should have same indentation
-	   (setq here (nemerle-find-if here)))
+	   (setq here (nemerle-find-if (point) here)))
        (if (eq type 'none)
 	   offset
 	 ;; we have the if-case, so increase offset and check another line
@@ -408,11 +484,11 @@
 	   (nemerle-check-if here 'none offset))))))
 	 
 
-(defun nemerle-find-if (end)
+(defun nemerle-find-if (begin end)
   "Find if clause corresponding to else clause at position END. Return
 its position or END if no appropriate clause was found."
-  (let ((begin (point))
-	(depth 1)
+  (save-excursion
+    (let ((depth 1)
 	(retval nil)
 	(too-far nil))
     ;; depth is number of unmatched else's. We start with value of
@@ -420,18 +496,17 @@
     (goto-char end)
     (while (and (not (eq depth 0)) (not too-far))
       (nemerle-go-backwards begin)
-      (if (> begin (point)) ;; oops - to far
+	(if (or (> begin (point)) (eq (point-min) (point))) ;; oops - too far
 	  (setq too-far t))
       (goto-char (max (+ 1 begin) (point)))
-      (if (looking-at "[ \t]*else[ \t]*")
+	(if (looking-at "[ \t]*else\\b")
 	  (setq depth (+ depth 1))
-	(if (looking-at "[ \t]*if[ \t]*(")
+	  (if (looking-at ".*\\bif[ \t]*(")
 	    (setq depth (- depth 1)))))
     (if (eq depth 0)
 	(setq retval (point))
       (setq retval end))
-    (goto-char begin)
-    retval))
+      retval)))
       
 
 (defun nemerle-get-offset (end line)
@@ -450,7 +525,7 @@
 	((eq line 'else-clause)
 	 ;; handle proper indentation of else clause
 	 (nemerle-analyze-block end (nemerle-check-if
-				     (nemerle-find-if end)
+				     (nemerle-find-if (point) end)
 				     line 0)))
 	(t
 	 ;; handle if-clause without braces
@@ -465,11 +540,96 @@
     (+ nemerle-basic-offset (nemerle-get-offset end line))))
 
 
+(defun nemerle-looking-at-function ()
+  (or (looking-at
+	   "[ \t]*\\(\\(public\\|internal\\|private\\|override\\|virtual\\|static\\|protected\\)[ \t]*\\)+[^ \t]+[ \t]*(")
+      (looking-at "[ \t]*[^ \t]+[ \t]*(.*)[ \t]*:")
+      (looking-at "[ \t]*def[ \t]*[^ \t]+[ \t]*([^{]*$")))
+	 
+
+(defun nemerle-if-add-indent (line)
+  "Try to infer where indentation-based syntax need to increase indent."
+  (cond ((or
+	  (looking-at ".*\\b\\(if\\|for\\|foreach\\|while\\|when\\|unless\\)[ \t]*(")
+	  (looking-at "[ \t]*namespace\\b")
+	  (looking-at ".*\\b\\(class\\|struct\\|module\\|try\\)\\b")
+	  (nemerle-looking-at-function)
+	  (looking-at "[ \t]*else\\b")
+	  (looking-at ".*\\bmatch[ \t]*(")
+	  (looking-at "[ \t]*catch\\b")
+	  (looking-at "[ \t]*finally\b"))
+	 nemerle-basic-offset)
+	((looking-at "[ \t]*|")
+	 nemerle-match-case-offset)
+	(t
+	 0)))
+
+
+(defun nemerle-calculate-dedent (line)
+  "Try to infer, if we need to decrease indentation in indentation-based syntax."
+  (cond ((eq line 'star-comment)
+	 -1)
+	(t
+	 0)))
+
+
+(defun nemerle-find-try-clause ()
+  "Finds and moves to the try clause."
+  (let ((depth 1)
+	(too-far nil))
+      ;; depth is number of unmatched else's. We start with value of
+      ;; of 1 - the else line at end, and proceed until it reaches 0
+      (while (and (not (eq depth 0)) (not too-far))
+	(nemerle-go-backwards (point-min))
+	(if (eq (point-min) (point)) ;; oops - too far
+	    (setq too-far t))
+	(if (looking-at "[ \t]*catch\\b")
+	    (setq depth (+ depth 1))
+	  (if (looking-at ".*\\btry\\b")
+	      (setq depth (- depth 1)))))))
+
+
+(defun nemerle-find-match-clause (current)
+  "Find matching match clause."
+  (nemerle-go-backwards (point-min))
+  (let ((indent (current-indentation))
+	(previous current))
+    (while (and (or (>= indent previous)
+		    (and (not (looking-at ".*\\bmatch[ \t]*("))
+			 (not (looking-at "[ \t]*catch\\b"))
+			 (not (nemerle-looking-at-function))))
+		(not (eq (point) (point-min))))
+      (setq previous (min previous indent))
+      (nemerle-go-backwards (point-min))
+      (setq indent (current-indentation)))))
+
+
+
 (defun nemerle-calculate-indentation-of-line (line)
   "Return the absolute indentation for the line at the current point,
 where its syntactic meaning is given by LINE, and may not be IN-STRING."
   (save-excursion 
     (beginning-of-line)
+    (cond ((and nemerle-indentation-based-syntax
+		(nemerle-is-not-indentation-exception))
+	   (cond ((eq line 'else-clause)
+		  (goto-char (nemerle-find-if (point-min) (point)))
+		  (current-indentation))
+		 ((eq line 'match-case)
+		  (nemerle-find-match-clause (current-indentation))
+		  (+ (current-indentation) nemerle-basic-offset))
+		 ((eq line 'catch-clause)
+		  (nemerle-find-try-clause)
+		  (current-indentation))
+		 (t
+		  (nemerle-go-backwards (point-min) t)
+		  (if (or (eq line 'begin-brace)
+			  (looking-at "[ \t]*//")
+			  (looking-at ".*\\\\[ \t]*$"))
+		      (current-indentation)
+		    (- (+ (current-indentation) (nemerle-if-add-indent line))
+		       (nemerle-calculate-dedent line))))))
+	  (t
     (let ((end (point))
 	  (paren-char (nemerle-go-up-one-level))
 	  (top-indentation (current-indentation))
@@ -481,7 +641,7 @@
 	    ((eq paren-char 0)
 	     (nemerle-get-offset end line))
 	    (t
-	     (1+ paren-column))))))
+		    (1+ paren-column))))))))
 
 
 (defun nemerle-calculate-indentation ()
@@ -510,6 +670,78 @@
     (nemerle-indent-to level)))
 
 
+(defun nemerle-change-indentation (shift)
+  (let ((level (+ (current-indentation) shift)))
+    (if (< level 0)
+	(setq level 0))
+    (nemerle-indent-to level)))
+
+
+(defun nemerle-electric-tab ()
+  "If indentation-based syntax is turned on and the point is not
+inside any parens and it is not only brace opening or closing,
+increase indentation level. Otherwise indent line."
+  (interactive)
+  (beginning-of-line)
+  (cond ((and nemerle-indentation-based-syntax
+	   (not (looking-at "[ \t]*\\({\\|}\\)"))
+	   (nemerle-is-not-indentation-exception))
+	 (let ((current (current-indentation))
+	       (preceding 0)
+	       (suggested (nemerle-calculate-indentation)))
+	   (save-excursion
+	     (nemerle-go-backwards (point-min))
+	     (setq preceding (current-indentation)))
+	   (if (> current preceding)
+	       (nemerle-indent-to (min suggested (max 0 (- preceding nemerle-basic-offset))))
+	     (if (eq current preceding)
+		 (nemerle-indent-to
+		  (if (<= suggested current)
+		      (+ current nemerle-basic-offset)
+		    suggested))
+	       (nemerle-indent-to preceding)))))
+	(t
+	 (nemerle-indent-line))))
+
+
+(defun nemerle-electric-delete (arg)
+  "Normally behaves like backspace, but if the point is at first
+non-blank character on line and indentation based syntax should be
+applied, delete characters up to line begin or next indentation level,
+whichever comes first."
+  (interactive "p")
+  (let ((here (point))
+	(paren-column (backward-to-indentation 0))
+	(count arg))
+    (if (and nemerle-indentation-based-syntax
+	     (eq (point) here)
+	     (nemerle-is-not-indentation-exception))
+	(setq count (max (min nemerle-basic-offset paren-column) 1)))
+    (goto-char here)
+    (backward-delete-char-untabify count nil)))
+
+
+(defun nemerle-shift-region-right (start end)
+  "Shift selected region to the right by indentation level."
+  (interactive "r")
+  (nemerle-shift-region start end nemerle-basic-offset))
+
+
+(defun nemerle-shift-region-left (start end)
+  "Shift selected region to the left by indentation level."
+  (interactive "r")
+  (nemerle-shift-region start end (- nemerle-basic-offset)))
+
+
+(defun nemerle-shift-region (start end shift)
+  "Shift given region."
+  (goto-char start)
+  (nemerle-change-indentation shift)
+  (while (and (eq (forward-line 1) 0) (<= (point) end))
+    (nemerle-change-indentation shift))
+  (goto-char end))
+      
+
 (defun nemerle-electric-bar (arg)
   "Insert a bar.
 
@@ -517,7 +749,9 @@
   (interactive "p")
   (if (or (and arg (> arg 1)) (not (nemerle-on-empty-line)))
       (self-insert-command (or arg 1))
-    (if (nemerle-in-match)
+    (if (or (and nemerle-indentation-based-syntax
+		 (nemerle-is-not-indentation-exception))
+	    (nemerle-in-match))
 	(let ((level (nemerle-calculate-indentation-of-line 'match-case)))
 	  (nemerle-indent-to level))
       (let ((level (nemerle-calculate-indentation-of-line 'none)))
@@ -531,11 +765,22 @@
   (self-insert-command (or arg 1))
    (save-excursion
      (beginning-of-line)
-     (cond ((looking-at "[ \t]*else[ \t]*")
+     (cond ((looking-at "[ \t]*else\\b")
  	   (let ((level (nemerle-calculate-indentation-of-line 'else-clause)))
  	     (nemerle-indent-to level))))))
 
 
+(defun nemerle-electric-h (arg)
+  "Insert letter e and force reindent if it's else-clause."
+  (interactive "p")
+  (self-insert-command (or arg 1))
+   (save-excursion
+     (beginning-of-line)
+     (cond ((looking-at "[ \t]*catch\\b")
+ 	   (let ((level (nemerle-calculate-indentation-of-line 'catch-clause)))
+ 	     (nemerle-indent-to level))))))
+
+
 ;; Common part of brace handling. Functions for specific brace (begin, end)
 ;; need just to pass appropriate MEANING param.
 (defun nemerle-electric-brace (arg meaning)
@@ -595,7 +840,9 @@
   (interactive)
   (kill-all-local-variables)
   
-  (setq mode-name "Nemerle")
+  (if nemerle-indentation-based-syntax
+      (setq mode-name "Nemerle Indent.")
+    (setq mode-name "Nemerle"))
   (setq major-mode 'nemerle-mode)
 
   (use-local-map nemerle-mode-map)



More information about the svn mailing list