;; VB-Mode.el -- A major mode for editing Visual Basic source files.

;; Author: Zastai <zastai@hotmail.com>
;; Keywords: languages

;;; Commentary:

;; I don't know why ANYONE would ever want to edit Visual Basic files in Emacs,
;;  but here's a major mode for those people.
;;
;; Currently only has the vb-mode function, a hook, and variables
;;  for fontification through font-lock.
;;
;; Associate the following extensions with this mode:
;;   .bas, .mak, .vbp, .frm, and .cls
;;

;;; Code:

(require 'font-lock)

(defvar vb-mode-hook nil
  "*Hook called by `vb-mode'.
")


(defvar vb-mode-syntax-table nil
  "The syntax table that is used in VB-Mode.")
(if vb-mode-syntax-table ()
  (setq vb-mode-syntax-table (make-syntax-table))
  (modify-syntax-entry ?# "w" vb-mode-syntax-table)
  (modify-syntax-entry ?_ "w" vb-mode-syntax-table)
  (modify-syntax-entry ?\. "w" vb-mode-syntax-table)
  (modify-syntax-entry ?\\ "." vb-mode-syntax-table)
  (modify-syntax-entry ?' ". 1b" vb-mode-syntax-table)
  (modify-syntax-entry ?\n "> 3b" vb-mode-syntax-table)
)

(defvar vb-mode-map nil
  "The keymap and menus used in VB-Mode.
")
(if vb-mode-map () (setq vb-mode-map (make-sparse-keymap)))

;;;###autoload
(defun vb-mode ()
  "Major mode for editing Visual Basic source code.
The hook variable `vb-mode-hook' is run at startup.

Key Bindings for VB-Mode:
  (None Yet)

Variables for customization:
  (None Yet)
"
  (interactive)
  (kill-all-local-variables)
  (set-syntax-table vb-mode-syntax-table)
  (use-local-map vb-mode-map)
  (setq major-mode 'vb-mode mode-name 
	(concat "VB" (vb-modeline-file-info)))
  (make-local-variable 'font-lock-defaults)
  (setq font-lock-defaults '(vb-font-lock-keywords))

  ;; minimize excessive tabbing
  (setq tab-width 2)

  (make-local-variable 'comment-start)
  (make-local-variable 'comment-end)
  (make-local-variable 'comment-multi-line)
  (setq comment-start "'"
 	comment-end   ""
 	comment-multi-line nil)

  (run-hooks 'vb-mode-hook)
)

(defvar vb-internal-file-subtype "Unknown"
  "The current file sub-type. For internal use only; do NOT set this yourself.
Possible values are \"Class\", \"Module\", \"Form\", \"Project\", and
\"Unknown\".
")

(defun vb-modeline-file-info ()
  "Return \" [FileType vVersion]\" for VB modeline.
FileType is gotten from extension; version from vb-figure-out-version.
"
  (concat " ["
	  (cond
	   ((not (eq (string-match "\.bas$" buffer-file-name) nil)) 
	    (setq vb-internal-file-subtype "Module"))
	   ((not (eq (string-match "\.vbp$" buffer-file-name) nil)) 
	    (setq vb-internal-file-subtype "Project"))
	    ((not (eq (string-match "\.mak$" buffer-file-name) nil)) 
	     (setq vb-internal-file-subtype "Project"))
	   (t (cond
	       ((not (eq (string-match "\.frm$" buffer-file-name) nil))
		(concat (setq vb-internal-file-subtype "Form") 
			" v" (vb-figure-out-version)))
	       ((not (eq (string-match "\.cls$" buffer-file-name) nil))
		(concat (setq vb-internal-file-subtype "Class")
			" v" (vb-figure-out-version)))
	       (t (setq vb-internal-file-subtype "Unknown"))
	       ))
	  )
	  "]"
  )
)

(defun vb-figure-out-version ()
  "Return version of Visual Basic file in current buffer.
Since this is gotten from the VERSION statement, it doesn't work for
modules (.bas).
"
  (save-excursion
    (beginning-of-buffer-nomark)
    (if (not (looking-at "VERSION \\([0-9]+\.[0-9]+\\)")) "?.0"
      (match-string 1)
    )
  )
)

(defvar vb-keywords 
  (concat ""
	  "I[fns]\\|Then\\|E\\(lse\\(If\\|\\)\\|nd\\)\\|If\\|Sub\\|With\\|"
	  "Function\\|Property\\|Type\\|Select\\|As\\|\\(Re\\|\\)Dim\\|"
	  "\\(P\\(rivate\\|ublic\\)\\)\\|Function\\|Sub\\|Const\\|Property"
	  "\\|Let\\|For\\( \\(Each\\|Input\\|Output\\|Append\\|Binary\\|"
          "Random\\)\\|\\)\\|To\\|Next\\|AddressOf\\|"
	  "Do\\|Loop\\|With\\|In\\|Declare\\|Lib\\|Alias\\|\\(Select\\|\\)"
	  "Case\\|Option \\(Compare \\(Text\\|Database\\)\\|Explicit\\|Base\\)"
	  "\\|By\\(Val\\|Ref\\)\\|N\\(othing\\|ull\\)\\|Set\\|Not\\|True\\|"
	  "False\\|Erase\\|Optional\\|ParamArray\\|And\\|Or\\|Xor\\|Mod\\|"
	  "Exit\\|\\(On Error \\|\\)\\(Resume Next\\|GoTo\\|\\)\\|Access"
	  " \\(Read\\( Write\\|\\)\\|Write\\)\\|Lock \\(Shared\\|Read\\("
	  " Write\\|\\)\\|Write\\)\\|TypeOf\\|P\\(ublic\\|rivate\\)\\|Global"
	  )
"Regexp that expands to Visual Basic's keywords.
")

(defvar vb-types
  (concat ""
	  "Integer\\|B\\(oolean\\|yte\\)\\|Long\\|String"
	  "\\|Object\\|C\\(ontrol\\|urrency\\)\\|Any\\|Date\\|Variant\\|"
	  "\\(Std\\|\\)\\(Picture\\|Font\\)"
	  )
"Regexp that expands to Visual Basic's types.
")

(defvar vb-functions
  (concat "Abs\\|Sgn\\|Int\\|Fix\\|FreeFile\\|DoEvents\\|Len\\|LoadRes\\("
	  "Data\\|String\\|Picture\\)\\|Is\\(Empty\\|Null\\|Missing\\)\\|"
	  "Format\\|C\\(Int\\|Str\\|Long\\|Byte\\|Date\\)\\|Get #\\|Put\\|"
	  "Print \\#\\|Open\\|Close\\|Tab\\|Kill\\|File\\(Attr\\|Copy\\|"
	  "Len(\\)"
	  )
"Regexp that expands to some of Visual Basic's predefined functions/subs.
")

(defvar vb-directives
  (concat "\#\\(If\\|E\\(lse\\(If\\|\\)\\|nd If\\)\\|Const\\)\\|Attribute"
	  )
"Regexp that expands to Visual Basic's directives.
")

(defvar vb-font-lock-keywords
 (list
  ;; Comments
  '("'[^\n]*$" . font-lock-comment-face)
  ;; Directives
  (cons (concat "\\<\\("  vb-directives "\\)\\>")  'font-lock-reference-face)
  ;; Keywords
  (cons (concat "\\<\\("  vb-keywords "\\)\\>")  'font-lock-keyword-face)
  ;; Types
  (cons (concat "\\<\\("  vb-types "\\)\\>")  'font-lock-type-face)
  ;; Functions
  (cons (concat "\\<\\("  vb-functions "\\)\\>") 'font-lock-function-name-face)
  '("[<>=\+-\*/\\]" . font-lock-function-name-face)
  )
  "List of expressions to fontify in Visual Basic Mode.
"
)

(provide 'vb-mode)
