;; SYNOPSIS: emacs'verilog mode enhancements, bounce on begin/end/etc.
;; AUTHOR: GPL(C) Mohsin Ahmed, http://www.cs.albany.edu/~mosh

;; USAGE:    put this in your .emacs:
;; (load-library "verilog-mode.el")
;; (load-file "PATH_TO_THE_FILE/ve-mod.el")
;; (setq auto-mode-alist (append auto-mode-alist '(
;;       ("\\.v$"  . verilog-mode))))

(add-hook 'verilog-mode-hook 'moshveri-mode)

(defun moshveri-mode ()
  "verilog mode enhancements"
  (interactive)
  (modify-syntax-entry ?_ "w"   )    ; Underscore is a word.
  
  (local-set-key [(control down)]    'moshveri-next-func)
  (local-set-key [(control up  )]    'moshveri-prev-func)
  (local-set-key [?\C-x ?w]          'moshveri-which-func)
  (local-set-key [?\C-=]             'moshveri-match-paren)
  (local-set-key [kp-space]          'moshveri-match-paren)
)

(defvar moshveri-module-re (concat
   "^\\(module\\|primitive\\)[ \t]+\\([A-Za-z_0-9]+\\)?"
  )
  "Regexp matching verilog modules used by moshveri-which-func also."
)

(defun moshveri-prev-func ()
  "goto last verilog function"
  (interactive)
  (beginning-of-line)
  (if (re-search-backward moshveri-module-re 0 t)
      nil
    (goto-char 0))
  (moshveri-which-func)
)

(defun moshveri-next-func ()
  "goto next verilog function"
  (interactive)
  (end-of-line)
  (if (re-search-forward moshveri-module-re (point-max) t)
      nil
    (goto-char (point-max))
  )
  (moshveri-which-func)
)

(defvar moshveri-parens (concat
  "\\<\\("
  "begin"         "\\|"
  "end"           "\\|"
  "module"        "\\|"
  "endmodule"     "\\|"
  "table"         "\\|"
  "endtable"      "\\|"
  "case"          "\\|"
  "endcase"       "\\|"
  "primitive"     "\\|"
  "endprimitive"  "\\|"
  "specify"       "\\|"
  "endspecify"
  "\\)\\>"        "\\|"
  "("             "\\|"
  ")"             "\\|"
  "["             "\\|"
  "]"             "\\|"
  "/\\*"          "\\|"
  "\\*/"
))

(defun moshveri-match-paren (&optional findtok count token)
  "Goto matching verilog begin/end etc.
   Bound to \\[moshveri-match-paren]."
  (interactive)
  (cond
   ((equal findtok "begin")
     (while (> count 0)
       (re-search-backward moshveri-parens (point-min))
       (setq token (match-string 1))
       (cond
          ((comment-shadow nil) nil)
          ((equal token  "*/")           (search-backward "/*"))
          ((equal token  "end")          (setq count (+ count 1)))
          ((equal token  "endtable")     (setq count (+ count 1)))
          ((equal token  "endmodule")    (setq count (+ count 1)))
          ((equal token  "endprimitive") (setq count (+ count 1)))
          ((equal token  "endspecify")   (setq count (+ count 1)))
          ((equal token  "endcase")      (setq count (+ count 1)))
          ((equal token  "begin")        (setq count (- count 1)))
          ((equal token  "module")       (setq count (- count 1)))
          ((equal token  "primitive")    (setq count (- count 1)))
          ((equal token  "table")        (setq count (- count 1)))
          ((equal token  "specify")      (setq count (- count 1)))
          ((equal token  "case")         (setq count (- count 1))))
   ))
   ((equal findtok "end")
    (while (> count 0)
      (re-search-forward moshveri-parens (point-max))
      (setq token (match-string 1))
      (cond
          ((comment-shadow t) nil)
          ((equal token  "/*")           (search-forward "*/"))
          ((equal token  "begin")        (setq count (+ count 1)))
          ((equal token  "module")       (setq count (+ count 1)))
          ((equal token  "primitive")    (setq count (+ count 1)))
          ((equal token  "table")        (setq count (+ count 1)))
          ((equal token  "specify")      (setq count (+ count 1)))
          ((equal token  "case")         (setq count (+ count 1)))
          ((equal token  "end")          (setq count (- count 1)))
          ((equal token  "endmodule")    (setq count (- count 1)))
          ((equal token  "endprimitive") (setq count (- count 1)))
          ((equal token  "endtable")     (setq count (- count 1)))
          ((equal token  "endspecify")   (setq count (- count 1)))
          ((equal token  "endcase")      (setq count (- count 1))))
    )
    (backward-word 1)
   )
   ((looking-at "\\<\\(begin\\|module\\|primitive\\|case\\|table\\)\\>")
    (end-of-line)
    (moshveri-match-paren "end" 1)
   )
   ((looking-at "\\(end\\)"  )
    (backward-char)
    (moshveri-match-paren "begin"  1)
   )
   ;; Match /* comments */
   ((looking-at "/\\*") (search-forward  "*/") (backward-char 2))
   ((looking-at "\\*/") (search-backward "/*"))
   ;; If nothing switch windows.
   ((looking-at "\\s\(") (forward-list 1) (backward-char 1))
   ((looking-at "\\s\)") (forward-char 1) (backward-list 1))
   ;; Single window?
   ((eq (count-windows) 1) (exchange-point-and-mark))
   ;; go to other window.
   (t (other-window '1))
  )
)

(defun comment-shadow (forward)
  (interactive)
  "If we are in // comment shadow move out of it, return // comment position"
  (let (eol comment-at)
    (save-excursion
      (end-of-line)
      (setq eol (point))
      (beginning-of-line)
      (setq comment-at (search-forward "//" eol 'noerror))
    )
    (if comment-at
     (progn
       (setq comment-at (- comment-at 2))
       (if (> (point) comment-at) ;; inside comment?
           (if forward (forward-line 1) (goto-char comment-at))
         (setq comment-at nil)    ;; outside comment
       )
    ))
    comment-at     ;; return where we were inside a comment.
))


(defvar moshveri-func-name "none" "Name of function for cut/paste.")

(defun moshveri-which-func (&optional moveit)
  "show and color the current function name in which the point is located.
   move point if moveit."
  (interactive)
  (let (mark1 mark2)
     (save-excursion
       (cond       
        ((eq major-mode 'verilog-mode)
         (end-of-line)
         (re-search-backward moshveri-module-re)
         (setq mark1 (match-beginning 2))
         (setq mark2 (match-end       2))
         )
       )
     )
     ;; Now return the function name if mark1 and mark2 are set.
     ;; else return an empty string.
     (if (and mark1 mark2)
         (progn
          (setq moshveri-func-name (buffer-substring mark1 mark2)) ; Save "foo"
          (message "Currently in: %s()" moshveri-func-name)        ; Print foo()
          (if moveit (goto-char mark1))                        ; goto to start.
          moshveri-func-name                                       ; Return name.
         )
       ""
     )
))

(provide 'moshveri)

;; EOF

