Commits

camlspotter committed a47f496

added

Comments (0)

Files changed (1)

+; In your .emacs:
+;
+; ;; (load "offside-trap.el") is NOT good. It must be loaded 
+; ;; after loading "haskell-indentation.el" 
+; (autoload 'offside-trap-mode (expand-file-name "~/offside-trap.el"))
+;
+; (add-hook 
+;  'haskell-mode-hook
+;  '(lambda ()
+;     (turn-on-haskell-doc-mode)
+;     (turn-on-haskell-indentation)
+;
+;     ;; this must be called after turn-on-haskell-indentation or haskell-indentation-mode,
+;     ;; so that offside-trap-mode-map overrides haskell-indentation-mode-map
+;     (offside-trap-mode t) 
+;     ))
+
+(eval-when-compile (require 'cl)) ; for `destructuring-bind'
+
+;; the overlay
+(defvar offside-trap-overlay (make-overlay 1 1))
+(defface offside-trap-face
+  '((t (:background "#000022")))
+  "Face for offside-trap highlight"
+  :group 'offside-trap)
+(overlay-put offside-trap-overlay 'face 'offside-trap-face)
+
+;; get current point position
+(defun offside-trap-lines-of-point ()
+  (count-lines (point-min) (min (1+ (point)) (point-max))))
+(defun offside-trap-chars-of-point ()
+  (- (point) (line-beginning-position)))
+
+;; get the line string at the point
+(defun offside-trap-string-of-line ()
+  (buffer-substring-no-properties
+   (line-beginning-position) (line-end-position)))
+
+;; count the indent length of the argument
+(defun offside-trap-indent-of-string (str)
+  (string-match "^[ ]*" str) ; TAB chars? Who cares ?
+  (match-end 0))
+
+;; get the indent length of the current line
+(defun offside-trap-indent-of-line ()
+  (offside-trap-indent-of-string (offside-trap-string-of-line)))
+
+;; check the line is "sticky"
+(defun offside-trap-is-sticky-line (chars)
+  (let ((str (offside-trap-string-of-line)))
+    (or (string-match "^[ ]*$" str)
+	(<= chars (offside-trap-indent-of-string str)))))
+
+;; goto line
+(defun offside-trap-goto-line (line)
+  (goto-char (point-min))
+  (forward-line (1- line)))
+
+;; find sticky lines
+(defun offside-trap-find-sticky-lines ()
+  (let ((line (offside-trap-lines-of-point))
+	(char (offside-trap-chars-of-point))
+	(indent (offside-trap-indent-of-line))
+	(last-line (count-lines (point-min) (point-max))))
+    (let ((min-indent (max char indent)) ; CR: is it really useful ?
+	  (cur-line line))
+      ; (message (format "min-indent %d" min-indent))
+      (save-excursion
+	(while
+	    (if (= cur-line last-line) nil
+	      (offside-trap-goto-line (1+ cur-line))
+	      (if (offside-trap-is-sticky-line min-indent)
+		  (progn
+		    (setq cur-line (+ cur-line 1))
+		    t)
+		nil)))
+	cur-line))))
+
+;; the last char pos of the line
+(defun offside-trap-pos-of-end-of-line (line)
+  (save-excursion
+    (offside-trap-goto-line line)
+    (line-end-position)))
+
+;; put the overlay for the sticky lines
+(defun offside-trap-overlay-sticky-lines ()
+  (interactive)
+  (let ((line (offside-trap-lines-of-point))
+	(sticky-line-end (offside-trap-find-sticky-lines)))
+    (move-overlay offside-trap-overlay
+		  (point)
+		  (offside-trap-pos-of-end-of-line sticky-line-end)
+		  (current-buffer))
+    (if (= line sticky-line-end)
+	(progn
+	  ; (delete-overlay offside-trap-overlay)
+	  nil)
+      (list (1+ line) sticky-line-end))))
+
+;; move the point to the indent head of the line
+(defun offside-trap-move-to-indent-head ()
+  (let ((indent (offside-trap-indent-of-line)))
+    (goto-char (+ (line-beginning-position) indent))))
+
+;; push back the last event and exit from the offside trap mode
+(defun offside-trap-other-char ()
+  (interactive)
+  (delete-overlay offside-trap-overlay)
+  (setq overriding-terminal-local-map nil)
+
+  ;; push back the keypress
+  (setq unread-command-events
+	(append (listify-key-sequence(this-command-keys))
+		unread-command-events)))
+
+;; offside trap mode keymap (borrowed from isearch.el)
+;; offside trap mode keymap (borrowed from isearch.el)
+(defvar offside-trap-mode-map
+  (let ((keymap (make-sparse-keymap)))
+    (define-key keymap (kbd "RET") 'offside-trap-newline-and-block-indent)
+    (define-key keymap (kbd "TAB") 'offside-trap-block-indent)
+    (define-key keymap (kbd "<C-tab>") 'indent-for-tab-command)
+    keymap))
+
+;; offside trap sticky mode keymap (borrowed from isearch.el)
+(defvar offside-trap-sticky-mode-map
+  (let ((i 0)
+	(map (make-keymap)))
+    (or (char-table-p (nth 1 map))
+	(error "The initialization of offside-trap-sticky-mode-map must be updated"))
+    ;; Default binding: exit
+    (define-key map [t] 'offside-trap-other-char)
+    (define-key map (kbd "TAB") 'offside-trap-block-indent-step)
+    (define-key map (kbd "<C-tab>") 'offside-trap-block-indent-step)
+
+    map)
+  "Keymap for `offside-trap-sticky-mode'.")
+
+;; current sticky block
+;; CR: should be buffer local ?
+(setq offside-trap-block nil)
+
+;; point must be at the indent head
+;; the sticky lines must be registered in offside-trap-block
+(defun offside-trap-block-indent-step-gen (f)
+  ; (offside-trap-move-to-indent-head) ; not required. point is at the head already
+  ; CR: check offside-trap-block is non nil
+  (let ((old-lines (offside-trap-lines-of-point))
+	(old-chars (offside-trap-chars-of-point)))
+    (apply f ())
+    (save-excursion
+      (let* ((new-lines (offside-trap-lines-of-point))
+	     (new-chars (offside-trap-chars-of-point))
+	     (diff-lines (- new-lines old-lines))
+	     (diff-chars (- new-chars old-chars)))
+        ; Line might be changed. Rebind offside-trap-block.
+	(destructuring-bind (line-start line-end) offside-trap-block
+	  (setq offside-trap-block (list (+ diff-lines line-start)
+					 (+ diff-lines line-end))))
+	(if offside-trap-block
+	    (destructuring-bind (line-start line-end) offside-trap-block
+	      ; (message (format "%d~%d (%d)" line-start line-end diff-chars))
+	      (let ((i line-start))
+		(while (<= i line-end)
+		  (offside-trap-goto-line i)
+		  (if (not (string-match "^[ ]*$" (offside-trap-string-of-line))) ;; if the line is empty, do nothing
+		      (if (> diff-chars 0)
+			  (insert-char 32 diff-chars) ; 32 is space
+			(delete-char (- diff-chars))))
+		  (setq i (1+ i))))))))))
+
+(defun offside-trap-block-indent-step ()
+  (interactive)
+  (offside-trap-block-indent-step-gen 'indent-for-tab-command))
+
+(defun offside-trap-block-indent ()
+  (interactive)
+  (offside-trap-move-to-indent-head)
+  (setq offside-trap-block (offside-trap-overlay-sticky-lines))
+  (setq overriding-terminal-local-map offside-trap-sticky-mode-map)
+  (offside-trap-block-indent-step))
+
+(defun offside-trap-newline-and-block-indent ()
+  (interactive)
+  (setq offside-trap-block (offside-trap-overlay-sticky-lines))
+  (setq overriding-terminal-local-map offside-trap-sticky-mode-map)
+  (offside-trap-block-indent-step-gen 'haskell-newline-and-indent))
+
+;;;###autoload
+(define-minor-mode offside-trap-mode
+  "Offside trap mode."
+  :lighter " OffsideTrap"
+  :keymap offside-trap-mode-map)