]> git.rkrishnan.org Git - .emacs.d.git/blob - emacs/basic-mode.el
70f58c69f8fb95652a556ffdfbe54d1cf9a1f656
[.emacs.d.git] / emacs / basic-mode.el
1 ;; basic-mode.el --- A mode for editing Visual Basic programs.
2
3 ;; Copyright (C) 1996, Fred White <fwhite@world.std.com>
4
5 ;; Author: Fred White <fwhite@world.std.com>
6 ;; Version: 1.0 (April 18, 1996)
7 ;; Keywords: languages basic
8
9 ;; LCD Archive Entry:
10 ;; basic-mode|Fred White|fwhite@world.std.com|
11 ;; A mode for editing Visual Basic programs.|
12 ;; 18-Apr-96|1.0|~/modes/basic-mode.el.Z|
13
14 ;; This file is NOT part of GNU Emacs but the same permissions apply.
15 ;;
16 ;; GNU Emacs  is free software;  you can redistribute it and/or modify
17 ;; it under the terms of  the GNU General  Public License as published
18 ;; by  the Free Software  Foundation;  either version  2, or (at  your
19 ;; option) any later version.
20 ;;
21 ;; GNU  Emacs is distributed  in the hope that  it will be useful, but
22 ;; WITHOUT    ANY  WARRANTY;  without even the     implied warranty of
23 ;; MERCHANTABILITY or FITNESS FOR A  PARTICULAR PURPOSE.  See the  GNU
24 ;; General Public License for more details.
25 ;;
26 ;; You should have received  a copy of  the GNU General Public License
27 ;; along with GNU Emacs; see  the file COPYING.  If  not, write to the
28 ;; Free Software Foundation, 675  Mass Ave, Cambridge, MA 02139,  USA.
29 ;; This  program  is free  software;  you  can  redistribute it and/or
30 ;; modify it  under  the terms of the  GNU  General Public License  as
31 ;; published by the Free Software  Foundation; either version 2 of the
32 ;; License, or (at your option) any later version.
33
34
35 ;; Purpose of this package:
36 ;;  This is a mode for editing programs written in The World's Most
37 ;;  Successful Programming Language.  It features automatic
38 ;;  indentation, font locking, keyword capitalization, and some minor
39 ;;  convenience functions.
40
41 ;; Installation instructions
42 ;;  Put basic-mode.el somewhere in your path, compile it, and add the
43 ;;  following to your init file:
44
45 ;;  (autoload 'basic-mode "basic-mode" "Basic mode." t)
46 ;;  (setq auto-mode-alist (append '(("\\.\\(frm\\|bas\\|cls\\)$" . 
47 ;;                                  basic-mode)) auto-mode-alist))
48
49 ;; Of course, under Windows 3.1, you'll have to name this file
50 ;; something shorter than basic-mode.el
51
52
53
54 ;; Known bugs:
55 ;;  Doesn't know about ":" separated stmts
56 ;;  Doesn't know about single-line IF stmts
57
58
59 ;; todo:
60 ;;  fwd/back-compound-statement
61 ;;  completion over OCX methods and properties.
62 ;;  ensure Then at the end of IF statements.
63 ;;  IDE integration
64 ;;  etc.
65
66
67 (provide 'basic-mode)
68
69 (defvar basic-xemacs-p (string-match "XEmacs\\|Lucid" (emacs-version)))
70 (defvar basic-winemacs-p (string-match "Win-Emacs" (emacs-version)))
71
72 ;; Variables you may want to customize.
73 (defvar basic-mode-indent 2 "*Default indentation per nesting level")
74 (defvar basic-fontify-p t "*Whether to fontify Basic buffers.")
75 (defvar basic-capitalize-keywords-p t
76   "*Whether to capitalize BASIC keywords.")
77 (defvar basic-wild-files "*.frm *.bas *.cls"
78   "*Wildcard pattern for BASIC source files")
79 (defvar basic-ide-pathname nil
80   "*The full pathname of your Visual Basic exe file, if any.")
81
82
83 (defvar basic-keywords-to-highlight
84   '("Dim" "If" "Then" "Else" "ElseIf" "End If")
85   "*A list of keywords to highlight in Basic mode, or T, meaning all keywords")
86
87 (defvar basic-defn-templates
88   (list "Public Sub ()\nEnd Sub\n\n"
89         "Public Function () As Variant\nEnd Function\n\n"
90         "Public Property Get ()\nEnd Property\n\n")
91   "*List of function templates though which basic-new-sub cycles.")
92
93
94
95 (defvar basic-mode-syntax-table nil)
96 (if basic-mode-syntax-table
97     ()
98   (setq basic-mode-syntax-table (make-syntax-table))
99   (modify-syntax-entry ?\' "\<" basic-mode-syntax-table) ; Comment starter
100   (modify-syntax-entry ?\n ">" basic-mode-syntax-table)
101   (modify-syntax-entry ?\\ "w" basic-mode-syntax-table)
102   (modify-syntax-entry ?_ "w" basic-mode-syntax-table))
103
104
105 (defvar basic-mode-map nil)
106 (if basic-mode-map
107     ()
108   (setq basic-mode-map (make-sparse-keymap))
109   (define-key basic-mode-map "\t" 'basic-indent-line)
110   (define-key basic-mode-map "\r" 'basic-newline-and-indent)
111   (define-key basic-mode-map "\M-\C-a" 'basic-beginning-of-defun)
112   (define-key basic-mode-map "\M-\C-e" 'basic-end-of-defun)
113   (define-key basic-mode-map "\M-\C-h" 'basic-mark-defun)
114   (define-key basic-mode-map "\M-\C-\\" 'basic-indent-region)
115   (define-key basic-mode-map "\M-q" 'basic-fill-or-indent)
116   (if basic-xemacs-p
117       (progn
118         (if basic-winemacs-p
119             (define-key basic-mode-map '(control C) 'basic-start-ide))
120         (define-key basic-mode-map "\M-G" 'basic-grep)
121         (define-key basic-mode-map '(meta backspace) 'backward-kill-word)
122         (define-key basic-mode-map '(control meta /) 'basic-new-sub))))
123
124
125 ;; These abbrevs are valid only in a code context.
126 (defvar basic-mode-abbrev-table nil)
127
128 (defvar basic-mode-hook ())
129
130
131 ;; Is there a way to case-fold all regexp matches?
132
133 (defconst basic-defun-start-regexp
134   (concat
135    "^[ \t]*\\([Pp]ublic \\|[Pp]rivate \\|[Ss]tatic \\)*"
136    "\\([Ss]ub\\|[Ff]unction\\|[Pp]roperty +[GgSsLl]et\\|[Tt]ype\\)"
137    "[ \t]+\\(\\w+\\)[ \t]*(?"))
138
139 (defconst basic-defun-end-regexp
140   "^[ \t]*[Ee]nd \\([Ss]ub\\|[Ff]unction\\|[Pp]roperty\\|[Tt]ype\\)")
141
142
143 ;; Includes the compile-time #if variation.
144 (defconst basic-if-regexp "^[ \t]*#?[Ii]f")
145 (defconst basic-else-regexp "^[ \t]*#?[Ee]lse\\([Ii]f\\)?")
146 (defconst basic-endif-regexp "[ \t]*#?[Ee]nd[ \t]*[Ii]f")
147
148 (defconst basic-continuation-regexp "^.*\\_[ \t]*$")
149 (defconst basic-label-regexp "^[ \t]*[a-zA-Z0-9_]+:$")
150
151 (defconst basic-select-regexp "^[ \t]*[Ss]elect[ \t]+[Cc]ase")
152 (defconst basic-case-regexp "^[ \t]*[Cc]ase")
153 (defconst basic-select-end-regexp "^[ \t]*[Ee]nd[ \t]+[Ss]elect")
154
155 (defconst basic-for-regexp "^[ \t]*[Ff]or")
156 (defconst basic-next-regexp "^[ \t]*[Nn]ext")
157
158 (defconst basic-do-regexp "^[ \t]*[Dd]o")
159 (defconst basic-loop-regexp "^[ \t]*[Ll]oop")
160
161 (defconst basic-while-regexp "^[ \t]*[Ww]hile")
162 (defconst basic-wend-regexp "^[ \t]*[Ww]end")
163
164 (defconst basic-with-regexp "^[ \t]*[Ww]ith")
165 (defconst basic-end-with-regexp "^[ \t]*[Ee]nd[ \t]+[Ww]ith")
166
167 (defconst basic-blank-regexp "^[ \t]*$")
168 (defconst basic-comment-regexp "^[ \t]*\\s<.*$")
169
170
171 ;; This is some approximation of the set of reserved words in Visual Basic.
172 (defconst basic-all-keywords
173   '("Aggregate" "And" "App" "AppActivate" "Application" "Array" "As"
174     "Asc" "AscB" "Atn" "Beep" "BeginTrans" "ByVal" "CBool" "CByte" "CCur"
175     "CDate" "CDbl" "CInt" "CLng" "CSng" "CStr" "CVErr" "CVar" "Call"
176     "Case" "ChDir" "ChDrive" "Character" "Choose" "Chr" "ChrB"
177     "ClassModule" "Clipboard" "Close" "Collection" "Column" "Columns"
178     "Command" "CommitTrans" "CompactDatabase" "Component" "Components"
179     "Const" "Container" "Containers" "Cos" "CreateDatabase" "CreateObject"
180     "CurDir" "Currency" "DBEngine" "DDB" "Data" "Database" "Databases"
181     "Date" "DateAdd" "DateDiff" "DatePart" "DateSerial" "DateValue" "Day"
182     "Debug" "Declare" "Deftype" "DeleteSetting" "Dim" "Dir" "Do" "Domain"
183     "Double" "Dynaset" "EOF" "Each" "Else" "End" "Environ" "Erase" "Err"
184     "Error" "Exit" "Exp" "FV" "False" "Field" "Fields" "FileAttr"
185     "FileCopy" "FileDateTime" "FileLen" "Fix" "Font" "For" "Form"
186     "FormTemplate" "Format" "Forms" "FreeFile" "FreeLocks" "Function"
187     "Get" "GetAllSettings" "GetAttr" "GetObject" "GetSetting" "GoSub"
188     "GoTo" "Group" "Groups" "Hex" "Hour" "IIf" "IMEStatus" "IPmt" "IRR"
189     "If" "InStr" "Input" "Int" "Integer" "Is" "IsArray" "IsDate" "IsEmpty"
190     "IsError" "IsMissing" "IsNull" "IsNumeric" "IsObject" "Kill" "LBound"
191     "LCase" "LOF" "LSet" "LTrim" "Left" "Len" "Let" "Like" "Line" "Load"
192     "LoadPicture" "LoadResData" "LoadResPicture" "LoadResString" "Loc"
193     "Lock" "Log" "Long" "Loop" "MDIForm" "MIRR" "Me" "MenuItems"
194     "MenuLine" "Mid" "Minute" "MkDir" "Month" "MsgBox" "NPV" "NPer" "Name"
195     "New" "Next" "Now" "Oct" "On" "Open" "OpenDatabase" "Operator"
196     "Option" "PPmt" "PV" "Parameter" "Parameters" "Partition" "Picture"
197     "Pmt" "Print" "Printer" "Printers" "Private" "ProjectTemplate"
198     "Properties" "Public" "Put" "QBColor" "QueryDef" "QueryDefs" "RGB"
199     "RSet" "RTrim" "Randomize" "Rate" "ReDim" "Recordset" "Recordsets"
200     "RegisterDatabase" "Relation" "Relations" "Rem" "RepairDatabase"
201     "Reset" "Resume" "Return" "Right" "RmDir" "Rnd" "Rollback" "RowBuffer"
202     "SLN" "SYD" "SavePicture" "SaveSetting" "Screen" "Second" "Seek"
203     "SelBookmarks" "Select" "SelectedComponents" "SendKeys" "Set"
204     "SetAttr" "SetDataAccessOption" "SetDefaultWorkspace" "Sgn" "Shell"
205     "Sin" "Single" "Snapshot" "Space" "Spc" "Sqr" "Static" "Stop" "Str"
206     "StrComp" "StrConv" "String" "Sub" "SubMenu" "Switch" "Tab" "Table"
207     "TableDef" "TableDefs" "Tan" "Then" "Time" "TimeSerial" "TimeValue"
208     "Timer" "To" "Trim" "True" "Type" "TypeName" "UBound" "UCase" "Unload"
209     "Unlock" "Val" "VarType" "Verb" "Weekday" "Wend"
210     "While" "Width" "With" "Workspace" "Workspaces" "Write" "Year"))
211
212
213 (defun basic-word-list-regexp (keys)
214   (let ((re "\\b\\(")
215         (key nil))
216     (while keys
217       (setq key (car keys)
218             keys (cdr keys))
219       (setq re (concat re key (if keys "\\|" ""))))
220     (concat re "\\)\\b")))
221
222 (defun basic-keywords-to-highlight ()
223   (if (eq basic-keywords-to-highlight t)
224       basic-all-keywords
225     basic-keywords-to-highlight))
226
227
228 (defvar basic-font-lock-keywords
229   (list
230    ;; Names of functions.
231    (list basic-defun-start-regexp 3 'font-lock-function-name-face)
232
233    ;; Statement labels
234    (cons basic-label-regexp 'font-lock-keyword-face)
235
236    ;; Case values
237    ;; String-valued cases get font-lock-string-face regardless.
238    (list "^[ \t]*[Cc]ase[ \t]+\\([^'\n]+\\)" 1 'font-lock-keyword-face t)
239
240    ;; Any keywords you like.
241    (cons (basic-word-list-regexp (basic-keywords-to-highlight))
242          'font-lock-keyword-face)))
243
244
245
246 (defun basic-mode ()
247   "A mode for editing Microsoft Visual Basic programs.
248 Features automatic  indentation, font locking, keyword capitalization, 
249 and some minor convenience functions.
250 Commands:
251 \\{basic-mode-map}"
252   (interactive)
253   (kill-all-local-variables)
254   (use-local-map basic-mode-map)
255   (setq major-mode 'basic-mode)
256   (setq mode-name "Basic")
257   (set-syntax-table basic-mode-syntax-table)
258
259   (add-hook 'write-file-hooks 'basic-untabify)
260
261   (setq local-abbrev-table basic-mode-abbrev-table)
262   (if basic-capitalize-keywords-p
263       (progn
264         (make-local-variable 'pre-abbrev-expand-hook)
265         (add-hook 'pre-abbrev-expand-hook 'basic-pre-abbrev-expand-hook)
266         (abbrev-mode 1)))
267
268
269   (make-local-variable 'comment-start)
270   (setq comment-start "' ")
271   (make-local-variable 'comment-start-skip)
272   (setq comment-start-skip "'+ *")
273   (make-local-variable 'comment-column)
274   (setq comment-column 40)
275   (make-local-variable 'comment-end)
276   (setq comment-end "")
277
278   (make-local-variable 'indent-line-function)
279   (setq indent-line-function 'basic-indent-line)
280
281   (make-local-variable 'font-lock-keywords)
282   (setq font-lock-keywords basic-font-lock-keywords)
283
284   (if basic-fontify-p
285       (font-lock-mode 1))
286
287   (run-hooks 'basic-mode-hook))
288
289
290 (defun basic-construct-keyword-abbrev-table ()
291   (if basic-mode-abbrev-table
292       nil
293     (let ((words basic-all-keywords)
294           (word nil)
295           (list nil))
296       (while words
297         (setq word (car words)
298               words (cdr words))
299         (setq list (cons (list (downcase word) word) list)))
300
301       (define-abbrev-table 'basic-mode-abbrev-table list))))
302
303 (basic-construct-keyword-abbrev-table)
304
305
306 (defun basic-in-code-context-p ()
307   (if (fboundp 'buffer-syntactic-context) ; XEmacs function.
308       (null (buffer-syntactic-context))
309     ;; Attempt to simulate buffer-syntactic-context
310     ;; I don't know how reliable this is.
311     (let* ((beg (save-excursion
312                   (beginning-of-line)
313                   (point)))
314            (list
315             (parse-partial-sexp beg (point))))
316       (and (null (nth 3 list))          ; inside string.
317            (null (nth 4 list))))))      ; inside cocmment
318
319 (defun basic-pre-abbrev-expand-hook ()
320   ;; Allow our abbrevs only in a code context.
321   (setq local-abbrev-table
322         (if (basic-in-code-context-p)
323             basic-mode-abbrev-table)))
324          
325         
326
327 (defun basic-newline-and-indent (&optional count)
328   "Insert a newline, updating indentation."
329   (interactive)
330   (expand-abbrev)
331   (basic-indent-line)
332   (call-interactively 'newline-and-indent))
333   
334 (defun basic-beginning-of-defun ()
335   (interactive)
336   (re-search-backward basic-defun-start-regexp))
337
338 (defun basic-end-of-defun ()
339   (interactive)
340   (re-search-forward basic-defun-end-regexp))
341
342 (defun basic-mark-defun ()
343   (interactive)
344   (beginning-of-line)
345   (basic-end-of-defun)
346   (set-mark (point))
347   (basic-beginning-of-defun)
348   (if basic-xemacs-p
349       (zmacs-activate-region)))
350
351 (defun basic-indent-defun ()
352   (interactive)
353   (save-excursion
354     (basic-mark-defun)
355     (call-interactively 'basic-indent-region)))
356
357
358 (defun basic-fill-long-comment ()
359   "Fills block of comment lines around point."
360   ;; Derived from code in ilisp-ext.el.
361   (interactive)
362   (save-excursion
363     (beginning-of-line)
364     (let ((comment-re "^[ \t]*\\s<+[ \t]*"))
365       (if (looking-at comment-re)
366           (let ((fill-prefix
367                  (buffer-substring
368                   (progn (beginning-of-line) (point))
369                   (match-end 0))))
370
371             (while (and (not (bobp))
372                         (looking-at basic-comment-regexp))
373               (forward-line -1))
374             (if (not (bobp)) (forward-line 1))
375
376             (let ((start (point)))
377
378               ;; Make all the line prefixes the same.
379               (while (and (not (eobp))
380                           (looking-at comment-re))
381                 (replace-match fill-prefix)
382                 (forward-line 1))
383
384               (if (not (eobp))
385                   (beginning-of-line))
386
387               ;; Fill using fill-prefix
388               (fill-region-as-paragraph start (point))))))))
389
390
391 (defun basic-fill-or-indent ()
392   "Fill long comment around point, if any, else indent current definition."
393   (interactive)
394   (cond ((save-excursion
395            (beginning-of-line)
396            (looking-at basic-comment-regexp))
397          (basic-fill-long-comment))
398         (t
399          (basic-indent-defun))))
400
401
402 (defun basic-new-sub ()
403   "Insert template for a new subroutine. Repeat to cycle through alternatives."
404   (interactive)
405   (beginning-of-line)
406   (let ((templates (cons basic-blank-regexp
407                          basic-defn-templates))
408         (tem nil)
409         (bound (point)))
410     (while templates
411       (setq tem (car templates)
412             templates (cdr templates))
413       (cond ((looking-at tem)
414              (replace-match (or (car templates)
415                                 ""))
416              (setq templates nil))))
417
418     (search-backward "()" bound t)))
419
420
421 (defun basic-untabify ()
422   "Do not allow any tabs into the file"
423   (if (eq major-mode 'basic-mode)
424       (untabify (point-min) (point-max)))
425   nil)
426
427 (defun basic-default-tag ()
428   (if (and (not (bobp))
429            (save-excursion
430              (backward-char 1)
431              (looking-at "\\w")))
432       (backward-word 1))
433   (let ((s (point))
434         (e (save-excursion
435              (forward-word 1)
436              (point))))
437     (buffer-substring s e)))
438
439 (defun basic-grep (tag)
440   "Search BASIC source files in current directory for tag."
441   (interactive
442    (list (let* ((def (basic-default-tag))
443                 (tag (read-string
444                       (format "Grep for [%s]: " def))))
445            (if (string= tag "") def tag))))
446
447   (grep (format "grep -n %s %s" tag basic-wild-files)))
448
449
450
451 (defun basic-start-ide ()
452   "Start Visual Basic (or your favorite IDE, (after Emacs, of course))
453 on the project file in the current directory.
454 Note: it's not a good idea to leave Visual Basic running while you
455 are editing in emacs, since Visual Basic has no provision for reloading
456 changed files."
457   (interactive)
458   (let (file)
459     (cond ((not (fboundp 'win-exec))
460            (error "Not available"))
461           ((null basic-ide-pathname)
462            (error "No pathname set for Visual Basic. See basic-ide-pathname"))
463           ((setq file (car (directory-files (pwd) t "\\.vbp")))
464            (iconify-emacs)
465            (win-exec basic-ide-pathname 'win-show-normal file))
466           (t
467            (error "No project file found.")))))
468
469
470
471 ;;; Indentation-related stuff.
472
473 (defun basic-indent-region (start end)
474   "Perform basic-indent-line on each line in region."
475   (interactive "r")
476   (save-excursion
477     (goto-char start)
478     (beginning-of-line)
479     (while (and (not (eobp))
480                 (< (point) end))
481       (if (not (looking-at basic-blank-regexp))
482           (basic-indent-line))
483       (forward-line 1)))
484
485   (cond ((fboundp 'zmacs-deactivate-region)
486          (zmacs-deactivate-region))
487         ((fboundp 'deactivate-mark)
488          (deactivate-mark))))
489
490
491
492 (defun basic-previous-line-of-code ()
493   (if (not (bobp))
494       (forward-line -1))        ; previous-line depends on goal column
495   (while (and (not (bobp))
496               (or (looking-at basic-blank-regexp)
497                   (looking-at basic-comment-regexp)))
498     (forward-line -1)))
499
500
501 (defun basic-find-original-statement ()
502   ;; If the current line is a continuation from the previous, move
503   ;; back to the original stmt.
504   (let ((here (point)))
505     (basic-previous-line-of-code)
506     (while (and (not (bobp))
507                 (looking-at basic-continuation-regexp))
508       (setq here (point))
509       (basic-previous-line-of-code))
510     (goto-char here)))
511
512 (defun basic-find-matching-stmt (open-regexp close-regexp)
513   ;; Searching backwards
514   (let ((level 0))
515     (while (and (>= level 0) (not (bobp)))
516       (basic-previous-line-of-code)
517       (basic-find-original-statement)
518       (cond ((looking-at close-regexp)
519              (setq level (+ level 1)))
520             ((looking-at open-regexp)
521              (setq level (- level 1)))))))
522
523 (defun basic-find-matching-if ()
524   (basic-find-matching-stmt basic-if-regexp basic-endif-regexp))
525
526 (defun basic-find-matching-select ()
527   (basic-find-matching-stmt basic-select-regexp basic-select-end-regexp))
528
529 (defun basic-find-matching-for ()
530   (basic-find-matching-stmt basic-for-regexp basic-next-regexp))
531
532 (defun basic-find-matching-do ()
533   (basic-find-matching-stmt basic-do-regexp basic-loop-regexp))
534
535 (defun basic-find-matching-while ()
536   (basic-find-matching-stmt basic-while-regexp basic-wend-regexp))
537
538 (defun basic-find-matching-with ()
539   (basic-find-matching-stmt basic-with-regexp basic-end-with-regexp))
540
541
542 (defun basic-calculate-indent ()
543   (let ((original-point (point)))
544     (save-excursion
545       (beginning-of-line)
546       ;; Some cases depend only on where we are now.
547       (cond ((or (looking-at basic-defun-start-regexp)
548                  (looking-at basic-label-regexp)
549                  (looking-at basic-defun-end-regexp))
550              0)
551
552             ;; The outdenting stmts, which simply match their original.
553             ((or (looking-at basic-else-regexp)
554                  (looking-at basic-endif-regexp))
555              (basic-find-matching-if)
556              (current-indentation))
557
558             ;; All the other matching pairs act alike.
559             ((looking-at basic-next-regexp) ; for/next
560              (basic-find-matching-for)
561              (current-indentation))
562
563             ((looking-at basic-loop-regexp) ; do/loop
564              (basic-find-matching-do)
565              (current-indentation))
566
567             ((looking-at basic-wend-regexp) ; while/wend
568              (basic-find-matching-while)
569              (current-indentation))
570
571             ((looking-at basic-with-regexp) ; with/end with
572              (basic-find-matching-with)
573              (current-indentation))
574
575             ((looking-at basic-select-end-regexp) ; select case/end select
576              (basic-find-matching-select)
577              (current-indentation))
578
579             ;; A case of a select is somewhat special.
580             ((looking-at basic-case-regexp)
581              (basic-find-matching-select)
582              (+ (current-indentation) basic-mode-indent))
583
584             (t
585              ;; Other cases which depend on the previous line.
586              (basic-previous-line-of-code)
587
588              ;; Skip over label lines, which always have 0 indent.
589              (while (looking-at basic-label-regexp)
590                (basic-previous-line-of-code))
591
592              (cond 
593               ((looking-at basic-continuation-regexp)
594                (basic-find-original-statement)
595                ;; Indent continuation line under matching open paren,
596                ;; or else one word in.
597                (let* ((orig-stmt (point))
598                       (matching-open-paren
599                        (condition-case ()
600                            (save-excursion
601                              (goto-char original-point)
602                              (beginning-of-line)
603                              (backward-up-list 1)
604                              ;; Only if point is now w/in cont. block.
605                              (if (<= orig-stmt (point))
606                                  (current-column)))
607                          (error nil))))
608                  (cond (matching-open-paren
609                         (1+ matching-open-paren))
610                        (t
611                         ;; Else, after first word on original line.
612                         (back-to-indentation)
613                         (forward-word 1)
614                         (while (looking-at "[ \t]")
615                           (forward-char 1))
616                         (current-column)))))
617               (t
618                (basic-find-original-statement)
619                (let ((indent (current-indentation)))
620                  ;; All the various +indent regexps.
621                  (cond ((looking-at basic-defun-start-regexp)
622                         (+ indent basic-mode-indent))
623
624                        ((or (looking-at basic-if-regexp)
625                             (looking-at basic-else-regexp))
626                         (+ indent basic-mode-indent))
627
628                        ((or (looking-at basic-select-regexp)
629                             (looking-at basic-case-regexp))
630                         (+ indent basic-mode-indent))
631                         
632                        ((or (looking-at basic-do-regexp)
633                             (looking-at basic-for-regexp)
634                             (looking-at basic-while-regexp)
635                             (looking-at basic-with-regexp))
636                         (+ indent basic-mode-indent))
637
638                        (t
639                         ;; By default, just copy indent from prev line.
640                         indent))))))))))
641
642 (defun basic-indent-to-column (col)
643   (let* ((bol (save-excursion
644                 (beginning-of-line)
645                 (point)))
646          (point-in-whitespace
647           (<= (point) (+ bol (current-indentation))))
648          (blank-line-p
649           (save-excursion
650             (beginning-of-line)
651             (looking-at basic-blank-regexp))))
652
653     (cond ((/= col (current-indentation))
654            (save-excursion
655              (beginning-of-line)
656              (back-to-indentation)
657              (delete-region bol (point))
658              (indent-to col))))
659
660     ;; If point was in the whitespace, move back-to-indentation.
661     (cond (blank-line-p
662            (end-of-line))
663           (point-in-whitespace
664            (back-to-indentation)))))
665
666 (defun basic-indent-line ()
667   "Indent current line for BASIC"
668   (interactive)
669   (basic-indent-to-column (basic-calculate-indent)))