Download
(defvar vbnet-xemacs-p (string-match "XEmacs\\|Lucid" (emacs-version)))
(defvar vbnet-winemacs-p (string-match "Win-Emacs" (emacs-version)))
(defvar vbnet-win32-p (eq window-system 'w32))
(defvar vbnet--yasnippet-has-been-fixed nil)
(defcustom vbnet-mode-indent 4
"*Default indentation per nesting level."
:type 'integer :group 'vbnet)
(defcustom vbnet-want-fontification t
"*Whether to fontify VB.NET buffers."
:type 'boolean :group 'vbnet)
(defcustom vbnet-want-imenu t
"*Whether to generate a buffer index via imenu for VB.NET buffers."
:type 'boolean :group 'vbnet)
(defcustom vbnet-want-yasnippet-fixup t
"*Whether to enable the builtin snippets for ya-snippet. This is meaningful
only if ya-snippet is available."
:type 'boolean :group 'vbnet)
(defcustom vbnet-want-flymake-fixup t
"*Whether to enable the builtin supprot for flymake. This is meaningful
only if flymake is loaded."
:type 'boolean :group 'vbnet)
(defcustom vbnet-capitalize-keywords-p t
"*Whether to capitalize BASIC keywords."
:type 'boolean :group 'vbnet)
(defcustom vbnet-wild-files (list "*.vb" "*.frm" "*.bas" "*.cls")
"*List of Wildcard patterns for BASIC source files."
:type 'list :group 'vbnet)
(defcustom vbnet-ide-pathname nil
"*The full pathname of your Visual Basic exe file, if any."
:type 'string :group 'vbnet)
(defcustom vbnet-allow-single-line-if nil
"*Whether to allow single line if"
:type 'boolean :group 'vbnet)
(defcustom vbnet-cmd-line-limit 18
"The number of lines at the top of the file to look in, to find
the command that vbnet-mode will use to compile the current
buffer, or the command \"stub\" that vbnet-mode will use to
check the syntax of the current buffer via flymake.
If the value of this variable is zero, then vbnet-mode looks
everywhere in the file. If the value is positive, then only in
the first N lines. If negative, then only in the final N lines.
The line should appear in a comment inside the C# buffer.
Compile
--------
In the case of compile, the compile command must be prefixed with
\"compile:\". For example,
// compile: csc.exe /r:Hallo.dll Arfie.cs
This command will be suggested as the compile command when the
user invokes `compile' for the first time.
Flymake
--------
In the case of flymake, the command \"stub\" string must be
prefixed with \"flymake-command:\". For example,
// flymake-command: DOTNETDIR\csc.exe /target:netmodule /r:foo.dll
In the case of flymake-command, the string should NOT
include the name of the file for the buffer being checked.
vbnet-mode appends the name of the source file to compile, to
this command \"stub\" before passing the command to flymake to
run it.
If for some reason the command is invalid or illegal, flymake
will report an error and disable itself.
In all cases
------------
Be sure to specify the proper path for your csc.exe, whatever
version that might be, or no path if you want to use the system
PATH search.
If the buffer depends on external libraries, then you will want
to include /R arguments to that csc.exe command.
To be clear, this variable sets the number of lines to search for
the command. This cariable is an integer.
If the marker string (either \"compile:\" or \"flymake-command:\"
is present in the given set of lines, vbnet-mode will take
anything after the marker string as the command to run.
"
:type 'integer :group 'vbnet)
(defvar vbnet-defn-templates
(list "Public Sub ()\nEnd Sub\n\n"
"Public Function () As Variant\nEnd Function\n\n"
"Public Property ()\nEnd Property\n\n")
"*List of function templates though which vbnet-new-sub cycles.")
(defadvice imenu--split-menu (around
vbnet--imenu-split-menu-patch
activate compile)
(if (and (string-match "\\.[Vv][Bb]$" (file-relative-name buffer-file-name))
(boundp 'vbnet-want-imenu)
vbnet-want-imenu)
(let ((menulist (copy-sequence menulist))
keep-at-top)
(if (memq imenu--rescan-item menulist)
(setq keep-at-top (list imenu--rescan-item)
menulist (delq imenu--rescan-item menulist)))
(if imenu-sort-function
(setq menulist (sort menulist imenu-sort-function)))
(if (> (length menulist) imenu-max-items)
(setq menulist
(mapcar
(lambda (menu)
(cons (format "From: %s" (caar menu)) menu))
(imenu--split menulist imenu-max-items))))
(setq ad-return-value
(cons title
(nconc (nreverse keep-at-top) menulist))))
ad-do-it))
(defun vbnet--imenu-create-index-function-helper (&optional parent-ns indent-level)
"Helper fn for `vbnet-imenu-create-index-function-real'.
Scans for a namespace, then scans within the namespace for subs
and functions. Returns a list, suitable for use as an imenu index
alist. Leaves point after the \"End Namespace\", if it exists.
"
(if (not indent-level) (setq indent-level ""))
(let ((state 0)
done
menu-structure
submenu
suppress-next
this-flavor
this-item
container-name
(item-regex-tuples
'((func-start func-end)
(sub-start sub-end)
(propset-start propset-end)
(propget-start propget-end))))
(while (not done)
(if (eobp) (setq done t)
(setq suppress-next nil)
(cond
((or
(looking-at (vbnet-regexp 'class-start))
(looking-at (vbnet-regexp 'intf-start))
(looking-at (vbnet-regexp 'struct-start))
(looking-at (vbnet-regexp 'enum-start))
(looking-at (vbnet-regexp 'namespace-start)))
(if (string= (downcase (match-string-no-properties 1)) "namespace")
(setq this-flavor (match-string-no-properties 1)
this-item (match-string-no-properties 2))
(setq this-flavor (match-string-no-properties 2)
this-item (match-string-no-properties 3)))
(cond
((eq state 0)
(incf state)
(setq container-name
(if parent-ns
(concat parent-ns "." this-item)
this-item))
(push (concat this-flavor " " container-name) submenu)
(push (cons "(top)"
(let ((m (make-marker)))
(set-marker m (match-beginning 1)))) submenu))
((eq state 1)
(let ((child-menu
(vbnet--imenu-create-index-function-helper container-name
(concat indent-level " "))))
(if child-menu
(mapcar
'(lambda (item)
(push item submenu))
child-menu))
(setq suppress-next t)))))
((or
(looking-at (vbnet-regexp 'class-end))
(looking-at (vbnet-regexp 'intf-end))
(looking-at (vbnet-regexp 'struct-end))
(looking-at (vbnet-regexp 'enum-end))
(looking-at (vbnet-regexp 'namespace-end)))
(cond
((eq state 1)
(decf state)
(push (cons "(bottom)"
(let ((m (make-marker)))
(set-marker m (match-end 0)))) submenu)
(push (nreverse submenu) menu-structure)
(setq submenu nil))
((eq state 0)
(setq done t))))
((eq state 1)
(let (found)
(dolist (pair item-regex-tuples)
(if (and (not found)
(looking-at (vbnet-regexp (car pair))))
(progn
(setq found t)
(push (cons
(concat
(match-string-no-properties 3)
" ("
(downcase (substring (match-string-no-properties 2) 0 1))
")")
(let ((m (make-marker)))
(set-marker m (match-beginning 1)))) submenu)
(re-search-forward (vbnet-regexp (cadr pair)) nil t))))))
(t
(setq done nil))))
(if (and (not done)
(not suppress-next))
(vbnet-next-line-of-code)))
(nreverse menu-structure)))
(defun vbnet-imenu-create-index-function ()
"a function called by imenu to create an index for the current
VB.NET buffer, conforming to the format specified in
`imenu--index-alist' . To produce the index, which lists the
classes, functions, methods, and properties for the current
buffer, this function scans the entire buffer.
imenu calls this fn only when the buffer has been updated.
See `imenu-create-index-function' for more information.
"
(save-excursion
(save-restriction
(widen)
(goto-char (point-min))
(vbnet-next-line-of-code)
(let ((index-alist
(vbnet--imenu-create-index-function-helper)))
(if (and
(= 1 (length index-alist))
(consp (car index-alist))
(let ((tokens (split-string
(car (car index-alist))
"[ \t]" t)))
(and (<= 1 (length tokens))
(string= (downcase
(nth 0 tokens)) "namespace"))))
(nreverse (cdr (nreverse (cddar index-alist))))
index-alist)))))
(defvar vbnet-mode-syntax-table nil)
(if vbnet-mode-syntax-table
()
(setq vbnet-mode-syntax-table (make-syntax-table))
(modify-syntax-entry ?\' "\<" vbnet-mode-syntax-table)
(modify-syntax-entry ?\n ">" vbnet-mode-syntax-table)
(modify-syntax-entry ?\\ "w" vbnet-mode-syntax-table)
(modify-syntax-entry ?\= "." vbnet-mode-syntax-table)
(modify-syntax-entry ?\< "." vbnet-mode-syntax-table)
(modify-syntax-entry ?\> "." vbnet-mode-syntax-table))
(defvar vbnet-mode-map nil)
(if vbnet-mode-map ()
(setq vbnet-mode-map (make-sparse-keymap))
(define-key vbnet-mode-map "\t" 'vbnet-indent-line)
(define-key vbnet-mode-map "\r" 'vbnet-newline-and-indent)
(define-key vbnet-mode-map "\M-\C-a" 'vbnet-moveto-beginning-of-defun)
(define-key vbnet-mode-map (kbd "ESC <C-home>") 'vbnet-moveto-beginning-of-defun)
(define-key vbnet-mode-map (kbd "C-<") 'vbnet-moveto-beginning-of-defun)
(define-key vbnet-mode-map "\M-\C-e" 'vbnet-moveto-end-of-defun)
(define-key vbnet-mode-map (kbd "ESC <C-end>") 'vbnet-moveto-end-of-defun)
(define-key vbnet-mode-map (kbd "C->") 'vbnet-moveto-end-of-defun)
(define-key vbnet-mode-map "\M-\C-h" 'vbnet-mark-defun)
(define-key vbnet-mode-map "\M-\C-\\" 'vbnet-indent-region)
(define-key vbnet-mode-map "\C-c/" 'vbnet-close-current-block)
(define-key vbnet-mode-map "\M-q" 'vbnet-fill-or-indent)
(define-key vbnet-mode-map "\M-\C-j" 'vbnet-split-line)
(define-key vbnet-mode-map (kbd "M-RET") 'vbnet-split-line)
(define-key vbnet-mode-map (kbd "ESC <C-return>") 'vbnet-join-continued-lines)
(cond (vbnet-winemacs-p
(define-key vbnet-mode-map '(control C) 'vbnet-start-ide))
(vbnet-win32-p
(define-key vbnet-mode-map (read "[?\\S-\\C-c]") 'vbnet-start-ide)))
(if vbnet-xemacs-p
(progn
(define-key vbnet-mode-map "\M-G" 'vbnet-grep)
(define-key vbnet-mode-map '(meta backspace) 'backward-kill-word)
(define-key vbnet-mode-map '(control meta /) 'vbnet-new-sub))))
(defvar vbnet-mode-abbrev-table nil)
(defvar vbnet-mode-hook ())
(eval-and-compile
(defconst vbnet-regexp-alist
(list
`(block-start
,(concat
"^[ \t]*"
"\\([Pp]ublic\\(?: [Ss]hared\\)?\\(?: [Nn]ot[Ii]nheritable\\)?\\|"
"[Pp]rivate\\(?: [Ss]hared\\)?\\(?: [Nn]ot[Ii]nheritable\\)?\\|"
"[Ff]riend\\(?: [Ss]hared\\)?\\(?: [Nn]ot[Ii]nheritable\\)?\\|"
"[Ss]tatic\\)"
"[ \t]+"
"\\([Ss]ub\\|"
"[Ff]unction\\|"
"[Ss]tructure\\|"
"[Pp]roperty\\|"
"[Ii]nterface\\|"
"[Tt]ype\\|"
"[Ee]num\\|"
"[Cc]lass\\|"
"[Mm]odule\\)"
"[ \t]+"
"\\([^ \t\(\n]+\\)"
"[ \t]*"
"\(?"))
`(block-end
,(concat
"^[ \t]*[Ee]nd "
"\\("
"[Ss]ub\\|"
"[Ff]unction\\|"
"[Ss]tructure\\|"
"[Pp]roperty\\|"
"[Ii]nterface\\|"
"[Tt]ype\\|"
"[Ee]num\\|"
"[Cc]lass\\|"
"[Mm]odule\\)"))
`(block-flavor
,(concat
"\\b\\("
"[Ss]ub\\|"
"[Ff]unction\\|"
"[Pp]roperty\\|"
"[Ii]nterface\\|"
"[Tt]ype\\|"
"[Ee]num\\|"
"[Cc]lass\\|"
"[Mm]odule\\)\\b"))
`(intf-start
,(concat
"^[ \t]*"
"\\([Pp]ublic\\(?: [Ss]hared\\)?\\|"
"[Pp]rivate\\(?: [Ss]hared\\)?\\|"
"[Ff]riend\\(?: [Ss]hared\\)?\\|"
"[Ss]tatic\\)"
"[ \t]+"
"\\([Ii]nterface\\)"
"[ \t]+"
"\\([^ \t\(\n]+\\)"
"[ \t]*"
"\(?"))
'(intf-end "^[ \t]*[Ee]nd +[Ii]nterface")
`(func-start
,(concat
"^[ \t]*"
"\\([Pp]ublic\\(?: [Ss]hared\\)?\\|"
"[Pp]rivate\\(?: [Ss]hared\\)?\\|"
"[Ff]riend\\(?: [Ss]hared\\)?\\|"
"[Ss]tatic\\)"
"[ \t]+"
"\\([Ff]unction\\)"
"[ \t]+"
"\\([^ \t\(\n]+\\)"
"[ \t]*"
"\(?"))
'(func-end "^[ \t]*[Ee]nd +[Ff]unction")
`(sub-start
,(concat
"^[ \t]*"
"\\([Pp]ublic\\(?: [Ss]hared\\)?\\|"
"[Pp]rivate\\(?: [Ss]hared\\)?\\|"
"[Ss]tatic\\|"
"[Ff]riend\\)"
"[ \t]+"
"\\([Ss]ub\\)"
"[ \t]+"
"\\([^ \t\(\n]+\\)"
"[ \t]*"
"\(?"))
'(sub-end "^[ \t]*[Ee]nd +[Ss]ub")
`(prop-start
,(concat
"^[ \t]*"
"\\([Pp]ublic\\(?: [Ss]hared\\)?[ \t]+\\|"
"[Pp]rivate\\(?: [Ss]hared\\)?[ \t]+\\|"
"\\)"
"\\([Pp]roperty\\)"
"[ \t]+"
"\\([^ \t\(\n]+\\)"
))
'(prop-end "^[ \t]*[Ee]nd +[Pp]roperty")
`(class-start
,(concat
"^[ \t]*"
"\\([Pp]ublic\\b\\(?: [Ss]hared\\)?\\(?: [Nn]ot[Ii]nheritable\\)?\\|"
"[Pp]rivate\\b\\(?: [Ss]hared\\)?\\(?: [Nn]ot[Ii]nheritable\\)?\\|"
"[Ss]tatic\\b\\|"
"\\)"
"[ \t]*"
"\\([Cc]lass\\)"
"[ \t]+"
"\\([^ \t\(\n]+\\)"
"[ \t]*"))
'(class-end "^[ \t]*[Ee]nd +[Cc]lass")
`(struct-start
,(concat
"^[ \t]*"
"\\([Pp]ublic\\(?: [Ss]hared\\)?\\(?: [Nn]ot[Ii]nheritable\\)?\\|"
"[Pp]rivate\\(?: [Ss]hared\\)?\\(?: [Nn]ot[Ii]nheritable\\)?\\|"
"[Ff]riend\\(?: [Ss]hared\\)?\\(?: [Nn]ot[Ii]nheritable\\)?\\|"
"[Ss]tatic\\)"
"[ \t]+"
"\\([Ss]tructure\\)"
"[ \t]+"
"\\([^ \t\(\n]+\\)"
"[ \t]*"))
'(struct-end "^[ \t]*[Ee]nd +[Ss]tructure")
`(enum-start
,(concat
"^[ \t]*"
"\\([Pp]ublic\\|"
"[Pp]rivate\\|"
"[Ff]riend\\)"
"[ \t]+"
"\\([Ee]num\\)"
"[ \t]+"
"\\([^ \t\(\n]+\\)"
"\\(?:\\([ \t]+[Aa]s\\)\\([ \t]+[^ \t\(\n]+\\)\\)?"
"[ \t]*"))
'(enum-end "^[ \t]*[Ee]nd +[Ee]num")
`(namespace-start
,(concat
"^[ \t]*"
"\\([Nn]amespace\\)"
"[ \t]+"
"\\([^ \t\(\n]+\\)"
"[ \t]*"))
'(namespace-end "^[ \t]*[Ee]nd[ \t]+[Nn]amespace\\b")
'(if "^[ \t]*#?\\([Ii]f\\)[ \t]+.*[ \t_]+")
'(ifthen "^[ \t]*#?\\([Ii]f\\)\\b.+\\<[Tt]hen\\>\\s-\\S-+")
'(else "^[ \t]*#?[Ee]lse\\([Ii]f\\)?")
'(endif "[ \t]*#?[Ee]nd[ \t]*[Ii]f")
'(end-of-attr-and-continuation "^.*>[ \t]+_[ \t]*$")
'(continuation "^.* _[ \t]*$")
'(label "^[ \t]*[a-zA-Z0-9_]+:$")
'(select "^[ \t]*\\([Ss]elect\\)[ \t]+[Cc]ase")
'(case "^[ \t]*[Cc]ase")
'(select-end "^[ \t]*[Ee]nd[ \t]+[Ss]elect")
'(for "^[ \t]*[Ff]or\\b")
'(next "^[ \t]*[Nn]ext\\b")
'(do "^[ \t]*[Dd]o\\b")
'(loop "^[ \t]*[Ll]oop\\b")
'(while "^[ \t]*\\([Ww]hile\\)\\b")
'(end-while "^[ \t]*[Ee]nd[ \t]+[Ww]hile\\b")
'(wend "^[ \t]*[Ww]end\\b")
'(with "^[ \t]*\\([Ww]ith\\)\\b")
'(end-with "^[ \t]*[Ee]nd[ \t]+[Ww]ith\\b")
'(try "^[ \t]*\\([Tr]ry\\)\\b")
'(catch "^[ \t]*[Cc]atch\\b")
'(finally "^[ \t]*[Ff]inally\\b")
'(end-try "^[ \t]*[Ee]nd[ \t]+[Tt]ry\\b")
'(class "^[ \t]*[Cc]lass\\b")
'(end-class "^[ \t]*[Ee]nd[ \t]+[Cc]lass\\b")
'(module "^[ \t]*[Mm]odule\\b")
'(end-module "^[ \t]*[Ee]nd[ \t]+[Mm]odule\\b")
'(using "^[ \t]*\\([Uu]sing\\)\\b")
'(end-using "^[ \t]*[Ee]nd[ \t]+[Uu]sing\\b")
'(blank "^[ \t]*$")
'(comment "^[ \t]*\\s<.*$")
'(propget-start "^[ \t]*\\([Gg]et\\)[ \t]*$")
'(propget-end "^[ \t]*[Ee]nd[ \t]+[Gg]et\\b")
'(propset-start "^[ \t]*\\([Ss]et\\)[ \t]*(")
'(propset-end "^[ \t]*[Ee]nd[ \t]+[Ss]et\\b")
'(funcall "\\b\\([[:alpha:]_][[:alnum:]_.]+\\)[ \t]*(")
'(import "^[ \t]*[Ii]mports[ \t]+\\([[:alpha:]_][[:alnum:]_.]+\\)[ \t]*$")
`(field
,(concat
"\\(?:[Pp]ublic\\|[Pp]rivate\\|[Ff]riend\\)"
"[ \t]+"
"\\([^- \t\(]+\\)"
"[ \t]+"
"\\([Aa]s\\)\\([ \t]+[^- \t\(\n]+\\)"
"[ \t]*"
))
`(dim
,(concat
"^[ \t]*"
"[Dd]im[ \t]+"
"\\([[:alpha:]_][[:alnum:]_]+\\)"
"\\(?:([^)]*)\\)?"
))
`(as
,(concat
"[ \t]+[Aa]s"
"\\(?:[ \t]+[Nn]ew\\)?"
"[ \t]+\\([-A-Za-z.0-9_]+\\)"))
`(assign
,(concat
"^[ \t]*"
"\\("
"[[:alpha:]_][[:alnum:]_.]+\\|"
"\\[[[:alpha:]_][[:alnum:]_.]+\\]"
"\\)"
"[ \t]*"
"\\(?:([^)]*)\\)?"
"[ \t]*"
"="
"[ \t]*"
))
`(using-simple
,(concat
"^[ \t]*"
"[Uu]sing[ \t]+"
"\\([[:alpha:]_][[:alnum:]_.]+\\)"
"[ \t]*"))
`(new
,(concat
"[ \t]*"
"[Nn]ew[ \t]+"
"\\([[:alpha:]_][[:alnum:]_.]+\\)"
))
`(constant "\\(\\b\\(?:[1-9][0-9.]*\\|[0-9]\\)\\b\\|&H[0-9A-F]+\\)")
)))
(defun vbnet-regexp (symbol)
"Retrieves a regexp from the `vbnet-regexp-alist' corresponding
to the given symbol. There's probably a nifty way to do this with
a fast hash table, but an alist works fine for this purpose. It's
fast enough.
"
(let ((elt (assoc symbol vbnet-regexp-alist)))
(if elt (cadr elt) nil)))
(eval-and-compile
(defvar vbnet-all-keywords
'("Add" "Aggregate" "And" "App" "AppActivate" "Application" "Array" "As"
"Asc" "AscB" "Atn" "Attribute"
"Beep" "Begin" "BeginTrans" "Boolean" "ByVal" "ByRef"
"Catch" "CBool" "CByte" "CCur"
"CDate" "CDbl" "CInt" "CLng" "CSng" "CStr" "CVErr" "CVar" "Call"
"Case" "ChDir" "ChDrive" "Character" "Choose" "Chr" "ChrB"
"Class" "Clipboard" "Close" "Collection" "Columns"
"Command" "CommitTrans" "CompactDatabase" "Component" "Components"
"Const" "Container" "Containers" "Cos" "CreateDatabase" "CreateObject"
"CurDir" "Currency"
"DBEngine" "DDB" "Data" "Database" "Databases"
"Date" "DateAdd" "DateDiff" "DatePart" "DateSerial" "DateValue" "Day"
"Debug" "Declare" "Deftype" "DeleteSetting" "Dim" "Dir" "Do"
"DoEvents" "Domain"
"Double" "Dynaset" "EOF" "Each" "Else" "End" "EndProperty"
"Enum" "Environ" "Erase" "Err" "Error" "Exit" "Exp" "FV" "False" "Field"
"Fields" "FileAttr" "FileCopy" "FileDateTime" "FileLen" "Fix" "Font" "For"
"Form" "FormTemplate" "Format" "Forms" "FreeFile" "FreeLocks" "Friend"
"Function"
"Get" "GetAllSettings" "GetAttr" "GetObject" "GetSetting" "Global" "GoSub"
"GoTo" "Group" "Groups" "Hex" "Hour" "IIf" "IMEStatus" "IPmt" "IRR"
"If" "Implements" "InStr" "Input" "Int" "Integer" "Is" "IsArray" "IsDate"
"IsEmpty" "IsError" "IsMissing" "IsNull" "IsNumeric" "IsObject" "Kill"
"LBound" "LCase" "LOF" "LSet" "LTrim" "Left" "Len" "Let" "Like" "Line"
"Load" "LoadPicture" "LoadResData" "LoadResPicture" "LoadResString" "Loc"
"Lock" "Log" "Long" "Loop" "MDIForm" "MIRR" "Me" "MenuItems"
"MenuLine" "Mid" "Minute" "MkDir" "Month" "MsgBox"
"NPV" "NPer" "Name" "Namespace"
"New" "Next" "Not" "Now" "Nothing" "Object" "Oct" "On" "Open"
"OpenDatabase"
"Operator" "Option" "Optional"
"Or" "PPmt" "PV" "Parameter" "Parameters" "Partition"
"Picture" "Pmt" "Print" "Printer" "Printers" "Private" "ProjectTemplate"
"Property"
"Properties" "Public" "Put" "QBColor" "QueryDef" "QueryDefs"
"RSet" "RTrim" "Randomize" "Rate" "ReDim" "Recordset" "Recordsets"
"RegisterDatabase" "Relation" "Relations" "Rem" "RepairDatabase"
"Reset" "Resume" "Return" "Right" "RmDir" "Rnd" "Rollback" "RowBuffer"
"SLN" "SYD" "SavePicture" "SaveSetting" "Screen" "Second" "Seek"
"SelBookmarks" "Select" "SelectedComponents" "SendKeys" "Set"
"SetAttr" "SetDataAccessOption" "SetDefaultWorkspace" "Sgn" "Shell"
"Sin" "Single" "Snapshot" "Space" "Spc" "Sqr" "Static" "Step" "Stop" "Str"
"Structure"
"StrComp" "StrConv"
"Sub" "SubMenu" "Switch" "Tab" "Table"
"TableDef" "TableDefs" "Tan" "Then" "Time" "TimeSerial" "TimeValue"
"Timer" "To"
"True" "Try" "Type" "TypeName" "UBound" "UCase" "Unload"
"Unlock" "Using" "Val" "Variant" "VarType" "Verb" "Weekday" "Wend"
"While" "Width" "With" "Workspace" "Workspaces" "Write" "Year"
"NotInheritable" "Shared" "OrElse"
"Overridable" "WithEvents" "Finally" "Imports" "Compare" "Handles"
"Of" "Module"
)))
(make-face 'vbnet-namespace-face)
(set-face-foreground 'vbnet-namespace-face "DarkSalmon")
(defvar vbnet-namespace-face 'vbnet-namespace-face
"Face name to use for namespace names (in the Imports statement)
in VB.NET buffers.")
(make-face 'vbnet-funcall-face)
(set-face-foreground 'vbnet-funcall-face "grey")
(defvar vbnet-funcall-face 'vbnet-funcall-face
"Face name to use for function calls in VB.NET buffers.")
(defvar vbnet-font-lock-keywords-1
(eval-when-compile
(list
(list (vbnet-regexp 'constant)
'(1 font-lock-constant-face nil t))
(list (vbnet-regexp 'using-simple)
'(1 font-lock-variable-name-face))
(list (vbnet-regexp 'new)
'(1 font-lock-type-face))
(list (vbnet-regexp 'dim)
'(1 font-lock-variable-name-face)
'(2 font-lock-type-face nil t))
(list (vbnet-regexp 'field)
'(1 font-lock-variable-name-face nil t)
'(2 font-lock-keyword-face nil t)
'(3 font-lock-type-face nil t))
(list (vbnet-regexp 'as)
'(1 font-lock-type-face nil t))
(list (vbnet-regexp 'assign)
'(1 font-lock-variable-name-face))
(list (vbnet-regexp 'func-start)
'(1 font-lock-keyword-face nil t)
'(2 font-lock-keyword-face nil t)
'(3 font-lock-function-name-face))
(list (vbnet-regexp 'sub-start)
'(1 font-lock-keyword-face nil t)
'(2 font-lock-keyword-face nil t)
'(3 font-lock-function-name-face))
(list (vbnet-regexp 'class-start)
'(1 font-lock-keyword-face nil t)
'(2 font-lock-keyword-face nil t)
'(3 font-lock-type-face))
(list (vbnet-regexp 'struct-start)
'(1 font-lock-keyword-face nil t)
'(2 font-lock-keyword-face nil t)
'(3 font-lock-type-face))
(list (vbnet-regexp 'enum-start)
'(1 font-lock-keyword-face nil t)
'(2 font-lock-keyword-face nil t)
'(3 font-lock-type-face)
'(4 font-lock-keyword-face)
'(5 font-lock-type-face))
(list (vbnet-regexp 'namespace-start)
'(1 font-lock-keyword-face nil t)
'(2 vbnet-namespace-face))
(list (vbnet-regexp 'funcall)
'(1 vbnet-funcall-face))
(list (vbnet-regexp 'import)
'(1 vbnet-namespace-face))
(cons (vbnet-regexp 'label)
'font-lock-keyword-face)
(list "^[ \t]*case[ \t]+\\([^'\n]+\\)" 1 'font-lock-keyword-face t)
(list (concat "\\<" (regexp-opt
'("Dim" "If" "Then" "Else" "ElseIf" "End If") t)
"\\>")
1 'font-lock-keyword-face))))
(defvar vbnet-font-lock-keywords-2
(append vbnet-font-lock-keywords-1
(eval-when-compile
`((,(concat "\\<" (regexp-opt vbnet-all-keywords t) "\\>")
1 font-lock-keyword-face)))))
(defvar vbnet-font-lock-keywords vbnet-font-lock-keywords-1)
(put 'vbnet-mode 'font-lock-keywords 'vbnet-font-lock-keywords)
(defun vbnet-enable-font-lock ()
(cond ((or vbnet-xemacs-p window-system)
(if vbnet-winemacs-p
(font-lock-mode 1))
(cond ((boundp 'font-lock-defaults)
(make-local-variable 'font-lock-defaults)
(setq font-lock-defaults
`((vbnet-font-lock-keywords
vbnet-font-lock-keywords-1
vbnet-font-lock-keywords-2)
nil t ((,(string-to-char "_") . "w")))))
(t
(make-local-variable 'font-lock-keywords)
(setq font-lock-keywords vbnet-font-lock-keywords)))
(if vbnet-winemacs-p
(font-lock-fontify-buffer)
(font-lock-mode 1)))))
(defun vbnet-construct-keyword-abbrev-table ()
(if vbnet-mode-abbrev-table
nil
(let ((words vbnet-all-keywords)
(word nil)
(list nil))
(while words
(setq word (car words)
words (cdr words))
(setq list (cons (list (downcase word) word) list)))
(define-abbrev-table 'vbnet-mode-abbrev-table list))))
(vbnet-construct-keyword-abbrev-table)
(defun vbnet-in-code-context-p ()
(if (fboundp 'buffer-syntactic-context)
(null (buffer-syntactic-context))
(let* ((beg (save-excursion
(beginning-of-line)
(point)))
(list
(parse-partial-sexp beg (point))))
(and (null (nth 3 list))
(null (nth 4 list))))))
(defun vbnet-pre-abbrev-expand-hook ()
(setq local-abbrev-table
(if (vbnet-in-code-context-p)
vbnet-mode-abbrev-table)))
(defun vbnet-newline-and-indent (&optional count)
"Insert a newline, updating indentation."
(interactive)
(save-excursion
(expand-abbrev)
(vbnet-indent-line))
(call-interactively 'newline-and-indent))
(defun vbnet-moveto-beginning-of-block ()
"Moves to the line containing the start of the smallest containing block,
regardless whether it is a Function, Sub, Class, Namespace, etc.
See also, the related functions, `vbnet-moveto-end-of-block',
`vbnet-moveto-beginning-of-defun', and `vbnet-moveto-end-of-defun'.
"
(interactive)
(if (re-search-backward (vbnet-regexp 'block-start) 0 t)
(back-to-indentation)))
(defun vbnet-moveto-end-of-block ()
"Moves to the line containing the end of the smallest containing block,
regardless whether it is a Function, Sub, Class, Namespace, etc.
See also, the related functions, `vbnet-moveto-beginning-of-block',
`vbnet-moveto-beginning-of-defun', and `vbnet-moveto-end-of-defun'.
"
(interactive)
(if (re-search-forward (vbnet-regexp 'block-end) nil t)
(back-to-indentation)))
(defun vbnet-close-current-block ()
"Inserts the \"End Xxxx\" (etc) string to close the current
containing block, whether it is a Sub, Class, Function,
Namespace, Struct, If, While, For, Enum, Using, etc.
It looks backwards in the source code to find the innermost \"block\"
that is open, and inserts the appropriate ending syntax for that block.
The logic is naive. If you invoke this fn when point is within a
class declaration, it will insert \"End Class\" even if there is
an \"End Class\" on the line immediately following point. So
don't do that.
"
(interactive)
(let ((orig-point (point))
(block-regex-tuples
(list '(prop-start prop-end 2 0)
'(select select-end 1 0)
'(with end-with 1 0)
'(using end-using 1 0)
'(if endif 1 0)
'(for next "Next" 0)
'(while end-while 1 0)
'(sub-start sub-end 2 0)
'(try end-try 1 0)
'(func-start func-end 2 0)
'(intf-start intf-end 2 0)
'(class-start class-end 2 0)
'(struct-start struct-end 2 0)
'(enum-start enum-end 2 0)
'(propset-start propset-end 1 0)
'(propget-start propget-end 1 0)
'(namespace-start namespace-end 1 0)
))
block-end
done)
(while (not done)
(let (found
eol)
(vbnet-previous-line-of-code)
(if (bobp) (setq done t)
(setq eol (save-excursion
(end-of-line)
(point)))
(dolist (tuple block-regex-tuples)
(if (not found)
(if (re-search-forward (vbnet-regexp (cadr tuple)) eol t)
(progn
(incf (cadddr tuple))
(setq found t)))))
(if (not found)
(dolist (tuple block-regex-tuples)
(if (not found)
(if (re-search-forward (vbnet-regexp (car tuple)) eol t)
(if (eq (cadddr tuple) 0)
(setq found t
done t
block-end
(let ((value (caddr tuple)))
(if (integerp value)
(concat "End " (match-string value))
value)))
(decf (cadddr tuple))
(setq found t)))))))))
(goto-char orig-point)
(if block-end
(progn
(insert block-end)
(vbnet-indent-line)
(move-end-of-line 1)
(just-one-space 0)
))))
(defun vbnet--moveto-boundary-of-defun (goto-top)
"Moves to a boundary of the Function (or Sub) that surrounds point.
If GOTO-TOP is non-nil, then it moves to the top of the
Function (or Sub). Otherwise, it moves to the bottom of the
Function (or Sub).
If the original point is not within a Function or Sub, returns nil, and
does not move point.
"
(let* ((orig-point (point))
(block-regex-tuples
'((func-start func-end)
(sub-start sub-end)
(propset-start propset-end)
(propget-start propget-end)))
get-regex-fn1
get-regex-fn2
test-done-fn
move-fn
found
done)
(if goto-top
(setq get-regex-fn1 'cadr
get-regex-fn2 'car
test-done-fn 'bobp
move-fn 'vbnet-previous-line-of-code)
(setq get-regex-fn1 'car
get-regex-fn2 'cadr
test-done-fn 'eobp
move-fn 'vbnet-next-line-of-code))
(while (not done)
(let (eol)
(funcall move-fn)
(if (funcall test-done-fn) (setq done t)
(setq eol (save-excursion (end-of-line) (point)))
(dolist (regex-pair block-regex-tuples)
(if (not done)
(if (re-search-forward (vbnet-regexp (funcall get-regex-fn1 regex-pair)) eol t)
(setq done t))))
(if (not done)
(dolist (regex-pair block-regex-tuples)
(if (not done)
(if (re-search-forward (vbnet-regexp (funcall get-regex-fn2 regex-pair)) eol t)
(progn
(setq done t found t)
(vbnet-indent-line)
(if goto-top
(back-to-indentation)
(move-end-of-line 1))))))))))
(if (not found)
(progn
(goto-char orig-point)))
found))
(defun vbnet-moveto-beginning-of-defun ()
"Moves to the top of the Function (or Sub) that surrounds point.
If the original point is not within a Function or Sub, it throws
an error.
See also, `vbnet-moveto-end-of-defun'.
--------
NB: Emacs has a fn called `beginning-of-defun' which is designed
to do the same thing, for lisp code. It allows for alternative
logic to search to the beginning of the containing function, via
the variable `beginning-of-defun-function'. So, if things worked
nicely, vbnet-mode could simply set that variable to this
function.
But, for `beginning-of-defun' does additional things beyond after
calling the custom function. Not sure why, and VBnet-mode doesn't
want that extra stuff for navigating in VB.NET code. So, we don't
use that facility. In fact, I'm not sure of the utility of that
extension mechanism, but, whatever.
"
(interactive)
(let (debug-on-error)
(if (not (vbnet--moveto-boundary-of-defun t))
(error "Not in Function, not in Sub"))))
(defun vbnet-moveto-end-of-defun ()
"Moves to the bottom of the Function (or Sub) that surrounds point.
If the original point is not within a Function or Sub, it throws an error.
See also, `vbnet-moveto-beginning-of-defun'.
--------
NB: Emacs has a fn called `end-of-defun' which is designed
to do the same thing, for lisp code. Also, it allows for alternative
logic to search to the end of the containing function, via
the variable `end-of-defun-function'. So, if things worked
nicely, vbnet-mode could simply set that variable to this
function.
But, for `end-of-defun' does additional things beyond after
calling the custom function. Not sure why, and VBnet-mode doesn't
want that extra stuff for navigating in VB.NET code. So, we don't
use that facility. In fact, I'm not sure of the utility of that
extension mechanism, but, whatever.
"
(interactive)
(let (debug-on-error)
(if (not (vbnet--moveto-boundary-of-defun nil))
(error "Not in Function, not in Sub"))))
(defun vbnet-mark-defun ()
"Sets the mark to the bottom of the Function (or Sub) that surrounds point,
then sets the point to the top of the Function (or Sub).
If the original point is not within a Function or Sub, returns nil.
"
(interactive)
(let ((orig-point (point)))
(condition-case ()
(progn
(vbnet-moveto-end-of-defun)
(set-mark (point))
(goto-char orig-point)
(vbnet-moveto-beginning-of-defun)
(if vbnet-xemacs-p
(zmacs-activate-region)))
(progn
(goto-char orig-point)
(error "Not in a Function or Sub")))))
(defun vbnet-indent-defun ()
(interactive)
(save-excursion
(vbnet-mark-defun)
(call-interactively 'vbnet-indent-region)))
(defun vbnet-fill-long-comment ()
"Fills block of comment lines around point."
(interactive)
(save-excursion
(beginning-of-line)
(let ((comment-re "^[ \t]*\\s<+[ \t]*"))
(if (looking-at comment-re)
(let ((fill-prefix
(buffer-substring
(progn (beginning-of-line) (point))
(match-end 0))))
(while (and (not (bobp))
(looking-at (vbnet-regexp 'comment)))
(forward-line -1))
(if (not (bobp)) (forward-line 1))
(let ((start (point)))
(while (and (not (eobp))
(looking-at comment-re))
(replace-match fill-prefix)
(forward-line 1))
(if (not (eobp))
(beginning-of-line))
(fill-region-as-paragraph start (point))))))))
(defun vbnet-fill-or-indent ()
"Fill long comment around point, if any, else indent current definition."
(interactive)
(cond ((save-excursion
(beginning-of-line)
(looking-at (vbnet-regexp 'comment)))
(vbnet-fill-long-comment))
(t
(vbnet-indent-defun))))
(defun vbnet-new-sub ()
"Insert template for a new subroutine. Repeat to cycle through
alternatives.
This is probably better handled with a dedicated template module,
like ya-snippet.
"
(interactive)
(beginning-of-line)
(let ((templates (cons (vbnet-regexp 'blank)
vbnet-defn-templates))
(tem nil)
(bound (point)))
(while templates
(setq tem (car templates)
templates (cdr templates))
(cond ((looking-at tem)
(replace-match (or (car templates)
""))
(setq templates nil))))
(search-backward "()" bound t)))
(defun vbnet-untabify ()
"Used to convert any tabs present in the file, to spaces."
(if (eq major-mode 'vbnet-mode)
(untabify (point-min) (point-max)))
nil)
(defun vbnet-get-tag-around-point ()
(if (and (not (bobp))
(save-excursion
(backward-sexp)
(looking-at "\\w")))
(backward-word 1))
(let ((s (point))
(e (save-excursion
(forward-sexp)
(point))))
(buffer-substring s e)))
(defun vbnet-grep (tag)
"Search BASIC source files in current directory for TAG."
(interactive
(list (let* ((def (vbnet-get-tag-around-point))
(tag (read-string
(format "Grep for [%s]: " def))))
(if (string= tag "") def tag))))
(grep (format "grep -n %s %s" tag
(mapconcat 'identity vbnet-wild-files " "))))
(defun vbnet-buffer-project-file ()
"Return a guess as to the project file associated with the current buffer."
(car (directory-files (file-name-directory (buffer-file-name)) t "\\.vbp")))
(defun vbnet-start-ide ()
"Start Visual Basic (or your favorite IDE, (after Emacs, of course))
on the first project file in the current directory.
Note: it's not a good idea to leave Visual Basic running while you
are editing in Emacs, since Visual Basic has no provision for reloading
changed files."
(interactive)
(let (file)
(cond ((null vbnet-ide-pathname)
(error "No pathname set for Visual Basic. See vbnet-ide-pathname"))
((null (setq file (vbnet-buffer-project-file)))
(error "No project file found"))
((fboundp 'win-exec)
(iconify-emacs)
(win-exec vbnet-ide-pathname 'win-show-normal file))
((fboundp 'start-process)
(iconify-frame (selected-frame))
(start-process "*VisualBasic*" nil vbnet-ide-pathname file))
(t
(error "No way to spawn process!")))))
(defun vbnet-indent-region (start end)
"Apply indentation according to Visual Basic .NET syntax, for
each line in the region.
See also `vbnet-indent-line'.
"
(interactive "r")
(save-excursion
(goto-char start)
(beginning-of-line)
(while (and (not (eobp))
(< (point) end))
(if (not (looking-at (vbnet-regexp 'blank)))
(vbnet-indent-line))
(forward-line 1)))
(cond ((fboundp 'zmacs-deactivate-region)
(zmacs-deactivate-region))
((fboundp 'deactivate-mark)
(deactivate-mark))))
(defun vbnet-previous-line-of-code ()
"Moves to the previous non-blank, non-comment line in the buffer."
(if (not (bobp))
(forward-line -1))
(while (and (not (bobp))
(or (looking-at (vbnet-regexp 'blank))
(looking-at (vbnet-regexp 'comment))))
(forward-line -1)))
(defun vbnet-next-line-of-code ()
"Moves to the next non-blank, non-comment line in the buffer."
(if (not (eobp))
(forward-line 1))
(while (and (not (eobp))
(or (looking-at (vbnet-regexp 'blank))
(looking-at (vbnet-regexp 'comment))))
(forward-line 1)))
(defun vbnet--back-to-start-of-continued-statement (&optional dont-backup-over-attributes)
"If the current line is a continuation, move back to the original statement.
Do not backup over attributes if the optional arg,
DONT-BACKUP-OVER-ATTRIBUTES, is t.
"
(let ((here (point)))
(vbnet-previous-line-of-code)
(while (and (not (bobp))
(looking-at (vbnet-regexp 'continuation))
(not (and dont-backup-over-attributes
(looking-at (vbnet-regexp 'end-of-attr-and-continuation))))
)
(setq here (point))
(vbnet-previous-line-of-code))
(goto-char here)))
(defun vbnet-find-matching-stmt (open-regexp close-regexp)
"Search backwards to find a matching statement. Attempts to properly
handle nested blocks. "
(let ((level 0))
(while (and (>= level 0) (not (bobp)))
(vbnet-previous-line-of-code)
(vbnet--back-to-start-of-continued-statement t)
(cond ((looking-at close-regexp)
(setq level (+ level 1)))
((looking-at open-regexp)
(setq level (- level 1)))))))
(defun vbnet--get-indent-column-for-continued-line (original-point)
"Calculate indent for a line which follows a continuation line.
Upon entry, the point must be positioned on the line *prior to
the one to be indented*, and ORIGINAL-POINT refers to the line
being indented.
Indent continuation lines according to some rules.
1. If the continuation line is a .NET Attribute, (eg
<DllImport(...)> then indent the following line to the same
column.
2. if the continued line has an open paren pair, then
indent the following line to the first open paren on the
previous line.
3. otherwise, indent one word in.
"
(let ((starting (point)))
(cond
((looking-at (vbnet-regexp 'end-of-attr-and-continuation))
(vbnet--back-to-start-of-continued-statement)
(back-to-indentation)
(current-column))
(t
(vbnet--back-to-start-of-continued-statement)
(let* ((orig-stmt (point))
(matching-open-paren
(condition-case ()
(save-excursion
(goto-char original-point)
(beginning-of-line)
(backward-up-list 1)
(if (<= orig-stmt (point))
(current-column)))
(error nil))))
(cond (matching-open-paren
(1+ matching-open-paren))
(t
(back-to-indentation)
(forward-word 1)
(while (looking-at "[ \t]")
(forward-char 1))
(current-column))))))))
(defun vbnet-calculate-indent ()
"Calculate the indentation for the current point in a vb.net buffer."
(let ((original-point (point)))
(save-excursion
(beginning-of-line)
(cond
((bobp)
0)
((looking-at (vbnet-regexp 'namespace-end))
(vbnet-find-matching-stmt (vbnet-regexp 'namespace-start)
(vbnet-regexp 'namespace-end))
(current-indentation))
((looking-at (vbnet-regexp 'class-end))
(vbnet-find-matching-stmt (vbnet-regexp 'class-start)
(vbnet-regexp 'class-end))
(current-indentation))
((looking-at (vbnet-regexp 'struct-end))
(vbnet-find-matching-stmt (vbnet-regexp 'struct-start)
(vbnet-regexp 'struct-end))
(current-indentation))
((looking-at (vbnet-regexp 'enum-end))
(vbnet-find-matching-stmt (vbnet-regexp 'enum-start)
(vbnet-regexp 'enum-end))
(current-indentation))
((looking-at (vbnet-regexp 'prop-end))
(vbnet-find-matching-stmt (vbnet-regexp 'prop-start)
(vbnet-regexp 'prop-end))
(current-indentation))
((looking-at (vbnet-regexp 'propget-end))
(vbnet-find-matching-stmt (vbnet-regexp 'propget-start)
(vbnet-regexp 'propget-end))
(current-indentation))
((looking-at (vbnet-regexp 'propset-end))
(vbnet-find-matching-stmt (vbnet-regexp 'propset-start)
(vbnet-regexp 'propset-end))
(current-indentation))
((looking-at (vbnet-regexp 'func-end))
(vbnet-find-matching-stmt (vbnet-regexp 'func-start)
(vbnet-regexp 'func-end))
(current-indentation))
((looking-at (vbnet-regexp 'sub-end))
(vbnet-find-matching-stmt (vbnet-regexp 'sub-start)
(vbnet-regexp 'sub-end))
(current-indentation))
((or (looking-at (vbnet-regexp 'else))
(looking-at (vbnet-regexp 'endif)))
(vbnet-find-matching-stmt (vbnet-regexp 'if)
(vbnet-regexp 'endif))
(current-indentation))
((or (looking-at (vbnet-regexp 'catch))
(looking-at (vbnet-regexp 'finally)))
(vbnet-find-matching-stmt (vbnet-regexp 'try)
(vbnet-regexp 'end-try))
(current-indentation))
((looking-at (vbnet-regexp 'next))
(vbnet-find-matching-stmt (vbnet-regexp 'for)
(vbnet-regexp 'next))
(current-indentation))
((looking-at (vbnet-regexp 'loop))
(vbnet-find-matching-stmt (vbnet-regexp 'do)
(vbnet-regexp 'loop))
(current-indentation))
((looking-at (vbnet-regexp 'wend))
(vbnet-find-matching-stmt (vbnet-regexp 'while)
(vbnet-regexp 'wend))
(current-indentation))
((looking-at (vbnet-regexp 'end-while))
(vbnet-find-matching-stmt (vbnet-regexp 'while)
(vbnet-regexp 'end-while))
(current-indentation))
((looking-at (vbnet-regexp 'end-with))
(vbnet-find-matching-stmt (vbnet-regexp 'with)
(vbnet-regexp 'end-with))
(current-indentation))
((looking-at (vbnet-regexp 'end-try))
(vbnet-find-matching-stmt (vbnet-regexp 'try)
(vbnet-regexp 'end-try))
(current-indentation))
((looking-at (vbnet-regexp 'end-using))
(vbnet-find-matching-stmt (vbnet-regexp 'using)
(vbnet-regexp 'end-using))
(current-indentation))
((looking-at (vbnet-regexp 'select-end))
(vbnet-find-matching-stmt (vbnet-regexp 'select)
(vbnet-regexp 'select-end))
(current-indentation))
((looking-at (vbnet-regexp 'case))
(vbnet-find-matching-stmt (vbnet-regexp 'select)
(vbnet-regexp 'select-end))
(+ (current-indentation) vbnet-mode-indent))
(t
(vbnet-previous-line-of-code)
(while (looking-at (vbnet-regexp 'label))
(vbnet-previous-line-of-code))
(cond
((looking-at (vbnet-regexp 'continuation))
(vbnet--get-indent-column-for-continued-line original-point))
(t
(vbnet--back-to-start-of-continued-statement t)
(let ((indent (current-indentation)))
(cond
((looking-at (vbnet-regexp 'block-start))
(+ indent vbnet-mode-indent))
((or (looking-at (vbnet-regexp 'class-start))
(looking-at (vbnet-regexp 'struct-start))
(looking-at (vbnet-regexp 'enum-start))
(looking-at (vbnet-regexp 'namespace-start))
(looking-at (vbnet-regexp 'propget-start))
(looking-at (vbnet-regexp 'propset-start))
(looking-at (vbnet-regexp 'module)))
(+ indent vbnet-mode-indent))
((and (or (looking-at (vbnet-regexp 'if))
(looking-at (vbnet-regexp 'else)))
(not (and vbnet-allow-single-line-if
(looking-at (vbnet-regexp 'ifthen)))))
(+ indent vbnet-mode-indent))
((or (looking-at (vbnet-regexp 'select))
(looking-at (vbnet-regexp 'case)))
(+ indent vbnet-mode-indent))
((or (looking-at (vbnet-regexp 'try))
(looking-at (vbnet-regexp 'catch))
(looking-at (vbnet-regexp 'finally)))
(+ indent vbnet-mode-indent))
((or (looking-at (vbnet-regexp 'do))
(looking-at (vbnet-regexp 'for))
(looking-at (vbnet-regexp 'while))
(looking-at (vbnet-regexp 'with))
(looking-at (vbnet-regexp 'using)))
(+ indent vbnet-mode-indent))
(t
indent))))))))))
(defun vbnet-indent-to-column (col)
(let* ((bol (save-excursion
(beginning-of-line)
(point)))
(point-in-whitespace
(<= (point) (+ bol (current-indentation))))
(blank-line-p
(save-excursion
(beginning-of-line)
(looking-at (vbnet-regexp 'blank)))))
(cond ((/= col (current-indentation))
(save-excursion
(beginning-of-line)
(back-to-indentation)
(delete-region bol (point))
(indent-to col))))
(cond (blank-line-p
(end-of-line))
(point-in-whitespace
(back-to-indentation)))))
(defun vbnet-indent-line ()
"Indent current line for Visual Basic syntax. This assumes that the
previous non-blank line is indented properly.
See also `vbnet-indent-region'.
"
(interactive)
(vbnet-indent-to-column (vbnet-calculate-indent)))
(defun vbnet-split-line ()
"Split line at point, adding continuation character or continuing
a comment. In Abbrev mode, any abbrev before point will be expanded.
See also `vbnet-join-continued-lines'
"
(interactive)
(save-excursion
(let* ((opoint (point))
(bol (progn (beginning-of-line) (point)))
(pps-list
(parse-partial-sexp bol opoint)))
(cond ((nth 4 pps-list)
(indent-new-comment-line))
((nth 3 pps-list)
(error "Can't break line inside a string"))
(t
(just-one-space)
(insert "_")
(vbnet-newline-and-indent))))))
(defun vbnet-join-continued-lines ()
"Join the split line at point, removing the continuation
character and concatenating the current line and the following
line. At exit, point is moved to the end of the joined line.
See also `vbnet-split-line'
"
(interactive)
(save-excursion
(let* ((opoint (point))
(bol (progn (beginning-of-line) (point)))
(eol (progn (end-of-line) (point)))
(pps-list
(parse-partial-sexp bol eol)))
(cond ((nth 4 pps-list)
nil)
((nth 3 pps-list)
nil)
(t
(cond
((equal opoint eol)
(backward-char 4))
((equal opoint (1- eol))
(backward-char 3))
((equal (1+ opoint) (1- eol))
(backward-char 2))
(t
(goto-char opoint)))
(if (looking-at ".+ _$")
(progn
(end-of-line)
(delete-char -1)
(delete-char 1)
(just-one-space))))))))
(defun vbnet-load-associated-files ()
"Load files that are useful to have around when editing the source
of the file that has just been loaded.
The buffer must have a local variable, `vbnet-associated-files',
that is a list of strings, naming the \"associated\" files to be
opened into editing buffers. If the file name is relative it is
relative to the directory containing the current buffer. If the
file is already loaded into an editing buffer, nothing happens;
this prevents circular references from causing trouble.
After an associated file is loaded, if it is a VB.NET module and
if it has the appropriate variable set, its associated files list
will be processed in turn.
"
(if (boundp 'vbnet-associated-files)
(let ((files vbnet-associated-files)
(file nil))
(while files
(setq file (car files)
files (cdr files))
(message "Load associated file: %s" file)
(vbnet-load-file-ifnotloaded file default-directory)))))
(defun vbnet-load-file-ifnotloaded (file default-directory)
"Load file if not already loaded.
If file is relative then default-directory provides the path"
(let((file-absolute (expand-file-name file default-directory)))
(if (get-file-buffer file-absolute)
()
(find-file-noselect file-absolute ))))
(defun vbnet-fixup-yasnippet ()
"Sets snippets into ya-snippet for VB.NET, if they do not already exist.
"
(if (not vbnet--yasnippet-has-been-fixed)
(if (fboundp 'yas/snippet-table-fetch)
(let ((snippet-table (yas/snippet-table 'vbnet-mode))
(keymap (if yas/use-menu
(yas/menu-keymap-for-mode mode)
nil))
(yas/require-template-condition nil)
(builtin-snips
'(
("wl" "System.Console.WriteLine(${1://thing to write})
" "WriteLine (...)" nil)
("prop" " Private _${1:Name} as ${2:Type}
Public Property ${1:Name}() As ${2:Type}
Get
Return m${1:Name}
End Get
Set(ByVal value As ${2:Type})
m${1:Name} = value
End Set
End Property ' ${1:Name}
" "Property ... { ... }" nil)
("ife" "If ${1:predicate} Then
${2:// then clause}
Else
${3:// else clause}
End If" "If ... Then ... Else ... End If" nil)
("if" "If ${1:predicate} Then
${2:// then clause}
End If
" "If ... Then ... End If" nil)
("fore" "Dim ${1:var} As ${2:type}
For Each $1 In ${3:IEnumerable}
${4:'' body...}
Next
" "For Each ... Next" nil)
("for" "For ${1:index} As Integer = 0 To ${2:finish}
${3:''body}
Next $1
" "for (...) { ... }" nil)
("args" " Dim i As Integer
For i = 0 To args.Length - 1
Select Case(args(i))
Case \"-b\":
If (_boolValue = True) Then
Throw New ArgumentException(args(i))
End If
_boolValue = True
Case \"-s\":
i += 1
If (args.Length <= i) Then
Throw New ArgumentException(args(i))
End If
If Not (Me._stringValue Is Nothing) Then
Throw New ArgumentException(args((i - 1)))
End If
_stringValue = args(i)
Case \"-n\":
i += 1
If (args.Length <= i) Then
Throw New ArgumentException(args(i))
End If
If (Me._intValue <> 0) Then
Throw New ArgumentException(args((i - 1)))
End If
If args(i).StartsWith(\"0x\") Then
Me._intValue = Integer.Parse(args(i).Substring(2), NumberStyles.AllowHexSpecifier)
Else
Me._intValue = Integer.Parse(args(i))
End If
case \"-?\":
Throw New ArgumentException(args(i))
Case Else:
Throw New ArgumentException(args(i))
End Select
Next i
If (Me._intValue = 0) Then
Me._intValue = Me.DefaultIntValue
End If
" "Select Case(args(i) ..." nil)
)))
(setq vbnet--yasnippet-has-been-fixed t)
(when yas/use-menu
(define-key
yas/menu-keymap
(vector 'vbnet-mode)
`(menu-item "VB.Net" ,keymap)))
(mapcar
'(lambda (item)
(let* ((full-key (car item))
(existing-snip
(yas/snippet-table-fetch snippet-table full-key)))
(if (not existing-snip)
(let* ((key (file-name-sans-extension full-key))
(name (caddr item))
(condition (nth 3 item))
(template (yas/make-template (cadr item)
(or name key)
condition)))
(yas/snippet-table-store snippet-table
full-key
key
template)
(when yas/use-menu
(define-key keymap (vector (make-symbol full-key))
`(menu-item ,(yas/template-name template)
,(yas/make-menu-binding (yas/template-content template))
:keys ,(concat key yas/trigger-symbol))))))))
builtin-snips)))))
(defun vbnet-flymake-init ()
(vbnet-flymake-init-impl
'flymake-create-temp-inplace t t 'vbnet-flymake-get-cmdline))
(defun vbnet-flymake-init-impl (create-temp-f use-relative-base-dir use-relative-source get-cmdline-f)
"Create syntax check command line for a directly checked source file.
Use CREATE-TEMP-F for creating temp copy."
(let* ((args nil)
(temp-source-file-name (flymake-init-create-temp-buffer-copy create-temp-f)))
(setq args (flymake-get-syntax-check-program-args
temp-source-file-name "."
use-relative-base-dir use-relative-source
get-cmdline-f))
args))
(defun vbnet-flymake-cleanup ()
"Delete the temporary .netmodule file created in syntax checking,
then call through to flymake-simple-cleanup."
(if flymake-temp-source-file-name
(progn
(let* ((netmodule-name
(concat (file-name-sans-extension flymake-temp-source-file-name)
".netmodule"))
(expanded-netmodule-name (expand-file-name netmodule-name ".")))
(if (file-exists-p expanded-netmodule-name)
(flymake-safe-delete-file expanded-netmodule-name)))
))
(flymake-simple-cleanup))
(defvar vbnet-flymake-vbc-arguments
(list "/t:module" "/nologo")
"A list of arguments to use with the vbc.exe
compiler, when using flymake with a
direct vbc.exe build for syntax checking purposes.")
(defun vbnet-split-string-respecting-quotes (s)
"splits a string into tokens, respecting double quotes
For example, the string 'This is \"a string\"' will be split into 3 tokens.
More pertinently, the string
'csc /t:module /R:\"c:\abba dabba\dooo\Foo.dll\"'
...will be split into 3 tokens.
This fn also removes quotes from the tokens that have them. This is for
compatibility with flymake and the process-start fn.
"
(let ((local-s s)
(my-re-1 "[^ \"]+\"[^\"]+\"\\|[^ \"]+")
(my-re-2 "\\([^ \"]+\\)\"\\([^\"]+\\)\"")
(tokens))
(while (string-match my-re-1 local-s)
(let ((token (match-string 0 local-s))
(remainder (substring local-s (match-end 0))))
(if (string-match my-re-2 token)
(setq token (concat (match-string 1 token) (match-string 2 token))))
(message "token: %s" token)
(setq tokens (append tokens (list token)))
(setq local-s remainder)))
tokens))
(defun vbnet-get-value-from-comments (marker-string line-limit)
"gets a string from the header comments in the current buffer.
This is used to extract the flymake command and the compile
command from the comments.
It looks for \"marker-string:\" and returns the string that
follows it, or returns nil if that string is not found.
eg, when marker-string is \"flymake-command\", and the following
string is found at the top of the buffer:
flymake-command: vbc.exe /r:Hallo.dll
...then this command will return the string
\"vbc.exe /r:Hallo.dll\"
"
(let (start search-limit found)
(save-excursion
(save-restriction
(widen)
(cond ((> line-limit 0)
(goto-char (setq start (point-min)))
(forward-line line-limit)
(setq search-limit (point)))
((< line-limit 0)
(goto-char (setq search-limit (point-max)))
(forward-line line-limit)
(setq start (point)))
(t
(setq start (point-min))
(setq search-limit (point-max))))))
(save-excursion
(save-restriction
(widen)
(let ((re-string
(concat "\\b" marker-string "[ \t]*:[ \t]*\\(.+\\)$")))
(if (and start
(< (goto-char start) search-limit)
(re-search-forward re-string search-limit 'move))
(buffer-substring-no-properties
(match-beginning 1)
(match-end 1))))))))
(defun vbnet-flymake-get-cmdline (source base-dir)
"Gets the cmd line for running a flymake session in a VB.NET buffer.
This gets called by flymake itself.
The fn looks in the buffer for a line that looks like:
flymake-command: <command goes here>
(It should be embedded into a comment)
Typically the command will be a line that runs nmake.exe,
msbuild.exe, or vbc.exe, with various options. It should
eventually run the VB.NET compiler, or something else that emits
error messages in the same form as the VB.NET compiler.
In general, you should use a target type of \"module\" (eg,
/t:module) to allow vbnet-flymake to clean up the products of the
build.
See `vbnet-cmd-line-limit' for a way to restrict where vbnet-mode
will search for the command.
If this string is not found, then this fn will fallback to a
generated vbc.exe command.
"
(let ((explicitly-specified-command
(vbnet-get-value-from-comments "flymake-command" vbnet-cmd-line-limit)))
(cond
(explicitly-specified-command
(let ((tokens (vbnet-split-string-respecting-quotes explicitly-specified-command)))
(list (car tokens) (append (cdr tokens) (list flymake-temp-source-file-name)))))
(t
(list "vbc.exe"
(append (vbnet-flymake-get-final-vbc-arguments
vbnet-flymake-vbc-arguments)
(list source)))))))
(defun vbnet-flymake-get-final-vbc-arguments (initial-arglist)
"Gets the arguments used by VBC.exe for flymake runs.
This may inject a /t:module into an arglist, where it is not present.
It burps if a different /t argument is found.
"
(interactive)
(let ((args initial-arglist)
arg
(found nil))
(while args
(setq arg (car args))
(cond
((string-equal arg "/t:module") (setq found t))
((string-match "^/t:" arg)
(setq found t)
(message "vbnet-mode: WARNING /t: option present in arglist, and not /t:module; fix this.")))
(setq args (cdr args)))
(setq args
(if found
initial-arglist
(append (list "/t:module") initial-arglist)))
(if (called-interactively-p 'any)
(message "result: %s" (prin1-to-string args)))
args))
(defvar vbnet-flymake-vbc-error-pattern
"^[ \t]*\\([_A-Za-z0-9][^(]+\\.[Vv][Bb]\\)(\\([0-9]+\\)) : \\(\\(error\\|warning\\) BC[0-9]+:[ \t\n]*\\(.+\\)\\)"
"Regexp to find error messages in the output of VBC.exe. Used for Flymake integration.")
(defun vbnet-flymake-install ()
"Change flymake variables and fns to work with VBNET.
This fn does four things:
1. add a VB.NET entry to the flymake-allowed-file-name-masks,
or replace it if it already exists.
2. add a VB.NET entry to flymake-err-line-patterns.
This isn't strictly necessary because of item #4.
3. redefine flymake-process-sentinel to NOT check the process
exit status. Vbc.exe returns a 1 when there are compile-time
errors. This causes flymake to disable itself, which we don't want.
4. provide advice to flymake-parse-line, specifically set up for
VB.NET buffers. This allows optimized searching for errors
in vbc.exe output.
It's necessary to invoke this function only once, not every time
vbnet-mode is invoked. vbnet-mode uses `eval-after-load' to call it
once, after flymake has loaded.
"
(flymake-log 2 "vbnet-flymake-install")
(let* ((key "\\.vb\\'")
(vbnet-entry (assoc key flymake-allowed-file-name-masks)))
(if vbnet-entry
(setcdr vbnet-entry '(vbnet-flymake-init vbnet-flymake-cleanup))
(add-to-list
'flymake-allowed-file-name-masks
(list key 'vbnet-flymake-init 'vbnet-flymake-cleanup))))
(add-to-list
'flymake-err-line-patterns
(list vbnet-flymake-vbc-error-pattern 1 2 nil 3))
(defun flymake-process-sentinel (process event)
"Sentinel for syntax check buffers."
(when (memq (process-status process) '(signal exit))
(let* ((exit-status (process-exit-status process))
(command (process-command process))
(source-buffer (process-buffer process))
(cleanup-f (flymake-get-cleanup-function (buffer-file-name source-buffer))))
(flymake-log 2 "process %d exited with code %d"
(process-id process) exit-status)
(condition-case err
(progn
(flymake-log 3 "cleaning up using %s" cleanup-f)
(when (buffer-live-p source-buffer)
(with-current-buffer source-buffer
(funcall cleanup-f)))
(delete-process process)
(setq flymake-processes (delq process flymake-processes))
(when (buffer-live-p source-buffer)
(with-current-buffer source-buffer
(flymake-parse-residual)
(flymake-post-syntax-check 0 command)
(setq flymake-is-running nil))))
(error
(let ((err-str (format "Error in process sentinel for buffer %s: %s"
source-buffer (error-message-string err))))
(flymake-log 0 err-str)
(with-current-buffer source-buffer
(setq flymake-is-running nil))))))))
(defadvice flymake-parse-line (around
flymake-for-vbnet-parse-line-patch
activate compile)
(if (string-match "\\.[Vv][Bb]$" (file-relative-name buffer-file-name))
(let (raw-file-name
e-text
result
(pattern (list vbnet-flymake-vbc-error-pattern 1 2 nil 3))
(line-no 0)
(err-type "e"))
(if (string-match (car pattern) line)
(let* ((file-idx (nth 1 pattern))
(line-idx (nth 2 pattern))
(e-idx (nth 4 pattern)))
(flymake-log 3 "parse line: fx=%s lx=%s ex=%s"
file-idx line-idx e-idx)
(setq raw-file-name (if file-idx (match-string file-idx line) nil))
(setq line-no (if line-idx (string-to-number (match-string line-idx line)) 0))
(setq e-text (if e-idx
(match-string e-idx line)
(flymake-patch-e-text (substring line (match-end 0)))))
(or e-text (setq e-text "<no error text>"))
(if (and e-text (string-match "^[wW]arning" e-text))
(setq err-type "w"))
(flymake-log 3 "parse line: fx=%s/%s lin=%s/%s text=%s"
file-idx raw-file-name
line-idx line-no
e-text)
(setq ad-return-value
(flymake-ler-make-ler raw-file-name line-no err-type e-text nil nil))
)))
ad-do-it)))
(defun vbnet-guess-compile-command ()
"set `compile-command' intelligently depending on the
current buffer, or the contents of the current directory.
"
(interactive)
(set (make-local-variable 'compile-command)
(cond
((or (file-expand-wildcards "*.csproj" t)
(file-expand-wildcards "*.vcproj" t)
(file-expand-wildcards "*.vbproj" t)
(file-expand-wildcards "*.shfbproj" t)
(file-expand-wildcards "*.sln" t))
"msbuild ")
(buffer-file-name
(let ((filename (file-name-nondirectory buffer-file-name)))
(cond
((string-equal (substring buffer-file-name -3) ".vb")
(let ((explicit-compile-command
(vbnet-get-value-from-comments "compile" vbnet-cmd-line-limit)))
(or explicit-compile-command
(concat "nmake "
(file-name-sans-extension filename)
".exe"))))
(t
(concat "nmake "
(file-name-sans-extension filename)
".exe")))))
(t
"nmake "))))
(defun vbnet-invoke-compile-interactively ()
"fn to wrap the `compile' function. This simply
checks to see if `compile-command' has been previously set, and
if not, invokes `vbnet-guess-compile-command' to set the value.
Then it invokes the `compile' function, interactively.
The effect is to guess the compile command only once, per buffer.
I tried doing this with advice attached to the `compile'
function, but because of the interactive nature of the fn, it
didn't work the way I wanted it to. So this fn should be bound to
the key sequence the user likes for invoking compile, like ctrl-c
ctrl-e.
"
(interactive)
(cond
((not (boundp 'vbnet-local-compile-command-has-been-set))
(vbnet-guess-compile-command)
(set (make-local-variable 'vbnet-local-compile-command-has-been-set) t)))
(call-interactively 'compile))
(eval-after-load "compile"
'(progn
(let ((e1
'(msvbc
"^[ \t]*\\([-_A-Za-z0-9][^\n(]*\\.vb\\)(\\([0-9]+\\)) ?: +\\(error\\|warning\\) BC[0-9]+:"
1 2 nil)))
(add-to-list 'compilation-error-regexp-alist-alist e1)
(add-to-list 'compilation-error-regexp-alist (car e1)))))
(defun vbnet-mode ()
"A mode for editing Microsoft Visual Basic .NET programs.
This is version 1.5 of the mode.
This mode features automatic indentation of VB.NET syntax, font
locking, keyword capitalization, integration with compile.el,
integration with flymake.el, integration with ya-snippet.el, and
some minor convenience functions.
As for those functions, here are some points of interest:
`vbnet-mode-indent' - customizable variable setting the indent size,
in spaces. The default is 4.
`vbnet-mark-defun' marks the current function, if there is one.
`vbnet-split-line' splits the current line at point, and inserts a
continuation character.
`vbnet-join-continued-lines' does the converse.
`vbnet-new-sub' - inserts a subroutine template into the buffer at
point.
`vbnet-moveto-beginning-of-defun'
`vbnet-moveto-end-of-defun'
`vbnet-moveto-beginning-of-block'
`vbnet-moveto-end-of-block'
Functions to move within the VB.NET buffer. The first two
move to the beginning and end, respectively, of a Function
or Sub. The latter two move to the beginning and end,
respectively, of the innermost containing block, whatever it
is - a Function, Sub, Struct, Enum, While, etc.
`vbnet-close-current-block' - intelligently closes a block, For
example, it inserts \"End Class\" when invoked if point is
after a Class declaration. This fn is naive: it
will insert an \"End Class\" even if an \"End Class\" is present
on the next line.
Consult the documentation for each of these functions for more
information.
For the syntax highlighting, it does not (yet?) support in-line
XML syntax, nor LINQ syntax.
Here's a summary of the key bindings:
\\{vbnet-mode-map}"
(interactive)
(kill-all-local-variables)
(use-local-map vbnet-mode-map)
(setq major-mode 'vbnet-mode)
(setq mode-name "VB.NET")
(set-syntax-table vbnet-mode-syntax-table)
(add-hook 'local-write-file-hooks 'vbnet-untabify)
(setq local-abbrev-table vbnet-mode-abbrev-table)
(if vbnet-capitalize-keywords-p
(progn
(make-local-variable 'pre-abbrev-expand-hook)
(add-hook 'pre-abbrev-expand-hook 'vbnet-pre-abbrev-expand-hook)
(abbrev-mode 1)))
(make-local-variable 'comment-start)
(setq comment-start "' ")
(make-local-variable 'comment-start-skip)
(setq comment-start-skip "'+ *")
(make-local-variable 'comment-column)
(setq comment-column 40)
(make-local-variable 'comment-end)
(setq comment-end "")
(make-local-variable 'indent-line-function)
(setq indent-line-function 'vbnet-indent-line)
(set (make-local-variable 'beginning-of-defun-function)
'vbnet-moveto-beginning-of-defun)
(set (make-local-variable 'end-of-defun-function)
'vbnet-moveto-end-of-defun)
(add-hook 'find-file-hooks 'vbnet-load-associated-files)
(local-set-key "\C-x\C-e" 'vbnet-invoke-compile-interactively)
(run-hooks 'vbnet-mode-hook)
(if vbnet-want-imenu
(progn
(setq imenu-create-index-function 'vbnet-imenu-create-index-function)
(imenu-add-menubar-index)))
(if vbnet-want-fontification
(vbnet-enable-font-lock))
(eval-after-load "yasnippet"
(if vbnet-want-yasnippet-fixup
(vbnet-fixup-yasnippet)))
(eval-after-load "flymake"
'(progn
(if vbnet-want-flymake-fixup
(vbnet-flymake-install))))
)
(provide 'vbnet-mode)