1;;; nsi-mode.el --- Major mode for editing and compiling Nsi programs (nsis scripts)
3;; This is just a cannibalization of python mode because I wanted to have
4;; something to edit and compile nsis scripts in emacs (Hans van Dam)
5;; Compilation of the current buffer is performed using Ctrl-f9 (just like in the nsis-editor).
7;; Copyright (C) 2005 Tim Peters
9;; Author: 1995-1998 Barry A. Warsaw
10;; 1992-1994 Tim Peters
11;; Maintainer: nsi-mode@nsi.org
13;; Keywords: nsi languages oop
15(defconst nsi-version "3.105"
16 "`nsi-mode' version number.")
18;; This software is provided as-is, without express or implied
19;; warranty. Permission to use, copy, modify, distribute or sell this
20;; software, without fee, for any purpose and by any individual or
21;; organization, is hereby granted, provided that the above copyright
22;; notice and this paragraph appear in all copies.
28;; This is a major mode for editing Nsi programs. It was developed
29;; by Tim Peters after an original idea by Michael A. Guravage. Tim
30;; subsequently left the net; in 1995, Barry Warsaw inherited the mode
31;; and is the current maintainer. Tim's now back but disavows all
32;; responsibility for the mode. Smart Tim :-)
34;; This version of nsi-mode.el is no longer compatible with Emacs
35;; 18. I am striving to maintain compatibility with the X/Emacs 19
36;; lineage but as time goes on that becomes more and more difficult.
37;; I current recommend that you upgrade to the latest stable released
38;; version of your favorite branch: Emacs 20.3 or better, or XEmacs
39;; 20.4 or better (XEmacs 21.0 is in beta testing as of this writing
40;; 27-Oct-1998 appears to work fine with this version of
41;; nsi-mode.el). Even Windows users should be using at least
42;; NTEmacs 20.3, and XEmacs 21.0 will work very nicely on Windows when
45;; FOR MORE INFORMATION:
47;; For more information on installing nsi-mode.el, especially with
48;; respect to compatibility information, please see
50;; http://www.nsi.org/emacs/nsi-mode/
52;; This site also contains links to other packages that you might find
53;; useful, such as pdb interfaces, OO-Browser links, etc.
57;; To submit bug reports, use C-c C-b. Please include a complete, but
58;; concise code sample and a recipe for reproducing the bug. Send
59;; suggestions and other comments to nsi-mode@nsi.org.
61;; When in a Nsi mode buffer, do a C-h m for more help. It's
62;; doubtful that a texinfo manual would be very useful, but if you
63;; want to contribute one, I'll certainly accept it!
67;; - Better integration with pdb.py and gud-mode for debugging.
68;; - Rewrite according to GNU Emacs Lisp standards.
69;; - have nsi-execute-region on indented code act as if the region is
70;; left justified. Avoids syntax errors.
71;; - add a nsi-goto-block-down, bound to C-c C-d
79 (if (not (and (condition-case nil
82 ;; Stock Emacs 19.34 has a broken/old Custom library
83 ;; that does more harm than good. Fortunately, it is
85 (fboundp 'defcustom)))
86 (error "STOP! STOP! STOP! STOP!
88The Custom library was not found or is out of date. A more current
89version is required. Please download and install the latest version
90of the Custom library from:
92 <http://www.dina.kvl.dk/~abraham/custom/>
94See the Nsi Mode home page for details:
96 <http://www.nsi.org/emacs/nsi-mode>
101;; user definable variables
102;; vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
105 "Support for the Nsi programming language, <http://www.nsi.org/>"
109(defcustom nsi-nsi-command "nsi"
110 "*Shell command used to start nsi compiler."
114(defcustom nsi-default-interpreter 'cnsi
115 "*Which Nsi interpreter is used by default.
116The value for this variable can be either `cnsi' or `jnsi'.
119ote that this variable is consulted only the first time that a Nsi
120mode buffer is visited during an Emacs session. After that, use
121\\[nsi-toggle-shells] to change the interpreter shell."
122 :type '(choice (const :tag "Nsi (a.k.a. CNsi)" cnsi)
123 (const :tag "JNsi" jnsi))
127(defcustom nsi-indent-offset 4
128 "*Amount of offset per level of indentation.
129`\\[nsi-guess-indent-offset]' can usually guess a good value when
130you're editing someone else's Nsi code."
134(defcustom nsi-smart-indentation t
135 "*Should `nsi-mode' try to automagically set some indentation variables?
136When this variable is non-nil, two things happen when a buffer is set
139 1. `nsi-indent-offset' is guessed from existing code in the buffer.
140 Only guessed values between 2 and 8 are considered. If a valid
141 guess can't be made (perhaps because you are visiting a new
142 file), then the value in `nsi-indent-offset' is used.
144 2. `indent-tabs-mode' is turned off if `nsi-indent-offset' does not
145 equal `tab-width' (`indent-tabs-mode' is never turned on by
146 Nsi mode). This means that for newly written code, tabs are
147 only inserted in indentation if one tab is one indentation
148 level, otherwise only spaces are used.
150Note that both these settings occur *after* `nsi-mode-hook' is run,
151so if you want to defeat the automagic configuration, you must also
152set `nsi-smart-indentation' to nil in your `nsi-mode-hook'."
156(defcustom nsi-align-multiline-strings-p t
157 "*Flag describing how multi-line triple quoted strings are aligned.
158When this flag is non-nil, continuation lines are lined up under the
159preceding line's indentation. When this flag is nil, continuation
160lines are aligned to column zero."
161 :type '(choice (const :tag "Align under preceding line" t)
162 (const :tag "Align to column zero" nil))
165(defcustom nsi-block-comment-prefix ";;"
166 "*String used by \\[comment-region] to comment out a block of code.
167This should follow the convention for non-indenting comment lines so
168that the indentation commands won't get confused (i.e., the string
169should be of the form `#x...' where `x' is not a blank or a tab, and
170`...' is arbitrary). However, this string should not end in whitespace."
174(defcustom nsi-honor-comment-indentation t
175 "*Controls how comment lines influence subsequent indentation.
177When nil, all comment lines are skipped for indentation purposes, and
178if possible, a faster algorithm is used (i.e. X/Emacs 19 and beyond).
180When t, lines that begin with a single `;' are a hint to subsequent
181line indentation. If the previous line is such a comment line (as
182opposed to one that starts with `nsi-block-comment-prefix'), then its
183indentation is used as a hint for this line's indentation. Lines that
184begin with `nsi-block-comment-prefix' are ignored for indentation
187When not nil or t, comment lines that begin with a `#' are used as
188indentation hints, unless the comment character is in column zero."
190 (const :tag "Skip all comment lines (fast)" nil)
191 (const :tag "Single ; `sets' indentation for next line" t)
192 (const :tag "Single ; `sets' indentation except at column zero"
197(defcustom nsi-temp-directory
198 (let ((ok '(lambda (x)
200 (setq x (expand-file-name x)) ; always true
204 (or (funcall ok (getenv "TMPDIR"))
205 (funcall ok "/usr/tmp")
209 "Couldn't find a usable temp directory -- set `nsi-temp-directory'")))
210 "*Directory used for temp files created by a *Nsi* process.
211By default, the first directory from this list that exists and that you
212can write into: the value (if any) of the environment variable TMPDIR,
213/usr/tmp, /tmp, or the current directory."
217(defcustom nsi-beep-if-tab-change t
218 "*Ring the bell if `tab-width' is changed.
219If a comment of the form
221 \t# vi:set tabsize=<number>:
223is found before the first code line when the file is entered, and the
224current value of (the general Emacs variable) `tab-width' does not
225equal <number>, `tab-width' is set to <number>, a message saying so is
226displayed in the echo area, and if `nsi-beep-if-tab-change' is non-nil
227the Emacs bell is also rung as a warning."
231(defcustom nsi-ask-about-save t
232 "If not nil, ask about which buffers to save before executing some code.
233Otherwise, all modified buffers are saved without asking."
237(defcustom nsi-backspace-function 'backward-delete-char-untabify
238 "*Function called by `nsi-electric-backspace' when deleting backwards."
242(defcustom nsi-delete-function 'delete-char
243 "*Function called by `nsi-electric-delete' when deleting forwards."
247(defcustom nsi-imenu-show-method-args-p nil
248 "*Controls echoing of arguments of functions & methods in the Imenu buffer.
249When non-nil, arguments are printed."
252(make-variable-buffer-local 'nsi-indent-offset)
255(defvar nsi-master-file nil
256 "If non-nil, execute the named file instead of the buffer's file.
257The intent is to allow you to set this variable in the file's local
258variable section, e.g.:
261 # nsi-master-file: \"master.py\"
264so that typing \\[nsi-execute-buffer] in that buffer executes the named
265master file instead of the buffer's file. If the file name has a
266relative path, the value of variable `default-directory' for the
267buffer is prepended to come up with a file name.")
268(make-variable-buffer-local 'nsi-master-file)
272;; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
273;; NO USER DEFINABLE VARIABLES BEYOND THIS POINT
275(defconst nsi-emacs-features
277 ;; NTEmacs 19.34.6 has a broken make-temp-name; it always returns
279 (let ((tmp1 (make-temp-name ""))
280 (tmp2 (make-temp-name "")))
281 (if (string-equal tmp1 tmp2)
282 (push 'broken-temp-names features)))
283 ;; return the features
285 "A list of features extant in the Emacs you are using.
286There are many flavors of Emacs out there, with different levels of
287support for features needed by `nsi-mode'.")
289(defvar nsi-font-lock-keywords
290 (let ((kw1 (mapconcat 'identity
291 '("Section" "Function" "FunctionEnd" "SectionEnd"
297 (cons (concat "\\b\\(" kw1 "\\)\\b[ \n\t(]") 1)
298 ;; block introducing keywords with immediately following colons.
299 ;; Yes "except" is in both lists.
301 '("\\bclass[ \t]+\\([a-zA-Z_]+[a-zA-Z0-9_]*\\)"
302 1 font-lock-type-face)
304 '("\\bdef[ \t]+\\([a-zA-Z_]+[a-zA-Z0-9_]*\\)"
305 1 font-lock-function-name-face)
307 "Additional expressions to highlight in Nsi mode.")
308(put 'nsi-mode 'font-lock-defaults '(nsi-font-lock-keywords))
310;; have to bind nsi-file-queue before installing the kill-emacs-hook
313(defconst nsi-stringlit-re
315 ;; These fail if backslash-quote ends the string (not worth
316 ;; fixing?). They precede the short versions so that the first two
317 ;; quotes don't look like an empty short string.
319 ;; (maybe raw), long single quoted triple quoted strings (SQTQ),
320 ;; with potential embedded single quotes
321 "[rR]?'''[^']*\\(\\('[^']\\|''[^']\\)[^']*\\)*'''"
323 ;; (maybe raw), long double quoted triple quoted strings (DQTQ),
324 ;; with potential embedded double quotes
325 "[rR]?\"\"\"[^\"]*\\(\\(\"[^\"]\\|\"\"[^\"]\\)[^\"]*\\)*\"\"\""
327 "[rR]?'\\([^'\n\\]\\|\\\\.\\)*'" ; single-quoted
329 "[rR]?\"\\([^\"\n\\]\\|\\\\.\\)*\"" ; double-quoted
331 "Regular expression matching a Nsi string literal.")
333(defconst nsi-continued-re
334 ;; This is tricky because a trailing backslash does not mean
335 ;; continuation if it's in a comment
337 "\\(" "[^;'\"\n\\]" "\\|" nsi-stringlit-re "\\)*"
339 "Regular expression matching Nsi backslash continuation lines.")
341(defconst nsi-blank-or-comment-re "[ \t]*\\($\\|;\\)"
342 "Regular expression matching a blank or comment line.")
344(defconst nsi-outdent-re
345 (concat "\\(" (mapconcat 'identity
347 "except\\(\\s +.*\\)?:"
352 "Regular expression matching statements to be dedented one level.")
354(defconst nsi-block-closing-keywords-re
355 "\\(return\\|raise\\|break\\|continue\\|pass\\)"
356 "Regular expression matching keywords which typically close a block.")
358(defconst nsi-no-outdent-re
363 "except\\(\\s +.*\\)?:"
368 (concat nsi-block-closing-keywords-re "[ \t\n]")
372 "Regular expression matching lines not to dedent after.")
374(defconst nsi-defun-start-re
375 "^\\([ \t]*\\)def[ \t]+\\([a-zA-Z_0-9]+\\)\\|\\(^[a-zA-Z_0-9]+\\)[ \t]*="
376 ;; If you change this, you probably have to change nsi-current-defun
377 ;; as well. This is only used by nsi-current-defun to find the name
379 "Regular expression matching a function, method, or variable assignment.")
381(defconst nsi-class-start-re "^class[ \t]*\\([a-zA-Z_0-9]+\\)"
382 ;; If you change this, you probably have to change nsi-current-defun
383 ;; as well. This is only used by nsi-current-defun to find the name
385 "Regular expression for finding a class name.")
387(defconst nsi-traceback-line-re
388 "[ \t]+File \"\\([^\"]+\\)\", line \\([0-9]+\\)"
389 "Regular expression that describes tracebacks.")
393;; Major mode boilerplate
395;; define a mode-specific abbrev table for those who use such things
396(defvar nsi-mode-abbrev-table nil
397 "Abbrev table in use in `nsi-mode' buffers.")
398(define-abbrev-table 'nsi-mode-abbrev-table nil)
400(defvar nsi-mode-hook nil
401 "*Hook called by `nsi-mode'.")
403;; In previous version of nsi-mode.el, the hook was incorrectly
404;; called nsi-mode-hook, and was not defvar'd. Deprecate its use.
405(and (fboundp 'make-obsolete-variable)
406 (make-obsolete-variable 'nsi-mode-hook 'nsi-mode-hook))
408(defvar nsi-mode-map ()
409 "Keymap used in `nsi-mode' buffers.")
412 (setq nsi-mode-map (make-sparse-keymap))
414 ;; indentation level modifiers
415 ;; subprocess commands
416 (define-key nsi-mode-map [C-f9] 'nsi-execute-buffer)
417 ;; Caution! Enter here at your own risk. We are trying to support
418 ;; several behaviors and it gets disgusting. :-( This logic ripped
419 ;; largely from CC Mode.
421 ;; In XEmacs 19, Emacs 19, and Emacs 20, we use this to bind
422 ;; backwards deletion behavior to DEL, which both Delete and
423 ;; Backspace get translated to. There's no way to separate this
424 ;; behavior in a clean way, so deal with it! Besides, it's been
425 ;; this way since the dawn of time.
426 (if (not (boundp 'delete-key-deletes-forward))
427 (define-key nsi-mode-map "\177" 'nsi-electric-backspace)
428 ;; However, XEmacs 20 actually achieved enlightenment. It is
429 ;; possible to sanely define both backward and forward deletion
430 ;; behavior under X separately (TTYs are forever beyond hope, but
431 ;; who cares? XEmacs 20 does the right thing with these too).
432 (define-key nsi-mode-map [delete] 'nsi-electric-delete)
433 (define-key nsi-mode-map [backspace] 'nsi-electric-backspace))
434 ;; Separate M-BS from C-M-h. The former should remain
435 ;; backward-kill-word.
436 ;; stuff that is `standard' but doesn't interface well with
437 ;; nsi-mode, which forces us to rebind to special commands
438 (define-key nsi-mode-map "\C-xnd" 'nsi-narrow-to-defun)
440 (define-key nsi-mode-map "\C-c\C-b" 'nsi-submit-bug-report)
441 (define-key nsi-mode-map "\C-c\C-v" 'nsi-version)
442 ;; shadow global bindings for newline-and-indent w/ the nsi- version.
443 ;; BAW - this is extremely bad form, but I'm not going to change it
445 (mapcar #'(lambda (key)
446 (define-key nsi-mode-map key 'nsi-newline-and-indent))
447 (where-is-internal 'newline-and-indent))
448 ;; Force RET to be nsi-newline-and-indent even if it didn't get
449 ;; mapped by the above code. motivation: Emacs' default binding for
450 ;; RET is `newline' and C-j is `newline-and-indent'. Most Nsieers
451 ;; expect RET to do a `nsi-newline-and-indent' and any Emacsers who
452 ;; dislike this are probably knowledgeable enough to do a rebind.
453 ;; However, we do *not* change C-j since many Emacsers have already
454 ;; swapped RET and C-j and they don't want C-j bound to `newline' to
456 (define-key nsi-mode-map "\C-m" 'nsi-newline-and-indent)
459(defvar nsi-mode-output-map nil
460 "Keymap used in *Nsi Output* buffers.")
461(if nsi-mode-output-map
463 (setq nsi-mode-output-map (make-sparse-keymap))
464 (define-key nsi-mode-output-map [button2] 'nsi-mouseto-exception)
465 (define-key nsi-mode-output-map "\C-c\C-c" 'nsi-goto-exception)
466 ;; TBD: Disable all self-inserting keys. This is bogus, we should
467 ;; really implement this as *Nsi Output* buffer being read-only
468 (mapcar #' (lambda (key)
469 (define-key nsi-mode-output-map key
470 #'(lambda () (interactive) (beep))))
471 (where-is-internal 'self-insert-command))
474(defvar nsi-mode-syntax-table nil
475 "Syntax table used in `nsi-mode' buffers.")
476(if nsi-mode-syntax-table
478 (setq nsi-mode-syntax-table (make-syntax-table))
479 (modify-syntax-entry ?\( "()" nsi-mode-syntax-table)
480 (modify-syntax-entry ?\) ")(" nsi-mode-syntax-table)
481 (modify-syntax-entry ?\[ "(]" nsi-mode-syntax-table)
482 (modify-syntax-entry ?\] ")[" nsi-mode-syntax-table)
483 (modify-syntax-entry ?\{ "(}" nsi-mode-syntax-table)
484 (modify-syntax-entry ?\} "){" nsi-mode-syntax-table)
485 ;; Add operator symbols misassigned in the std table
486 (modify-syntax-entry ?\$ "." nsi-mode-syntax-table)
487 (modify-syntax-entry ?\% "." nsi-mode-syntax-table)
488 (modify-syntax-entry ?\& "." nsi-mode-syntax-table)
489 (modify-syntax-entry ?\* "." nsi-mode-syntax-table)
490 (modify-syntax-entry ?\+ "." nsi-mode-syntax-table)
491 (modify-syntax-entry ?\- "." nsi-mode-syntax-table)
492 (modify-syntax-entry ?\/ "." nsi-mode-syntax-table)
493 (modify-syntax-entry ?\< "." nsi-mode-syntax-table)
494 (modify-syntax-entry ?\= "." nsi-mode-syntax-table)
495 (modify-syntax-entry ?\> "." nsi-mode-syntax-table)
496 (modify-syntax-entry ?\| "." nsi-mode-syntax-table)
497 ;; For historical reasons, underscore is word class instead of
498 ;; symbol class. GNU conventions say it should be symbol class, but
499 ;; there's a natural conflict between what major mode authors want
500 ;; and what users expect from `forward-word' and `backward-word'.
501 ;; Guido and I have hashed this out and have decided to keep
502 ;; underscore in word class. If you're tempted to change it, try
503 ;; binding M-f and M-b to nsi-forward-into-nomenclature and
504 ;; nsi-backward-into-nomenclature instead. This doesn't help in all
505 ;; situations where you'd want the different behavior
506 ;; (e.g. backward-kill-word).
507 (modify-syntax-entry ?\_ "w" nsi-mode-syntax-table)
508 ;; Both single quote and double quote are string delimiters
509 (modify-syntax-entry ?\' "\"" nsi-mode-syntax-table)
510 (modify-syntax-entry ?\" "\"" nsi-mode-syntax-table)
511 ;; backquote is open and close paren
512 (modify-syntax-entry ?\` "$" nsi-mode-syntax-table)
513 ;; comment delimiters
514 (modify-syntax-entry ?\# "<" nsi-mode-syntax-table)
515 (modify-syntax-entry ?\n ">" nsi-mode-syntax-table)
522(defmacro nsi-safe (&rest body)
523 "Safely execute BODY, return nil if an error occurred."
524 (` (condition-case nil
528(defsubst nsi-keep-region-active ()
529 "Keep the region active in XEmacs."
530 ;; Ignore byte-compiler warnings you might see. Also note that
531 ;; FSF's Emacs 19 does it differently; its policy doesn't require us
532 ;; to take explicit action.
533 (and (boundp 'zmacs-region-stays)
534 (setq zmacs-region-stays t)))
536(defsubst nsi-highlight-line (from to file line)
538 ((fboundp 'make-extent)
540 (let ((e (make-extent from to)))
541 (set-extent-property e 'mouse-face 'highlight)
542 (set-extent-property e 'nsi-exc-info (cons file line))
543 (set-extent-property e 'keymap nsi-mode-output-map)))
545 ;; Emacs -- Please port this!
549;; Menu definitions, only relevent if you have the easymenu.el package
550;; (standard in the latest Emacs 19 and XEmacs 19 distributions).
553This menu will get created automatically if you have the `easymenu'
554package. Note that the latest X/Emacs releases contain this package.")
556(and (nsi-safe (require 'easymenu) t)
558 nsi-menu nsi-mode-map "Nsi Mode menu"
560 ["Execute buffer" nsi-execute-buffer t]
561 ["Describe mode" nsi-describe-mode t]
567(defvar nsi-imenu-class-regexp
568 (concat ; <<classes>>
570 "^[ \t]*" ; newline and maybe whitespace
571 "\\(class[ \t]+[a-zA-Z0-9_]+\\)" ; class name
572 ; possibly multiple superclasses
573 "\\([ \t]*\\((\\([a-zA-Z0-9_,. \t\n]\\)*)\\)?\\)"
574 "[ \t]*:" ; and the final :
577 "Regexp for Nsi classes for use with the Imenu package."
580(defvar nsi-imenu-method-regexp
581 (concat ; <<methods and functions>>
583 "^[ \t]*" ; new line and maybe whitespace
584 "\\(def[ \t]+" ; function definitions start with def
585 "\\([a-zA-Z0-9_]+\\)" ; name is here
586 ; function arguments...
587;; "[ \t]*(\\([-+/a-zA-Z0-9_=,\* \t\n.()\"'#]*\\))"
588 "[ \t]*(\\([^:#]*\\))"
590 "[ \t]*:" ; and then the :
591 "\\)" ; >>methods and functions<<
593 "Regexp for Nsi methods/functions for use with the Imenu package."
596(defvar nsi-imenu-method-no-arg-parens '(2 8)
597 "Indices into groups of the Nsi regexp for use with Imenu.
599Using these values will result in smaller Imenu lists, as arguments to
600functions are not listed.
602See the variable `nsi-imenu-show-method-args-p' for more
605(defvar nsi-imenu-method-arg-parens '(2 7)
606 "Indices into groups of the Nsi regexp for use with imenu.
607Using these values will result in large Imenu lists, as arguments to
610See the variable `nsi-imenu-show-method-args-p' for more
613;; Note that in this format, this variable can still be used with the
614;; imenu--generic-function. Otherwise, there is no real reason to have
616(defvar nsi-imenu-generic-expression
619 nsi-imenu-class-regexp
621 nsi-imenu-method-regexp
623 nsi-imenu-method-no-arg-parens)
624 "Generic Nsi expression which may be used directly with Imenu.
625Used by setting the variable `imenu-generic-expression' to this value.
626Also, see the function \\[nsi-imenu-create-index] for a better
627alternative for finding the index.")
629;; These next two variables are used when searching for the Nsi
630;; class/definitions. Just saving some time in accessing the
631;; generic-nsi-expression, really.
632(defvar nsi-imenu-generic-regexp nil)
633(defvar nsi-imenu-generic-parens nil)
636(defun nsi-imenu-create-index-function ()
637 "Nsi interface function for the Imenu package.
638Finds all Nsi classes and functions/methods. Calls function
639\\[nsi-imenu-create-index-engine]. See that function for the details
641 (setq nsi-imenu-generic-regexp (car nsi-imenu-generic-expression)
642 nsi-imenu-generic-parens (if nsi-imenu-show-method-args-p
643 nsi-imenu-method-arg-parens
644 nsi-imenu-method-no-arg-parens))
645 (goto-char (point-min))
646 ;; Warning: When the buffer has no classes or functions, this will
647 ;; return nil, which seems proper according to the Imenu API, but
648 ;; causes an error in the XEmacs port of Imenu. Sigh.
649 (nsi-imenu-create-index-engine nil))
651(defun nsi-imenu-create-index-engine (&optional start-indent)
652 "Function for finding Imenu definitions in Nsi.
654Finds all definitions (classes, methods, or functions) in a Nsi
655file for the Imenu package.
657Returns a possibly nested alist of the form
659 (INDEX-NAME . INDEX-POSITION)
661The second element of the alist may be an alist, producing a nested
664 (INDEX-NAME . INDEX-ALIST)
666This function should not be called directly, as it calls itself
667recursively and requires some setup. Rather this is the engine for
668the function \\[nsi-imenu-create-index-function].
670It works recursively by looking for all definitions at the current
671indention level. When it finds one, it adds it to the alist. If it
672finds a definition at a greater indentation level, it removes the
673previous definition from the alist. In its place it adds all
674definitions found at the next indentation level. When it finds a
675definition that is less indented then the current level, it returns
676the alist it has created thus far.
678The optional argument START-INDENT indicates the starting indentation
679at which to continue looking for Nsi classes, methods, or
680functions. If this is not supplied, the function uses the indentation
681of the first definition found."
687 (class-paren (first nsi-imenu-generic-parens))
688 (def-paren (second nsi-imenu-generic-parens)))
690 (re-search-forward nsi-imenu-generic-regexp (point-max) t))
693 ;; used to set def-name to this value but generic-extract-name
694 ;; is new to imenu-1.14. this way it still works with
696 ;;(imenu--generic-extract-name nsi-imenu-generic-parens))
697 (let ((cur-paren (if (match-beginning class-paren)
698 class-paren def-paren)))
700 (buffer-substring-no-properties (match-beginning cur-paren)
701 (match-end cur-paren))))
703 (nsi-beginning-of-def-or-class 'either))
705 (setq cur-indent (current-indentation)))
706 ;; HACK: want to go to the next correct definition location. We
707 ;; explicitly list them here but it would be better to have them
710 (or (match-beginning class-paren)
711 (match-beginning def-paren)))
712 ;; if we don't have a starting indent level, take this one
714 (setq start-indent cur-indent))
715 ;; if we don't have class name yet, take this one
717 (setq prev-name def-name))
718 ;; what level is the next definition on? must be same, deeper
719 ;; or shallower indentation
721 ;; at the same indent level, add it to the list...
722 ((= start-indent cur-indent)
723 (push (cons def-name def-pos) index-alist))
724 ;; deeper indented expression, recurse
725 ((< start-indent cur-indent)
726 ;; the point is currently on the expression we're supposed to
727 ;; start on, so go back to the last expression. The recursive
728 ;; call will find this place again and add it to the correct
730 (re-search-backward nsi-imenu-generic-regexp (point-min) 'move)
731 (setq sub-method-alist (nsi-imenu-create-index-engine cur-indent))
733 ;; we put the last element on the index-alist on the start
734 ;; of the submethod alist so the user can still get to it.
735 (let ((save-elmt (pop index-alist)))
736 (push (cons prev-name
737 (cons save-elmt sub-method-alist))
739 ;; found less indented expression, we're done.
742 (re-search-backward nsi-imenu-generic-regexp (point-min) t)))
744 (setq prev-name def-name)
747 (re-search-forward nsi-imenu-generic-regexp
748 (point-max) 'move))))
749 (nreverse index-alist)))
754 "Major mode for editing Nsi files.
755To submit a problem report, enter `\\[nsi-submit-bug-report]' from a
756`nsi-mode' buffer. Do `\\[nsi-describe-mode]' for detailed
757documentation. To see what version of `nsi-mode' you are running,
758enter `\\[nsi-version]'.
760This mode knows about Nsi indentation, tokens, comments and
761continuation lines. Paragraphs are separated by blank lines only.
767nsi-indent-offset\t\tindentation increment
768nsi-block-comment-prefix\t\tcomment string used by `comment-region'
769nsi-nsi-command\t\tshell command to invoke Nsi interpreter
770nsi-temp-directory\t\tdirectory used for temp files (if needed)
771nsi-beep-if-tab-change\t\tring the bell if `tab-width' is changed"
773 ;; set up local variables
774 (kill-all-local-variables)
775 (make-local-variable 'font-lock-defaults)
776 (make-local-variable 'paragraph-separate)
777 (make-local-variable 'paragraph-start)
778 (make-local-variable 'require-final-newline)
779 (make-local-variable 'comment-start)
780 (make-local-variable 'comment-end)
781 (make-local-variable 'comment-start-skip)
782 (make-local-variable 'comment-column)
783 (make-local-variable 'comment-indent-function)
784 (make-local-variable 'indent-region-function)
785 (make-local-variable 'indent-line-function)
786 (make-local-variable 'add-log-current-defun-function)
788 (set-syntax-table nsi-mode-syntax-table)
789 (setq major-mode 'nsi-mode
791 local-abbrev-table nsi-mode-abbrev-table
792 font-lock-defaults '(nsi-font-lock-keywords)
793 paragraph-separate "^[ \t]*$"
794 paragraph-start "^[ \t]*$"
795 require-final-newline t
798 comment-start-skip "; *"
800 comment-indent-function 'nsi-comment-indent-function
801 indent-region-function 'nsi-indent-region
802 indent-line-function 'nsi-indent-line
803 ;; tell add-log.el how to find the current function/method/variable
804 add-log-current-defun-function 'nsi-current-defun
806 (use-local-map nsi-mode-map)
809 (easy-menu-add nsi-menu))
810 ;; Emacs 19 requires this
811 (if (boundp 'comment-multi-line)
812 (setq comment-multi-line nil))
813 ;; Install Imenu if available
814 (when (nsi-safe (require 'imenu))
815 (setq imenu-create-index-function #'nsi-imenu-create-index-function)
816 (setq imenu-generic-expression nsi-imenu-generic-expression)
817 (if (fboundp 'imenu-add-to-menubar)
818 (imenu-add-to-menubar (format "%s-%s" "IM" mode-name)))
820 ;; Run the mode hook. Note that nsi-mode-hook is deprecated.
822 (run-hooks 'nsi-mode-hook)
823 (run-hooks 'nsi-mode-hook))
824 ;; Now do the automagical guessing
825 (if nsi-smart-indentation
826 (let ((offset nsi-indent-offset))
827 ;; It's okay if this fails to guess a good value
828 (if (and (nsi-safe (nsi-guess-indent-offset))
829 (<= nsi-indent-offset 8)
830 (>= nsi-indent-offset 2))
831 (setq offset nsi-indent-offset))
832 (setq nsi-indent-offset offset)
833 ;; Only turn indent-tabs-mode off if tab-width !=
834 ;; nsi-indent-offset. Never turn it on, because the user must
835 ;; have explicitly turned it off.
836 (if (/= tab-width nsi-indent-offset)
837 (setq indent-tabs-mode nil))
839 ;; Set the default shell if not already set
840 (when (null nsi-which-shell)
841 (nsi-toggle-shells nsi-default-interpreter))
845;; electric characters
846(defun nsi-outdent-p ()
847 "Returns non-nil if the current line should dedent one level."
849 (and (progn (back-to-indentation)
850 (looking-at nsi-outdent-re))
851 ;; short circuit infloop on illegal construct
853 (progn (forward-line -1)
854 (nsi-goto-initial-line)
855 (back-to-indentation)
856 (while (or (looking-at nsi-blank-or-comment-re)
858 (backward-to-indentation 1))
859 (not (looking-at nsi-no-outdent-re)))
862(defun nsi-electric-colon (arg)
864In certain cases the line is dedented appropriately. If a numeric
865argument ARG is provided, that many colons are inserted
866non-electrically. Electric behavior is inhibited inside a string or
869 (self-insert-command (prefix-numeric-value arg))
870 ;; are we in a string or comment?
872 (let ((pps (parse-partial-sexp (save-excursion
873 (nsi-beginning-of-def-or-class)
876 (not (or (nth 3 pps) (nth 4 pps)))))
880 (indent (nsi-compute-indentation t)))
883 (= indent (save-excursion
884 (nsi-next-statement -1)
885 (nsi-compute-indentation t)))
887 (setq outdent nsi-indent-offset))
888 ;; Don't indent, only dedent. This assumes that any lines
889 ;; that are already dedented relative to
890 ;; nsi-compute-indentation were put there on purpose. It's
891 ;; highly annoying to have `:' indent for you. Use TAB, C-c
892 ;; C-l or C-c C-r to adjust. TBD: Is there a better way to
894 (if (< (current-indentation) indent) nil
897 (delete-horizontal-space)
898 (indent-to (- indent outdent))
902;; Nsi subprocess utilities and filters
903(defun nsi-execute-file (proc filename)
904 "Send to Nsi interpreter process PROC \"execfile('FILENAME')\".
905Make that process's buffer visible and force display. Also make
906comint believe the user typed this string so that
907`kill-output-from-shell' does The Right Thing."
908 (let ((curbuf (current-buffer))
909 (procbuf (process-buffer proc))
910; (comint-scroll-to-bottom-on-output t)
911 (msg (format "## working on region in file %s...\n" filename))
912 (cmd (format "execfile(r'%s')\n" filename)))
916 (goto-char (point-max))
917 (move-marker (process-mark proc) (point))
918 (funcall (process-filter proc) proc msg))
920 (process-send-string proc cmd)))
922;; (defun nsi-comint-output-filter-function (string)
923;; "Watch output for Nsi prompt and exec next file waiting in queue.
924;; This function is appropriate for `comint-output-filter-functions'."
925;; ;; TBD: this should probably use split-string
926;; (when (and (or (string-equal string ">>> ")
927;; (and (>= (length string) 5)
928;; (string-equal (substring string -5) "\n>>> ")))
930;; (nsi-safe (delete-file (car nsi-file-queue)))
931;; (setq nsi-file-queue (cdr nsi-file-queue))
933;; (let ((pyproc (get-buffer-process (current-buffer))))
934;; (nsi-execute-file pyproc (car nsi-file-queue))))
937(defun nsi-postprocess-output-buffer (buf)
938 "Highlight exceptions found in BUF.
939If an exception occurred return t, otherwise return nil. BUF must exist."
940 (let (line file bol err-p)
943 (beginning-of-buffer)
944 (while (re-search-forward nsi-traceback-line-re nil t)
945 (setq file (match-string 1)
946 line (string-to-int (match-string 2))
947 bol (nsi-point 'bol))
948 (nsi-highlight-line bol (nsi-point 'eol) file line)))
949 (when (and nsi-jump-on-exception line)
951 (nsi-jump-to-exception file line)
957;;; Subprocess commands
959;; only used when (memq 'broken-temp-names nsi-emacs-features)
960(defvar nsi-serial-number 0)
961(defvar nsi-exception-buffer nil)
962(defconst nsi-output-buffer "*Nsi Output*")
963(make-variable-buffer-local 'nsi-output-buffer)
965;; for toggling between CNsi and JNsi
966(defvar nsi-which-shell nil)
967(defvar nsi-which-bufname "Nsi")
968(make-variable-buffer-local 'nsi-which-shell)
969(make-variable-buffer-local 'nsi-which-args)
970(make-variable-buffer-local 'nsi-which-bufname)
972(defun nsi-toggle-shells (arg)
973 "Toggles between the CNsi and JNsi shells.
975With positive argument ARG (interactively \\[universal-argument]),
976uses the CNsi shell, with negative ARG uses the JNsi shell, and
977with a zero argument, toggles the shell.
979Programmatically, ARG can also be one of the symbols `cnsi' or
980`jnsi', equivalent to positive arg and negative arg respectively."
982 ;; default is to toggle
989 (if (string-equal nsi-which-bufname "Nsi")
992 ((equal arg 'cnsi) (setq arg 1))
993 ((equal arg 'jnsi) (setq arg -1)))
998 (setq nsi-which-shell nsi-nsi-command
999 nsi-which-bufname "Nsi"
1003 (setq nsi-which-shell nsi-jnsi-command
1004 nsi-which-bufname "JNsi"
1008 (message "Using the %s shell" msg)
1009 (setq nsi-output-buffer (format "*%s Output*" nsi-which-bufname))))
1012(defun nsi-shell (&optional argprompt)
1013 "Start an interactive Nsi interpreter in another window.
1014This is like Shell mode, except that Nsi is running in the window
1015instead of a shell. See the `Interactive Shell' and `Shell Mode'
1016sections of the Emacs manual for details, especially for the key
1017bindings active in the `*Nsi*' buffer.
1019With optional \\[universal-argument], the user is prompted for the
1020flags to pass to the Nsi interpreter. This has no effect when this
1021command is used to switch to an existing process, only when a new
1022process is started. If you use this, you will probably want to ensure
1023that the current arguments are retained (they will be included in the
1024prompt). This argument is ignored when this function is called
1025programmatically, or when running in Emacs 19.34 or older.
1027Note: You can toggle between using the CNsi interpreter and the
1028JNsi interpreter by hitting \\[nsi-toggle-shells]. This toggles
1029buffer local variables which control whether all your subshell
1030interactions happen to the `*JNsi*' or `*Nsi*' buffers (the
1031latter is the name used for the CNsi buffer).
1033Warning: Don't use an interactive Nsi if you change sys.ps1 or
1034sys.ps2 from their default values, or if you're running code that
1035prints `>>> ' or `... ' at the start of a line. `nsi-mode' can't
1036distinguish your output from Nsi's output, and assumes that `>>> '
1037at the start of a line is a prompt from Nsi. Similarly, the Emacs
1038Shell mode code assumes that both `>>> ' and `... ' at the start of a
1039line are Nsi prompts. Bad things can happen if you fool either
1042Warning: If you do any editing *in* the process buffer *while* the
1043buffer is accepting output from Nsi, do NOT attempt to `undo' the
1044changes. Some of the output (nowhere near the parts you changed!) may
1045be lost if you do. This appears to be an Emacs bug, an unfortunate
1046interaction between undo and process filters; the same problem exists in
1047non-Nsi process buffers using the default (Emacs-supplied) process
1050 ;; Set the default shell if not already set
1051 (when (null nsi-which-shell)
1052 (nsi-toggle-shells nsi-default-interpreter))
1053 (let ((args nsi-which-args))
1054 (when (and argprompt
1056 (fboundp 'split-string))
1057 ;; TBD: Perhaps force "-i" in the final list?
1058 (setq args (split-string
1059 (read-string (concat nsi-which-bufname
1062 (mapconcat 'identity nsi-which-args " ") " ")
1064 (switch-to-buffer-other-window
1065 (apply 'make-comint nsi-which-bufname nsi-which-shell nil args))
1066 (make-local-variable 'comint-prompt-regexp)
1067 (setq comint-prompt-regexp "^>>> \\|^[.][.][.] \\|^(pdb) ")
1068 (add-hook 'comint-output-filter-functions
1069 'nsi-comint-output-filter-function)
1070 (set-syntax-table nsi-mode-syntax-table)
1071 (use-local-map nsi-shell-map)
1074;; (defun nsi-clear-queue ()
1075;; "Clear the queue of temporary files waiting to execute."
1077;; (let ((n (length nsi-file-queue)))
1078;; (mapcar 'delete-file nsi-file-queue)
1079;; (setq nsi-file-queue nil)
1080;; (message "%d pending files de-queued." n)))
1083(defun nsi-execute-region (start end &optional async)
1084 "Execute the region in a Nsi interpreter.
1086The region is first copied into a temporary file (in the directory
1087`nsi-temp-directory'). If there is no Nsi interpreter shell
1088running, this file is executed synchronously using
1089`shell-command-on-region'. If the program is long running, use
1090\\[universal-argument] to run the command asynchronously in its own
1093When this function is used programmatically, arguments START and END
1094specify the region to execute, and optional third argument ASYNC, if
1095non-nil, specifies to run the command asynchronously in its own
1098If the Nsi interpreter shell is running, the region is execfile()'d
1099in that shell. If you try to execute regions too quickly,
1100`nsi-mode' will queue them up and execute them one at a time when
1101it sees a `>>> ' prompt from Nsi. Each time this happens, the
1102process buffer is popped into a window (if it's not already in some
1103window) so you can see it, and a comment of the form
1105 \t## working on region in file <name>...
1107is inserted at the end. See also the command `nsi-clear-queue'."
1108 (interactive "r\nP")
1110 (error "Region is empty"))
1111 (shell-command-on-region start end cmd nsi-output-buffer)
1114;; Code execution commands
1115(defun nsi-execute-buffer (&optional async)
1116 "Send the contents of the buffer to a Nsi interpreter.
1117If the file local variable `nsi-master-file' is non-nil, execute the
1118named file instead of the buffer's file.
1120If there is a *Nsi* process buffer it is used. If a clipping
1121restriction is in effect, only the accessible portion of the buffer is
1122sent. A trailing newline will be supplied if needed.
1124See the `\\[nsi-execute-region]' docs for an account of some
1125subtleties, including the use of the optional ASYNC argument."
1127 (let ((nsi-file-name (buffer-file-name)))
1129 (shell-command (concat "C:/Progra~1/nsis/makensisw.exe " nsi-file-name))))
1131(defun nsi-execute-import-or-reload (&optional async)
1132 "Import the current buffer's file in a Nsi interpreter.
1134If the file has already been imported, then do reload instead to get
1137If the file's name does not end in \".py\", then do execfile instead.
1139If the current buffer is not visiting a file, do `nsi-execute-buffer'
1142If the file local variable `nsi-master-file' is non-nil, import or
1143reload the named file instead of the buffer's file. The file may be
1144saved based on the value of `nsi-execute-import-or-reload-save-p'.
1146See the `\\[nsi-execute-region]' docs for an account of some
1147subtleties, including the use of the optional ASYNC argument.
1149This may be preferable to `\\[nsi-execute-buffer]' because:
1151 - Definitions stay in their module rather than appearing at top
1152 level, where they would clutter the global namespace and not affect
1153 uses of qualified names (MODULE.NAME).
1155 - The Nsi debugger gets line number information about the functions."
1157 ;; Check file local variable nsi-master-file
1159 (let* ((filename (expand-file-name nsi-master-file))
1160 (buffer (or (get-file-buffer filename)
1161 (find-file-noselect filename))))
1162 (set-buffer buffer)))
1163 (let ((file (buffer-file-name (current-buffer))))
1166 ;; Maybe save some buffers
1167 (save-some-buffers (not nsi-ask-about-save) nil)
1169 (if (string-match "\\.py$" file)
1170 (let ((f (file-name-sans-extension
1171 (file-name-nondirectory file))))
1172 (format "if globals().has_key('%s'):\n reload(%s)\nelse:\n import %s\n"
1174 (format "execfile(r'%s')\n" file))
1177 (nsi-execute-buffer async))))
1180(defun nsi-execute-def-or-class (&optional async)
1181 "Send the current function or class definition to a Nsi interpreter.
1183If there is a *Nsi* process buffer it is used.
1185See the `\\[nsi-execute-region]' docs for an account of some
1186subtleties, including the use of the optional ASYNC argument."
1189 (nsi-mark-def-or-class)
1190 ;; mark is before point
1191 (nsi-execute-region (mark) (point) async)))
1194(defun nsi-execute-string (string &optional async)
1195 "Send the argument STRING to a Nsi interpreter.
1197If there is a *Nsi* process buffer it is used.
1199See the `\\[nsi-execute-region]' docs for an account of some
1200subtleties, including the use of the optional ASYNC argument."
1201 (interactive "sExecute Nsi command: ")
1203 (set-buffer (get-buffer-create
1204 (generate-new-buffer-name " *Nsi Command*")))
1206 (nsi-execute-region (point-min) (point-max) async)))
1210(defun nsi-jump-to-exception (file line)
1211 "Jump to the Nsi code in FILE at LINE."
1212 (let ((buffer (cond ((string-equal file "<stdin>")
1213 (if (consp nsi-exception-buffer)
1214 (cdr nsi-exception-buffer)
1215 nsi-exception-buffer))
1216 ((and (consp nsi-exception-buffer)
1217 (string-equal file (car nsi-exception-buffer)))
1218 (cdr nsi-exception-buffer))
1219 ((nsi-safe (find-file-noselect file)))
1220 ;; could not figure out what file the exception
1221 ;; is pointing to, so prompt for it
1222 (t (find-file (read-file-name "Exception file: "
1225 (pop-to-buffer buffer)
1227 (if (not (eq major-mode 'nsi-mode))
1230 (message "Jumping to exception in file %s on line %d" file line)))
1232(defun nsi-mouseto-exception (event)
1233 "Jump to the code which caused the Nsi exception at EVENT.
1234EVENT is usually a mouse click."
1237 ((fboundp 'event-point)
1239 (let* ((point (event-point event))
1240 (buffer (event-buffer event))
1241 (e (and point buffer (extent-at point buffer 'nsi-exc-info)))
1242 (info (and e (extent-property e 'nsi-exc-info))))
1243 (message "Event point: %d, info: %s" point info)
1245 (nsi-jump-to-exception (car info) (cdr info)))
1247 ;; Emacs -- Please port this!
1250(defun nsi-goto-exception ()
1251 "Go to the line indicated by the traceback."
1256 (if (looking-at nsi-traceback-line-re)
1257 (setq file (match-string 1)
1258 line (string-to-int (match-string 2)))))
1260 (error "Not on a traceback line"))
1261 (nsi-jump-to-exception file line)))
1263(defun nsi-find-next-exception (start buffer searchdir errwhere)
1264 "Find the next Nsi exception and jump to the code that caused it.
1265START is the buffer position in BUFFER from which to begin searching
1266for an exception. SEARCHDIR is a function, either
1267`re-search-backward' or `re-search-forward' indicating the direction
1268to search. ERRWHERE is used in an error message if the limit (top or
1269bottom) of the trackback stack is encountered."
1273 (goto-char (nsi-point start))
1274 (if (funcall searchdir nsi-traceback-line-re nil t)
1275 (setq file (match-string 1)
1276 line (string-to-int (match-string 2)))))
1278 (nsi-jump-to-exception file line)
1279 (error "%s of traceback" errwhere))))
1281(defun nsi-down-exception (&optional bottom)
1282 "Go to the next line down in the traceback.
1283With \\[univeral-argument] (programmatically, optional argument
1284BOTTOM), jump to the bottom (innermost) exception in the exception
1287 (let* ((proc (get-process "Nsi"))
1288 (buffer (if proc "*Nsi*" nsi-output-buffer)))
1290 (nsi-find-next-exception 'eob buffer 're-search-backward "Bottom")
1291 (nsi-find-next-exception 'eol buffer 're-search-forward "Bottom"))))
1293(defun nsi-up-exception (&optional top)
1294 "Go to the previous line up in the traceback.
1295With \\[universal-argument] (programmatically, optional argument TOP)
1296jump to the top (outermost) exception in the exception stack."
1298 (let* ((proc (get-process "Nsi"))
1299 (buffer (if proc "*Nsi*" nsi-output-buffer)))
1301 (nsi-find-next-exception 'bob buffer 're-search-forward "Top")
1302 (nsi-find-next-exception 'bol buffer 're-search-backward "Top"))))
1306(defun nsi-electric-backspace (arg)
1307 "Delete preceding character or levels of indentation.
1308Deletion is performed by calling the function in `nsi-backspace-function'
1309with a single argument (the number of characters to delete).
1311If point is at the leftmost column, delete the preceding newline.
1313Otherwise, if point is at the leftmost non-whitespace character of a
1314line that is neither a continuation line nor a non-indenting comment
1315line, or if point is at the end of a blank line, this command reduces
1316the indentation to match that of the line that opened the current
1317block of code. The line that opened the block is displayed in the
1318echo area to help you keep track of where you are. With
1319\\[universal-argument] dedents that many blocks (but not past column
1322Otherwise the preceding character is deleted, converting a tab to
1323spaces if needed so that only a single column position is deleted.
1324\\[universal-argument] specifies how many characters to delete;
1327When used programmatically, argument ARG specifies the number of
1328blocks to dedent, or the number of characters to delete, as indicated
1331 (if (or (/= (current-indentation) (current-column))
1333 (nsi-continuation-line-p)
1334; (not nsi-honor-comment-indentation)
1335; (looking-at "#[^ \t\n]") ; non-indenting #
1337 (funcall nsi-backspace-function arg)
1338 ;; else indent the same as the colon line that opened the block
1339 ;; force non-blank so nsi-goto-block-up doesn't ignore it
1342 (let ((base-indent 0) ; indentation of base line
1343 (base-text "") ; and text of base line
1347 (condition-case nil ; in case no enclosing block
1349 (nsi-goto-block-up 'no-mark)
1350 (setq base-indent (current-indentation)
1351 base-text (nsi-suck-up-leading-text)
1354 (setq arg (1- arg))))
1355 (delete-char 1) ; toss the dummy character
1356 (delete-horizontal-space)
1357 (indent-to base-indent)
1359 (message "Closes block: %s" base-text)))))
1362(defun nsi-electric-delete (arg)
1363 "Delete preceding or following character or levels of whitespace.
1365The behavior of this function depends on the variable
1366`delete-key-deletes-forward'. If this variable is nil (or does not
1367exist, as in older Emacsen and non-XEmacs versions), then this
1368function behaves identically to \\[c-electric-backspace].
1370If `delete-key-deletes-forward' is non-nil and is supported in your
1371Emacs, then deletion occurs in the forward direction, by calling the
1372function in `nsi-delete-function'.
1374\\[universal-argument] (programmatically, argument ARG) specifies the
1375number of characters to delete (default is 1)."
1377 (if (or (and (fboundp 'delete-forward-p) ;XEmacs 21
1379 (and (boundp 'delete-key-deletes-forward) ;XEmacs 20
1380 delete-key-deletes-forward))
1381 (funcall nsi-delete-function arg)
1382 (nsi-electric-backspace arg)))
1384;; required for pending-del and delsel modes
1385(put 'nsi-electric-backspace 'delete-selection 'supersede) ;delsel
1386(put 'nsi-electric-backspace 'pending-delete 'supersede) ;pending-del
1387(put 'nsi-electric-delete 'delete-selection 'supersede) ;delsel
1388(put 'nsi-electric-delete 'pending-delete 'supersede) ;pending-del
1392(defun nsi-indent-line (&optional arg)
1393 "Fix the indentation of the current line according to Nsi rules.
1394With \\[universal-argument] (programmatically, the optional argument
1395ARG non-nil), ignore dedenting rules for block closing statements
1396(e.g. return, raise, break, continue, pass)
1398This function is normally bound to `indent-line-function' so
1399\\[indent-for-tab-command] will call it."
1401 (let* ((ci (current-indentation))
1402 (move-to-indentation-p (<= (current-column) ci))
1403 (need (nsi-compute-indentation (not arg))))
1404 ;; see if we need to dedent
1406 (setq need (- need nsi-indent-offset)))
1410 (delete-horizontal-space)
1412 (if move-to-indentation-p (back-to-indentation))))
1414(defun nsi-newline-and-indent ()
1415 "Strives to act like the Emacs `newline-and-indent'.
1416This is just `strives to' because correct indentation can't be computed
1417from scratch for Nsi code. In general, deletes the whitespace before
1418point, inserts a newline, and takes an educated guess as to how you want
1419the new line indented."
1421 (let ((ci (current-indentation)))
1422 (if (< ci (current-column)) ; if point beyond indentation
1423 (newline-and-indent)
1424 ;; else try to act like newline-and-indent "normally" acts
1427 (move-to-column ci))))
1429(defun nsi-compute-indentation (honor-block-close-p)
1430 "Compute Nsi indentation.
1431When HONOR-BLOCK-CLOSE-P is non-nil, statements such as `return',
1432`raise', `break', `continue', and `pass' force one level of
1436 (let* ((bod (nsi-point 'bod))
1437 (pps (parse-partial-sexp bod (point)))
1438 (boipps (parse-partial-sexp bod (nsi-point 'boi)))
1441 ;; are we inside a multi-line string or comment?
1442 ((or (and (nth 3 pps) (nth 3 boipps))
1443 (and (nth 4 pps) (nth 4 boipps)))
1445 (if (not nsi-align-multiline-strings-p) 0
1446 ;; skip back over blank & non-indenting comment lines
1447 ;; note: will skip a blank or non-indenting comment line
1448 ;; that happens to be a continuation line too
1449 (re-search-backward "^[ \t]*\\([^ \t\n;]\\|;[ \t\n]\\)" nil 'move)
1450 (back-to-indentation)
1452 ;; are we on a continuation line?
1453 ((nsi-continuation-line-p)
1454 (let ((startpos (point))
1455 (open-bracket-pos (nsi-nesting-level))
1456 endpos searching found state)
1457 (if open-bracket-pos
1459 ;; align with first item in list; else a normal
1460 ;; indent beyond the line with the open bracket
1461 (goto-char (1+ open-bracket-pos)) ; just beyond bracket
1462 ;; is the first list item on the same line?
1463 (skip-chars-forward " \t")
1464 (if (null (memq (following-char) '(?\n ?# ?\\)))
1465 ; yes, so line up with it
1467 ;; first list item on another line, or doesn't exist yet
1469 (while (and (< (point) startpos)
1470 (looking-at "[ \t]*[#\n\\\\]")) ; skip noise
1472 (if (and (< (point) startpos)
1475 (goto-char (1+ open-bracket-pos))
1476 (forward-comment (point-max))
1478 ;; again mimic the first list item
1479 (current-indentation)
1480 ;; else they're about to enter the first item
1481 (goto-char open-bracket-pos)
1482 (setq placeholder (point))
1483 (nsi-goto-initial-line)
1484 (nsi-goto-beginning-of-tqs
1485 (save-excursion (nth 3 (parse-partial-sexp
1486 placeholder (point)))))
1487 (+ (current-indentation) nsi-indent-offset))))
1489 ;; else on backslash continuation line
1491 (if (nsi-continuation-line-p) ; on at least 3rd line in block
1492 (current-indentation) ; so just continue the pattern
1493 ;; else started on 2nd line in block, so indent more.
1494 ;; if base line is an assignment with a start on a RHS,
1495 ;; indent to 2 beyond the leftmost "="; else skip first
1496 ;; chunk of non-whitespace characters on base line, + 1 more
1499 (setq endpos (point) searching t)
1500 (back-to-indentation)
1501 (setq startpos (point))
1502 ;; look at all "=" from left to right, stopping at first
1503 ;; one not nested in a list or string
1505 (skip-chars-forward "^=" endpos)
1506 (if (= (point) endpos)
1507 (setq searching nil)
1509 (setq state (parse-partial-sexp startpos (point)))
1510 (if (and (zerop (car state)) ; not in a bracket
1511 (null (nth 3 state))) ; & not in a string
1513 (setq searching nil) ; done searching in any case
1516 (eq (following-char) ?=)
1517 (memq (char-after (- (point) 2))
1519 (if (or (not found) ; not an assignment
1520 (looking-at "[ \t]*\\\\")) ; <=><spaces><backslash>
1522 (goto-char startpos)
1523 (skip-chars-forward "^ \t\n")))
1524 (1+ (current-column))))))
1526 ;; not on a continuation line
1527 ((bobp) (current-indentation))
1529 ;; Dfn: "Indenting comment line". A line containing only a
1530 ;; comment, but which is treated like a statement for
1531 ;; indentation calculation purposes. Such lines are only
1532 ;; treated specially by the mode; they are not treated
1533 ;; specially by the Nsi interpreter.
1535 ;; The rules for indenting comment lines are a line where:
1536 ;; - the first non-whitespace character is `#', and
1537 ;; - the character following the `#' is whitespace, and
1538 ;; - the line is dedented with respect to (i.e. to the left
1539 ;; of) the indentation of the preceding non-blank line.
1541 ;; The first non-blank line following an indenting comment
1542 ;; line is given the same amount of indentation as the
1543 ;; indenting comment line.
1545 ;; All other comment-only lines are ignored for indentation
1548 ;; Are we looking at a comment-only line which is *not* an
1549 ;; indenting comment line? If so, we assume that it's been
1550 ;; placed at the desired indentation, so leave it alone.
1551 ;; Indenting comment lines are aligned as statements down
1553 ((and (looking-at "[ \t]*#[^ \t\n]")
1554 ;; NOTE: this test will not be performed in older Emacsen
1555 (fboundp 'forward-comment)
1556 (<= (current-indentation)
1558 (forward-comment (- (point-max)))
1559 (current-indentation))))
1560 (current-indentation))
1562 ;; else indentation based on that of the statement that
1563 ;; precedes us; use the first line of that statement to
1564 ;; establish the base, in case the user forced a non-std
1565 ;; indentation for the continuation lines (if any)
1567 ;; skip back over blank & non-indenting comment lines note:
1568 ;; will skip a blank or non-indenting comment line that
1569 ;; happens to be a continuation line too. use fast Emacs 19
1570 ;; function if it's there.
1571 (if (and (eq nsi-honor-comment-indentation nil)
1572 (fboundp 'forward-comment))
1573 (forward-comment (- (point-max)))
1574 (let ((prefix-re (concat nsi-block-comment-prefix "[ \t]*"))
1577 (re-search-backward "^[ \t]*\\([^ \t\n#]\\|#\\)" nil 'move)
1578 (setq done (or (bobp)
1579 (and (eq nsi-honor-comment-indentation t)
1581 (back-to-indentation)
1582 (not (looking-at prefix-re))
1584 (and (not (eq nsi-honor-comment-indentation t))
1586 (back-to-indentation)
1587 (not (zerop (current-column)))))
1590 ;; if we landed inside a string, go to the beginning of that
1591 ;; string. this handles triple quoted, multi-line spanning
1593 (nsi-goto-beginning-of-tqs (nth 3 (parse-partial-sexp bod (point))))
1594 ;; now skip backward over continued lines
1595 (setq placeholder (point))
1596 (nsi-goto-initial-line)
1597 ;; we may *now* have landed in a TQS, so find the beginning of
1599 (nsi-goto-beginning-of-tqs
1600 (save-excursion (nth 3 (parse-partial-sexp
1601 placeholder (point)))))
1602 (+ (current-indentation)
1603 (if (nsi-statement-opens-block-p)
1605 (if (and honor-block-close-p (nsi-statement-closes-block-p))
1606 (- nsi-indent-offset)
1610(defun nsi-guess-indent-offset (&optional global)
1611 "Guess a good value for, and change, `nsi-indent-offset'.
1613By default, make a buffer-local copy of `nsi-indent-offset' with the
1614new value, so that other Nsi buffers are not affected. With
1615\\[universal-argument] (programmatically, optional argument GLOBAL),
1616change the global value of `nsi-indent-offset'. This affects all
1617Nsi buffers (that don't have their own buffer-local copy), both
1618those currently existing and those created later in the Emacs session.
1620Some people use a different value for `nsi-indent-offset' than you use.
1621There's no excuse for such foolishness, but sometimes you have to deal
1622with their ugly code anyway. This function examines the file and sets
1623`nsi-indent-offset' to what it thinks it was when they created the
1626Specifically, it searches forward from the statement containing point,
1627looking for a line that opens a block of code. `nsi-indent-offset' is
1628set to the difference in indentation between that line and the Nsi
1629statement following it. If the search doesn't succeed going forward,
1630it's tried again going backward."
1631 (interactive "P") ; raw prefix arg
1637 (nsi-goto-initial-line)
1638 (while (not (or found (eobp)))
1639 (when (and (re-search-forward ":[ \t]*\\($\\|[#\\]\\)" nil 'move)
1640 (not (nsi-in-literal restart)))
1641 (setq restart (point))
1642 (nsi-goto-initial-line)
1643 (if (nsi-statement-opens-block-p)
1645 (goto-char restart))))
1648 (nsi-goto-initial-line)
1649 (while (not (or found (bobp)))
1651 (re-search-backward ":[ \t]*\\($\\|[#\\]\\)" nil 'move)
1652 (or (nsi-goto-initial-line) t) ; always true -- side effect
1653 (nsi-statement-opens-block-p)))))
1654 (setq colon-indent (current-indentation)
1655 found (and found (zerop (nsi-next-statement 1)))
1656 new-value (- (current-indentation) colon-indent))
1659 (error "Sorry, couldn't guess a value for nsi-indent-offset")
1660 (funcall (if global 'kill-local-variable 'make-local-variable)
1662 (setq nsi-indent-offset new-value)
1664 (message "%s value of nsi-indent-offset set to %d"
1665 (if global "Global" "Local")
1666 nsi-indent-offset)))
1669(defun nsi-comment-indent-function ()
1670 "Nsi version of `comment-indent-function'."
1671 ;; This is required when filladapt is turned off. Without it, when
1672 ;; filladapt is not used, comments which start in column zero
1673 ;; cascade one character to the right
1676 (let ((eol (nsi-point 'eol)))
1677 (and comment-start-skip
1678 (re-search-forward comment-start-skip eol t)
1679 (setq eol (match-beginning 0)))
1681 (skip-chars-backward " \t")
1682 (max comment-column (+ (current-column) (if (bolp) 0 1)))
1685(defun nsi-narrow-to-defun (&optional class)
1686 "Make text outside current defun invisible.
1687The defun visible is the one that contains point or follows point.
1688Optional CLASS is passed directly to `nsi-beginning-of-def-or-class'."
1692 (nsi-end-of-def-or-class class)
1693 (let ((end (point)))
1694 (nsi-beginning-of-def-or-class class)
1695 (narrow-to-region (point) end))))
1698(defun nsi-shift-region (start end count)
1699 "Indent lines from START to END by COUNT spaces."
1706 (setq start (point))
1707 (indent-rigidly start end count)))
1709(defun nsi-shift-region-left (start end &optional count)
1710 "Shift region of Nsi code to the left.
1711The lines from the line containing the start of the current region up
1712to (but not including) the line containing the end of the region are
1713shifted to the left, by `nsi-indent-offset' columns.
1715If a prefix argument is given, the region is instead shifted by that
1716many columns. With no active region, dedent only the current line.
1717You cannot dedent the region if any line is already at column zero."
1721 (arg current-prefix-arg))
1723 (list (min p m) (max p m) arg)
1724 (list p (save-excursion (forward-line 1) (point)) arg))))
1725 ;; if any line is at column zero, don't shift the region
1728 (while (< (point) end)
1729 (back-to-indentation)
1730 (if (and (zerop (current-column))
1731 (not (looking-at "\\s *$")))
1732 (error "Region is at left edge"))
1734 (nsi-shift-region start end (- (prefix-numeric-value
1735 (or count nsi-indent-offset))))
1736 (nsi-keep-region-active))
1738(defun nsi-shift-region-right (start end &optional count)
1739 "Shift region of Nsi code to the right.
1740The lines from the line containing the start of the current region up
1741to (but not including) the line containing the end of the region are
1742shifted to the right, by `nsi-indent-offset' columns.
1744If a prefix argument is given, the region is instead shifted by that
1745many columns. With no active region, indent only the current line."
1749 (arg current-prefix-arg))
1751 (list (min p m) (max p m) arg)
1752 (list p (save-excursion (forward-line 1) (point)) arg))))
1753 (nsi-shift-region start end (prefix-numeric-value
1754 (or count nsi-indent-offset)))
1755 (nsi-keep-region-active))
1757(defun nsi-indent-region (start end &optional indent-offset)
1758 "Reindent a region of Nsi code.
1760The lines from the line containing the start of the current region up
1761to (but not including) the line containing the end of the region are
1762reindented. If the first line of the region has a non-whitespace
1763character in the first column, the first line is left alone and the
1764rest of the region is reindented with respect to it. Else the entire
1765region is reindented with respect to the (closest code or indenting
1766comment) statement immediately preceding the region.
1768This is useful when code blocks are moved or yanked, when enclosing
1769control structures are introduced or removed, or to reformat code
1770using a new value for the indentation offset.
1772If a numeric prefix argument is given, it will be used as the value of
1773the indentation offset. Else the value of `nsi-indent-offset' will be
1776Warning: The region must be consistently indented before this function
1777is called! This function does not compute proper indentation from
1778scratch (that's impossible in Nsi), it merely adjusts the existing
1779indentation to be correct in context.
1781Warning: This function really has no idea what to do with
1782non-indenting comment lines, and shifts them as if they were indenting
1783comment lines. Fixing this appears to require telepathy.
1785Special cases: whitespace is deleted from blank lines; continuation
1786lines are shifted by the same amount their initial line was shifted,
1787in order to preserve their relative indentation with respect to their
1788initial line; and comment lines beginning in column 1 are ignored."
1789 (interactive "*r\nP") ; region; raw prefix arg
1791 (goto-char end) (beginning-of-line) (setq end (point-marker))
1792 (goto-char start) (beginning-of-line)
1793 (let ((nsi-indent-offset (prefix-numeric-value
1794 (or indent-offset nsi-indent-offset)))
1795 (indents '(-1)) ; stack of active indent levels
1796 (target-column 0) ; column to which to indent
1797 (base-shifted-by 0) ; amount last base line was shifted
1798 (indent-base (if (looking-at "[ \t\n]")
1799 (nsi-compute-indentation t)
1802 (while (< (point) end)
1803 (setq ci (current-indentation))
1804 ;; figure out appropriate target column
1806 ((or (eq (following-char) ?#) ; comment in column 1
1807 (looking-at "[ \t]*$")) ; entirely blank
1808 (setq target-column 0))
1809 ((nsi-continuation-line-p) ; shift relative to base line
1810 (setq target-column (+ ci base-shifted-by)))
1812 (if (> ci (car indents)) ; going deeper; push it
1813 (setq indents (cons ci indents))
1814 ;; else we should have seen this indent before
1815 (setq indents (memq ci indents)) ; pop deeper indents
1817 (error "Bad indentation in region, at line %d"
1820 (1+ (count-lines 1 (point)))))))
1821 (setq target-column (+ indent-base
1822 (* nsi-indent-offset
1823 (- (length indents) 2))))
1824 (setq base-shifted-by (- target-column ci))))
1826 (if (/= ci target-column)
1828 (delete-horizontal-space)
1829 (indent-to target-column)))
1831 (set-marker end nil))
1833(defun nsi-comment-region (beg end &optional arg)
1834 "Like `comment-region' but uses double hash (`#') comment starter."
1835 (interactive "r\nP")
1836 (let ((comment-start nsi-block-comment-prefix))
1837 (comment-region beg end arg)))
1840;; Functions for moving point
1841(defun nsi-previous-statement (count)
1842 "Go to the start of the COUNTth preceding Nsi statement.
1843By default, goes to the previous statement. If there is no such
1844statement, goes to the first statement. Return count of statements
1845left to move. `Statements' do not include blank, comment, or
1847 (interactive "p") ; numeric prefix arg
1848 (if (< count 0) (nsi-next-statement (- count))
1849 (nsi-goto-initial-line)
1852 (setq start (point)) ; always true -- side effect
1854 (zerop (forward-line -1))
1855 (nsi-goto-statement-at-or-above))
1856 (setq count (1- count)))
1857 (if (> count 0) (goto-char start)))
1860(defun nsi-next-statement (count)
1861 "Go to the start of next Nsi statement.
1862If the statement at point is the i'th Nsi statement, goes to the
1863start of statement i+COUNT. If there is no such statement, goes to the
1864last statement. Returns count of statements left to move. `Statements'
1865do not include blank, comment, or continuation lines."
1866 (interactive "p") ; numeric prefix arg
1867 (if (< count 0) (nsi-previous-statement (- count))
1871 (setq start (point)) ; always true -- side effect
1873 (nsi-goto-statement-below))
1874 (setq count (1- count)))
1875 (if (> count 0) (goto-char start)))
1878(defun nsi-goto-block-up (&optional nomark)
1879 "Move up to start of current block.
1880Go to the statement that starts the smallest enclosing block; roughly
1881speaking, this will be the closest preceding statement that ends with a
1882colon and is indented less than the statement you started on. If
1883successful, also sets the mark to the starting point.
1885`\\[nsi-mark-block]' can be used afterward to mark the whole code
1888If called from a program, the mark will not be set if optional argument
1891 (let ((start (point))
1894 (nsi-goto-initial-line)
1895 ;; if on blank or non-indenting comment line, use the preceding stmt
1896 (if (looking-at "[ \t]*\\($\\|;[^ \t\n]\\)")
1898 (nsi-goto-statement-at-or-above)
1899 (setq found (nsi-statement-opens-block-p))))
1900 ;; search back for colon line indented less
1901 (setq initial-indent (current-indentation))
1902 (if (zerop initial-indent)
1904 (goto-char (point-min)))
1905 (while (not (or found (bobp)))
1908 (re-search-backward ":[ \t]*\\($\\|[;\\]\\)" nil 'move)
1909 (or (nsi-goto-initial-line) t) ; always true -- side effect
1910 (< (current-indentation) initial-indent)
1911 (nsi-statement-opens-block-p))))
1914 (or nomark (push-mark start))
1915 (back-to-indentation))
1917 (error "Enclosing block not found"))))
1919(defun nsi-beginning-of-def-or-class (&optional class count)
1920 "Move point to start of `def' or `class'.
1922Searches back for the closest preceding `def'. If you supply a prefix
1923arg, looks for a `class' instead. The docs below assume the `def'
1924case; just substitute `class' for `def' for the other case.
1925Programmatically, if CLASS is `either', then moves to either `class'
1928When second optional argument is given programmatically, move to the
1929COUNTth start of `def'.
1931If point is in a `def' statement already, and after the `d', simply
1932moves point to the start of the statement.
1934Otherwise (i.e. when point is not in a `def' statement, or at or
1935before the `d' of a `def' statement), searches for the closest
1936preceding `def' statement, and leaves point at its start. If no such
1937statement can be found, leaves point at the start of the buffer.
1939Returns t iff a `def' statement is found by these rules.
1941Note that doing this command repeatedly will take you closer to the
1942start of the buffer each time.
1944To mark the current `def', see `\\[nsi-mark-def-or-class]'."
1945 (interactive "P") ; raw prefix arg
1946 (setq count (or count 1))
1947 (let ((at-or-before-p (<= (current-column) (current-indentation)))
1948 (start-of-line (goto-char (nsi-point 'bol)))
1949 (start-of-stmt (goto-char (nsi-point 'bos)))
1950 (start-re (cond ((eq class 'either) "^[ \t]*\\(class\\|def\\)\\>")
1951 (class "^[ \t]*class\\>")
1952 (t "^[ \t]*def\\>")))
1954 ;; searching backward
1955 (if (and (< 0 count)
1956 (or (/= start-of-stmt start-of-line)
1957 (not at-or-before-p)))
1960 (if (and (> 0 count)
1961 (zerop (current-column))
1962 (looking-at start-re))
1964 (if (re-search-backward start-re nil 'move count)
1965 (goto-char (match-beginning 0)))))
1967;; Backwards compatibility
1968(defalias 'beginning-of-nsi-def-or-class 'nsi-beginning-of-def-or-class)
1970(defun nsi-end-of-def-or-class (&optional class count)
1971 "Move point beyond end of `def' or `class' body.
1973By default, looks for an appropriate `def'. If you supply a prefix
1974arg, looks for a `class' instead. The docs below assume the `def'
1975case; just substitute `class' for `def' for the other case.
1976Programmatically, if CLASS is `either', then moves to either `class'
1979When second optional argument is given programmatically, move to the
1980COUNTth end of `def'.
1982If point is in a `def' statement already, this is the `def' we use.
1984Else, if the `def' found by `\\[nsi-beginning-of-def-or-class]'
1985contains the statement you started on, that's the `def' we use.
1987Otherwise, we search forward for the closest following `def', and use that.
1989If a `def' can be found by these rules, point is moved to the start of
1990the line immediately following the `def' block, and the position of the
1991start of the `def' is returned.
1993Else point is moved to the end of the buffer, and nil is returned.
1995Note that doing this command repeatedly will take you closer to the
1996end of the buffer each time.
1998To mark the current `def', see `\\[nsi-mark-def-or-class]'."
1999 (interactive "P") ; raw prefix arg
2000 (if (and count (/= count 1))
2001 (nsi-beginning-of-def-or-class (- 1 count)))
2002 (let ((start (progn (nsi-goto-initial-line) (point)))
2003 (which (cond ((eq class 'either) "\\(class\\|def\\)")
2007 ;; move point to start of appropriate def/class
2008 (if (looking-at (concat "[ \t]*" which "\\>")) ; already on one
2009 (setq state 'at-beginning)
2010 ;; else see if nsi-beginning-of-def-or-class hits container
2011 (if (and (nsi-beginning-of-def-or-class class)
2012 (progn (nsi-goto-beyond-block)
2014 (setq state 'at-end)
2015 ;; else search forward
2017 (if (re-search-forward (concat "^[ \t]*" which "\\>") nil 'move)
2018 (progn (setq state 'at-beginning)
2019 (beginning-of-line)))))
2021 ((eq state 'at-beginning) (nsi-goto-beyond-block) t)
2022 ((eq state 'at-end) t)
2023 ((eq state 'not-found) nil)
2024 (t (error "Internal error in `nsi-end-of-def-or-class'")))))
2026;; Backwards compabitility
2027(defalias 'end-of-nsi-def-or-class 'nsi-end-of-def-or-class)
2030;; Functions for marking regions
2031(defun nsi-mark-block (&optional extend just-move)
2032 "Mark following block of lines. With prefix arg, mark structure.
2033Easier to use than explain. It sets the region to an `interesting'
2034block of succeeding lines. If point is on a blank line, it goes down to
2035the next non-blank line. That will be the start of the region. The end
2036of the region depends on the kind of line at the start:
2038 - If a comment, the region will include all succeeding comment lines up
2039 to (but not including) the next non-comment line (if any).
2041 - Else if a prefix arg is given, and the line begins one of these
2044 if elif else try except finally for while def class
2046 the region will be set to the body of the structure, including
2047 following blocks that `belong' to it, but excluding trailing blank
2048 and comment lines. E.g., if on a `try' statement, the `try' block
2049 and all (if any) of the following `except' and `finally' blocks
2050 that belong to the `try' structure will be in the region. Ditto
2051 for if/elif/else, for/else and while/else structures, and (a bit
2052 degenerate, since they're always one-block structures) def and
2055 - Else if no prefix argument is given, and the line begins a Nsi
2056 block (see list above), and the block is not a `one-liner' (i.e.,
2057 the statement ends with a colon, not with code), the region will
2058 include all succeeding lines up to (but not including) the next
2059 code statement (if any) that's indented no more than the starting
2060 line, except that trailing blank and comment lines are excluded.
2061 E.g., if the starting line begins a multi-statement `def'
2062 structure, the region will be set to the full function definition,
2063 but without any trailing `noise' lines.
2065 - Else the region will include all succeeding lines up to (but not
2066 including) the next blank line, or code or indenting-comment line
2067 indented strictly less than the starting line. Trailing indenting
2068 comment lines are included in this case, but not trailing blank
2071A msg identifying the location of the mark is displayed in the echo
2072area; or do `\\[exchange-point-and-mark]' to flip down to the end.
2074If called from a program, optional argument EXTEND plays the role of
2075the prefix arg, and if optional argument JUST-MOVE is not nil, just
2076moves to the end of the block (& does not set mark or display a msg)."
2077 (interactive "P") ; raw prefix arg
2078 (nsi-goto-initial-line)
2079 ;; skip over blank lines
2081 (looking-at "[ \t]*$") ; while blank line
2082 (not (eobp))) ; & somewhere to go
2085 (error "Hit end of buffer without finding a non-blank stmt"))
2086 (let ((initial-pos (point))
2087 (initial-indent (current-indentation))
2088 last-pos ; position of last stmt in region
2090 '((if elif else) (elif elif else) (else)
2091 (try except finally) (except except) (finally)
2092 (for else) (while else)
2094 first-symbol next-symbol)
2097 ;; if comment line, suck up the following comment lines
2098 ((looking-at "[ \t]*;")
2099 (re-search-forward "^[ \t]*[^ \t;]" nil 'move) ; look for non-comment
2100 (re-search-backward "^[ \t]*;") ; and back to last comment in block
2101 (setq last-pos (point)))
2103 ;; else if line is a block line and EXTEND given, suck up
2104 ;; the whole structure
2106 (setq first-symbol (nsi-suck-up-first-keyword) )
2107 (assq first-symbol followers))
2109 (or (nsi-goto-beyond-block) t) ; side effect
2110 (forward-line -1) ; side effect
2111 (setq last-pos (point)) ; side effect
2112 (nsi-goto-statement-below)
2113 (= (current-indentation) initial-indent)
2114 (setq next-symbol (nsi-suck-up-first-keyword))
2115 (memq next-symbol (cdr (assq first-symbol followers))))
2116 (setq first-symbol next-symbol)))
2118 ;; else if line *opens* a block, search for next stmt indented <=
2119 ((nsi-statement-opens-block-p)
2121 (setq last-pos (point)) ; always true -- side effect
2122 (nsi-goto-statement-below)
2123 (> (current-indentation) initial-indent))
2126 ;; else plain code line; stop at next blank line, or stmt or
2127 ;; indenting comment line indented <
2130 (setq last-pos (point)) ; always true -- side effect
2131 (or (nsi-goto-beyond-final-line) t)
2132 (not (looking-at "[ \t]*$")) ; stop at blank line
2134 (>= (current-indentation) initial-indent)
2135 (looking-at "[ \t]*;[^ \t\n]"))) ; ignore non-indenting #
2138 ;; skip to end of last stmt
2139 (goto-char last-pos)
2140 (nsi-goto-beyond-final-line)
2142 ;; set mark & display
2145 (push-mark (point) 'no-msg)
2147 (message "Mark set after: %s" (nsi-suck-up-leading-text))
2148 (goto-char initial-pos))))
2150(defun nsi-mark-def-or-class (&optional class)
2151 "Set region to body of def (or class, with prefix arg) enclosing point.
2152Pushes the current mark, then point, on the mark ring (all language
2153modes do this, but although it's handy it's never documented ...).
2155In most Emacs language modes, this function bears at least a
2156hallucinogenic resemblance to `\\[nsi-end-of-def-or-class]' and
2157`\\[nsi-beginning-of-def-or-class]'.
2159And in earlier versions of Nsi mode, all 3 were tightly connected.
2160Turned out that was more confusing than useful: the `goto start' and
2161`goto end' commands are usually used to search through a file, and
2162people expect them to act a lot like `search backward' and `search
2163forward' string-search commands. But because Nsi `def' and `class'
2164can nest to arbitrary levels, finding the smallest def containing
2165point cannot be done via a simple backward search: the def containing
2166point may not be the closest preceding def, or even the closest
2167preceding def that's indented less. The fancy algorithm required is
2168appropriate for the usual uses of this `mark' command, but not for the
2171So the def marked by this command may not be the one either of the
2172`goto' commands find: If point is on a blank or non-indenting comment
2173line, moves back to start of the closest preceding code statement or
2174indenting comment line. If this is a `def' statement, that's the def
2175we use. Else searches for the smallest enclosing `def' block and uses
2176that. Else signals an error.
2178When an enclosing def is found: The mark is left immediately beyond
2179the last line of the def block. Point is left at the start of the
2180def, except that: if the def is preceded by a number of comment lines
2181followed by (at most) one optional blank line, point is left at the
2182start of the comments; else if the def is preceded by a blank line,
2183point is left at its start.
2185The intent is to mark the containing def/class and its associated
2186documentation, to make moving and duplicating functions and classes
2188 (interactive "P") ; raw prefix arg
2189 (let ((start (point))
2190 (which (cond ((eq class 'either) "\\(class\\|def\\)")
2194 (if (not (nsi-go-up-tree-to-keyword which))
2195 (progn (goto-char start)
2196 (error "Enclosing %s not found"
2197 (if (eq class 'either)
2200 ;; else enclosing def/class found
2201 (setq start (point))
2202 (nsi-goto-beyond-block)
2205 (if (zerop (forward-line -1)) ; if there is a preceding line
2207 (if (looking-at "[ \t]*$") ; it's blank
2208 (setq start (point)) ; so reset start point
2209 (goto-char start)) ; else try again
2210 (if (zerop (forward-line -1))
2211 (if (looking-at "[ \t]*;") ; a comment
2212 ;; look back for non-comment line
2213 ;; tricky: note that the regexp matches a blank
2214 ;; line, cuz \n is in the 2nd character class
2216 (re-search-backward "^[ \t]*[^ \t;]" nil 'move)
2218 ;; no comment, so go back
2219 (goto-char start)))))))
2220 (exchange-point-and-mark)
2221 (nsi-keep-region-active))
2223;; ripped from cc-mode
2224(defun nsi-forward-into-nomenclature (&optional arg)
2225 "Move forward to end of a nomenclature section or word.
2226With \\[universal-argument] (programmatically, optional argument ARG),
2227do it that many times.
2229A `nomenclature' is a fancy way of saying AWordWithMixedCaseNotUnderscores."
2231 (let ((case-fold-search nil))
2234 "\\(\\W\\|[_]\\)*\\([A-Z]*[a-z0-9]*\\)"
2236 (while (and (< arg 0)
2238 "\\(\\W\\|[a-z0-9]\\)[A-Z]+\\|\\(\\W\\|[_]\\)\\w+"
2241 (setq arg (1+ arg)))))
2242 (nsi-keep-region-active))
2244(defun nsi-backward-into-nomenclature (&optional arg)
2245 "Move backward to beginning of a nomenclature section or word.
2246With optional ARG, move that many times. If ARG is negative, move
2249A `nomenclature' is a fancy way of saying AWordWithMixedCaseNotUnderscores."
2251 (nsi-forward-into-nomenclature (- arg))
2252 (nsi-keep-region-active))
2256;; Documentation functions
2258;; dump the long form of the mode blurb; does the usual doc escapes,
2259;; plus lines of the form ^[vc]:name$ to suck variable & command docs
2260;; out of the right places, along with the keys they're on & current
2262(defun nsi-dump-help-string (str)
2263 (with-output-to-temp-buffer "*Help*"
2264 (let ((locals (buffer-local-variables))
2265 funckind funcname func funcdoc
2266 (start 0) mstart end
2268 (while (string-match "^%\\([vc]\\):\\(.+\\)\n" str start)
2269 (setq mstart (match-beginning 0) end (match-end 0)
2270 funckind (substring str (match-beginning 1) (match-end 1))
2271 funcname (substring str (match-beginning 2) (match-end 2))
2272 func (intern funcname))
2273 (princ (substitute-command-keys (substring str start mstart)))
2275 ((equal funckind "c") ; command
2276 (setq funcdoc (documentation func)
2279 (mapconcat 'key-description
2280 (where-is-internal func nsi-mode-map)
2282 ((equal funckind "v") ; variable
2283 (setq funcdoc (documentation-property func 'variable-documentation)
2284 keys (if (assq func locals)
2286 "Local/Global values: "
2287 (prin1-to-string (symbol-value func))
2289 (prin1-to-string (default-value func)))
2292 (prin1-to-string (symbol-value func))))))
2294 (error "Error in nsi-dump-help-string, tag `%s'" funckind)))
2295 (princ (format "\n-> %s:\t%s\t%s\n\n"
2296 (if (equal funckind "c") "Command" "Variable")
2301 (princ (substitute-command-keys (substring str start))))
2302 (print-help-return-message)))
2304(defun nsi-describe-mode ()
2305 "Dump long form of Nsi-mode docs."
2307 (nsi-dump-help-string "Major mode for editing Nsi files.
2308Knows about Nsi indentation, tokens, comments and continuation lines.
2309Paragraphs are separated by blank lines only.
2311Major sections below begin with the string `@'; specific function and
2312variable docs begin with `->'.
2316\\[nsi-execute-import-or-reload]\timports or reloads the file in the Nsi interpreter
2317\\[nsi-execute-buffer]\tsends the entire buffer to the Nsi interpreter
2318\\[nsi-execute-region]\tsends the current region
2319\\[nsi-execute-def-or-class]\tsends the current function or class definition
2320\\[nsi-execute-string]\tsends an arbitrary string
2321\\[nsi-shell]\tstarts a Nsi interpreter window; this will be used by
2322\tsubsequent Nsi execution commands
2323%c:nsi-execute-import-or-reload
2324%c:nsi-execute-buffer
2325%c:nsi-execute-region
2326%c:nsi-execute-def-or-class
2327%c:nsi-execute-string
2332nsi-indent-offset\tindentation increment
2333nsi-block-comment-prefix\tcomment string used by comment-region
2335nsi-nsi-command\tshell command to invoke Nsi interpreter
2336nsi-temp-directory\tdirectory used for temp files (if needed)
2338nsi-beep-if-tab-change\tring the bell if tab-width is changed
2340%v:nsi-block-comment-prefix
2342%v:nsi-temp-directory
2343%v:nsi-beep-if-tab-change
2347Each physical line in the file is either a `continuation line' (the
2348preceding line ends with a backslash that's not part of a comment, or
2349the paren/bracket/brace nesting level at the start of the line is
2350non-zero, or both) or an `initial line' (everything else).
2352An initial line is in turn a `blank line' (contains nothing except
2353possibly blanks or tabs), a `comment line' (leftmost non-blank
2354character is `#'), or a `code line' (everything else).
2358Although all comment lines are treated alike by Nsi, Nsi mode
2359recognizes two kinds that act differently with respect to indentation.
2361An `indenting comment line' is a comment line with a blank, tab or
2362nothing after the initial `#'. The indentation commands (see below)
2363treat these exactly as if they were code lines: a line following an
2364indenting comment line will be indented like the comment line. All
2365other comment lines (those with a non-whitespace character immediately
2366following the initial `#') are `non-indenting comment lines', and
2367their indentation is ignored by the indentation commands.
2369Indenting comment lines are by far the usual case, and should be used
2370whenever possible. Non-indenting comment lines are useful in cases
2373\ta = b # a very wordy single-line comment that ends up being
2374\t #... continued onto another line
2377##\t\tprint 'panic!' # old code we've `commented out'
2380Since the `#...' and `##' comment lines have a non-whitespace
2381character following the initial `#', Nsi mode ignores them when
2382computing the proper indentation for the next line.
2384Continuation Lines and Statements
2386The Nsi-mode commands generally work on statements instead of on
2387individual lines, where a `statement' is a comment or blank line, or a
2388code line and all of its following continuation lines (if any)
2389considered as a single logical unit. The commands in this mode
2390generally (when it makes sense) automatically move to the start of the
2391statement containing point, even if point happens to be in the middle
2392of some continuation line.
2397Primarily for entering new code:
2398\t\\[indent-for-tab-command]\t indent line appropriately
2399\t\\[nsi-newline-and-indent]\t insert newline, then indent
2400\t\\[nsi-electric-backspace]\t reduce indentation, or delete single character
2402Primarily for reindenting existing code:
2403\t\\[nsi-guess-indent-offset]\t guess nsi-indent-offset from file content; change locally
2404\t\\[universal-argument] \\[nsi-guess-indent-offset]\t ditto, but change globally
2406\t\\[nsi-indent-region]\t reindent region to match its context
2407\t\\[nsi-shift-region-left]\t shift region left by nsi-indent-offset
2408\t\\[nsi-shift-region-right]\t shift region right by nsi-indent-offset
2410Unlike most programming languages, Nsi uses indentation, and only
2411indentation, to specify block structure. Hence the indentation supplied
2412automatically by Nsi-mode is just an educated guess: only you know
2413the block structure you intend, so only you can supply correct
2416The \\[indent-for-tab-command] and \\[nsi-newline-and-indent] keys try to suggest plausible indentation, based on
2417the indentation of preceding statements. E.g., assuming
2418nsi-indent-offset is 4, after you enter
2419\tif a > 0: \\[nsi-newline-and-indent]
2420the cursor will be moved to the position of the `_' (_ is not a
2421character in the file, it's just used here to indicate the location of
2425If you then enter `c = d' \\[nsi-newline-and-indent], the cursor will move
2430Nsi-mode cannot know whether that's what you intended, or whether
2434was your intent. In general, Nsi-mode either reproduces the
2435indentation of the (closest code or indenting-comment) preceding
2436statement, or adds an extra nsi-indent-offset blanks if the preceding
2437statement has `:' as its last significant (non-whitespace and non-
2438comment) character. If the suggested indentation is too much, use
2439\\[nsi-electric-backspace] to reduce it.
2441Continuation lines are given extra indentation. If you don't like the
2442suggested indentation, change it to something you do like, and Nsi-
2443mode will strive to indent later lines of the statement in the same way.
2445If a line is a continuation line by virtue of being in an unclosed
2446paren/bracket/brace structure (`list', for short), the suggested
2447indentation depends on whether the current line contains the first item
2448in the list. If it does, it's indented nsi-indent-offset columns beyond
2449the indentation of the line containing the open bracket. If you don't
2450like that, change it by hand. The remaining items in the list will mimic
2451whatever indentation you give to the first item.
2453If a line is a continuation line because the line preceding it ends with
2454a backslash, the third and following lines of the statement inherit their
2455indentation from the line preceding them. The indentation of the second
2456line in the statement depends on the form of the first (base) line: if
2457the base line is an assignment statement with anything more interesting
2458than the backslash following the leftmost assigning `=', the second line
2459is indented two columns beyond that `='. Else it's indented to two
2460columns beyond the leftmost solid chunk of non-whitespace characters on
2463Warning: indent-region should not normally be used! It calls \\[indent-for-tab-command]
2464repeatedly, and as explained above, \\[indent-for-tab-command] can't guess the block
2465structure you intend.
2466%c:indent-for-tab-command
2467%c:nsi-newline-and-indent
2468%c:nsi-electric-backspace
2471The next function may be handy when editing code you didn't write:
2472%c:nsi-guess-indent-offset
2475The remaining `indent' functions apply to a region of Nsi code. They
2476assume the block structure (equals indentation, in Nsi) of the region
2477is correct, and alter the indentation in various ways while preserving
2480%c:nsi-shift-region-left
2481%c:nsi-shift-region-right
2483@MARKING & MANIPULATING REGIONS OF CODE
2485\\[nsi-mark-block]\t mark block of lines
2486\\[nsi-mark-def-or-class]\t mark smallest enclosing def
2487\\[universal-argument] \\[nsi-mark-def-or-class]\t mark smallest enclosing class
2488\\[comment-region]\t comment out region of code
2489\\[universal-argument] \\[comment-region]\t uncomment region of code
2491%c:nsi-mark-def-or-class
2496\\[nsi-previous-statement]\t move to statement preceding point
2497\\[nsi-next-statement]\t move to statement following point
2498\\[nsi-goto-block-up]\t move up to start of current block
2499\\[nsi-beginning-of-def-or-class]\t move to start of def
2500\\[universal-argument] \\[nsi-beginning-of-def-or-class]\t move to start of class
2501\\[nsi-end-of-def-or-class]\t move to end of def
2502\\[universal-argument] \\[nsi-end-of-def-or-class]\t move to end of class
2504The first two move to one statement beyond the statement that contains
2505point. A numeric prefix argument tells them to move that many
2506statements instead. Blank lines, comment lines, and continuation lines
2507do not count as `statements' for these commands. So, e.g., you can go
2508to the first code statement in a file by entering
2509\t\\[beginning-of-buffer]\t to move to the top of the file
2510\t\\[nsi-next-statement]\t to skip over initial comments and blank lines
2511Or do `\\[nsi-previous-statement]' with a huge prefix argument.
2512%c:nsi-previous-statement
2513%c:nsi-next-statement
2515%c:nsi-beginning-of-def-or-class
2516%c:nsi-end-of-def-or-class
2518@LITTLE-KNOWN EMACS COMMANDS PARTICULARLY USEFUL IN NSI MODE
2520`\\[indent-new-comment-line]' is handy for entering a multi-line comment.
2522`\\[set-selective-display]' with a `small' prefix arg is ideally suited for viewing the
2523overall class and def structure of a module.
2525`\\[back-to-indentation]' moves point to a line's first non-blank character.
2527`\\[indent-relative]' is handy for creating odd indentation.
2531If you don't like the default value of a variable, change its value to
2532whatever you do like by putting a `setq' line in your .emacs file.
2533E.g., to set the indentation increment to 4, put this line in your
2535\t(setq nsi-indent-offset 4)
2536To see the value of a variable, do `\\[describe-variable]' and enter the variable
2539When entering a key sequence like `C-c C-n', it is not necessary to
2540release the CONTROL key after doing the `C-c' part -- it suffices to
2541press the CONTROL key, press and release `c' (while still holding down
2542CONTROL), press and release `n' (while still holding down CONTROL), &
2543then release CONTROL.
2545Entering Nsi mode calls with no arguments the value of the variable
2546`nsi-mode-hook', if that value exists and is not nil; for backward
2547compatibility it also tries `nsi-mode-hook'; see the `Hooks' section of
2548the Elisp manual for details.
2550Obscure: When nsi-mode is first loaded, it looks for all bindings
2551to newline-and-indent in the global keymap, and shadows them with
2552local bindings to nsi-newline-and-indent."))
2556(defvar nsi-parse-state-re
2558 "^[ \t]*\\(if\\|elif\\|else\\|while\\|def\\|class\\)\\>"
2562(defun nsi-parse-state ()
2563 "Return the parse state at point (see `parse-partial-sexp' docs)."
2565 (let ((here (point))
2568 ;; back up to the first preceding line (if any; else start of
2569 ;; buffer) that begins with a popular Nsi keyword, or a
2570 ;; non- whitespace and non-comment character. These are good
2571 ;; places to start parsing to see whether where we started is
2572 ;; at a non-zero nesting level. It may be slow for people who
2573 ;; write huge code blocks or huge lists ... tough beans.
2574 (re-search-backward nsi-parse-state-re nil 'move)
2576 ;; In XEmacs, we have a much better way to test for whether
2577 ;; we're in a triple-quoted string or not. Emacs does not
2578 ;; have this built-in function, which is its loss because
2579 ;; without scanning from the beginning of the buffer, there's
2580 ;; no accurate way to determine this otherwise.
2581 (if (not (fboundp 'buffer-syntactic-context))
2584 (save-excursion (setq pps (parse-partial-sexp (point) here)))
2585 ;; make sure we don't land inside a triple-quoted string
2586 (setq done (or (not (nth 3 pps))
2588 ;; Just go ahead and short circuit the test back to the
2589 ;; beginning of the buffer. This will be slow, but not
2590 ;; nearly as slow as looping through many
2591 ;; re-search-backwards.
2593 (goto-char (point-min))))
2595 (setq done (or (not (buffer-syntactic-context))
2598 (setq pps (parse-partial-sexp (point) here)))
2602(defun nsi-nesting-level ()
2603 "Return the buffer position of the last unclosed enclosing list.
2604If nesting level is zero, return nil."
2605 (let ((status (nsi-parse-state)))
2606 (if (zerop (car status))
2608 (car (cdr status))))) ; char# of open bracket
2610(defun nsi-backslash-continuation-line-p ()
2611 "Return t iff preceding line ends with backslash that is not in a comment."
2615 ;; use a cheap test first to avoid the regexp if possible
2616 ;; use 'eq' because char-after may return nil
2617 (eq (char-after (- (point) 2)) ?\\ )
2618 ;; make sure; since eq test passed, there is a preceding line
2619 (forward-line -1) ; always true -- side effect
2620 (looking-at nsi-continued-re))))
2622(defun nsi-continuation-line-p ()
2623 "Return t iff current line is a continuation line."
2626 (or (nsi-backslash-continuation-line-p)
2627 (nsi-nesting-level))))
2629(defun nsi-goto-beginning-of-tqs (delim)
2630 "Go to the beginning of the triple quoted string we find ourselves in.
2631DELIM is the TQS string delimiter character we're searching backwards
2633 (let ((skip (and delim (make-string 1 delim))))
2636 (nsi-safe (search-backward skip))
2637 (if (and (eq (char-before) delim)
2638 (eq (char-before (1- (point))) delim))
2639 (setq skip (make-string 3 delim))))
2640 ;; we're looking at a triple-quoted string
2641 (nsi-safe (search-backward skip)))))
2643(defun nsi-goto-initial-line ()
2644 "Go to the initial line of the current statement.
2645Usually this is the line we're on, but if we're on the 2nd or
2646following lines of a continuation block, we need to go up to the first
2648 ;; Tricky: We want to avoid quadratic-time behavior for long
2649 ;; continued blocks, whether of the backslash or open-bracket
2650 ;; varieties, or a mix of the two. The following manages to do that
2651 ;; in the usual cases.
2653 ;; Also, if we're sitting inside a triple quoted string, this will
2654 ;; drop us at the line that begins the string.
2655 (let (open-bracket-pos)
2656 (while (nsi-continuation-line-p)
2658 (if (nsi-backslash-continuation-line-p)
2659 (while (nsi-backslash-continuation-line-p)
2661 ;; else zip out of nested brackets/braces/parens
2662 (while (setq open-bracket-pos (nsi-nesting-level))
2663 (goto-char open-bracket-pos)))))
2664 (beginning-of-line))
2666(defun nsi-goto-beyond-final-line ()
2667 "Go to the point just beyond the fine line of the current statement.
2668Usually this is the start of the next line, but if this is a
2669multi-line statement we need to skip over the continuation lines."
2670 ;; Tricky: Again we need to be clever to avoid quadratic time
2673 ;; XXX: Not quite the right solution, but deals with multi-line doc
2675 (if (looking-at (concat "[ \t]*\\(" nsi-stringlit-re "\\)"))
2676 (goto-char (match-end 0)))
2680 (while (and (nsi-continuation-line-p)
2682 ;; skip over the backslash flavor
2683 (while (and (nsi-backslash-continuation-line-p)
2686 ;; if in nest, zip to the end of the nest
2687 (setq state (nsi-parse-state))
2688 (if (and (not (zerop (car state)))
2691 (parse-partial-sexp (point) (point-max) 0 nil state)
2692 (forward-line 1))))))
2694(defun nsi-statement-opens-block-p ()
2695 "Return t iff the current statement opens a block.
2696I.e., iff it ends with a colon that is not in a comment. Point should
2697be at the start of a statement."
2699 (let ((start (point))
2700 (finish (progn (nsi-goto-beyond-final-line) (1- (point))))
2706 ;; look for a colon with nothing after it except whitespace, and
2708 (if (re-search-forward ":\\([ \t]\\|\\\\\n\\)*\\(;.*\\)?$"
2710 (if (eq (point) finish) ; note: no `else' clause; just
2711 ; keep searching if we're not at
2713 ;; sure looks like it opens a block -- but it might
2716 (setq searching nil) ; search is done either way
2717 (setq state (parse-partial-sexp start
2718 (match-beginning 0)))
2719 (setq answer (not (nth 4 state)))))
2720 ;; search failed: couldn't find another interesting colon
2721 (setq searching nil)))
2724(defun nsi-statement-closes-block-p ()
2725 "Return t iff the current statement closes a block.
2726I.e., if the line starts with `return', `raise', `break', `continue',
2727and `pass'. This doesn't catch embedded statements."
2728 (let ((here (point)))
2729 (nsi-goto-initial-line)
2730 (back-to-indentation)
2732 (looking-at (concat nsi-block-closing-keywords-re "\\>"))
2735(defun nsi-goto-beyond-block ()
2736 "Go to point just beyond the final line of block begun by the current line.
2737This is the same as where `nsi-goto-beyond-final-line' goes unless
2738we're on colon line, in which case we go to the end of the block.
2739Assumes point is at the beginning of the line."
2740 (if (nsi-statement-opens-block-p)
2741 (nsi-mark-block nil 'just-move)
2742 (nsi-goto-beyond-final-line)))
2744(defun nsi-goto-statement-at-or-above ()
2745 "Go to the start of the first statement at or preceding point.
2746Return t if there is such a statement, otherwise nil. `Statement'
2747does not include blank lines, comments, or continuation lines."
2748 (nsi-goto-initial-line)
2749 (if (looking-at nsi-blank-or-comment-re)
2750 ;; skip back over blank & comment lines
2751 ;; note: will skip a blank or comment line that happens to be
2752 ;; a continuation line too
2753 (if (re-search-backward "^[ \t]*[^ \t;\n]" nil t)
2754 (progn (nsi-goto-initial-line) t)
2758(defun nsi-goto-statement-below ()
2759 "Go to start of the first statement following the statement containing point.
2760Return t if there is such a statement, otherwise nil. `Statement'
2761does not include blank lines, comments, or continuation lines."
2763 (let ((start (point)))
2764 (nsi-goto-beyond-final-line)
2766 (looking-at nsi-blank-or-comment-re)
2770 (progn (goto-char start) nil)
2773(defun nsi-go-up-tree-to-keyword (key)
2774 "Go to begining of statement starting with KEY, at or preceding point.
2776KEY is a regular expression describing a Nsi keyword. Skip blank
2777lines and non-indenting comments. If the statement found starts with
2778KEY, then stop, otherwise go back to first enclosing block starting
2779with KEY. If successful, leave point at the start of the KEY line and
2780return t. Otherwise, leav point at an undefined place and return nil."
2781 ;; skip blanks and non-indenting #
2782 (nsi-goto-initial-line)
2784 (looking-at "[ \t]*\\($\\|;[^ \t\n]\\)")
2785 (zerop (forward-line -1))) ; go back
2787 (nsi-goto-initial-line)
2788 (let* ((re (concat "[ \t]*" key "\\b"))
2789 (case-fold-search nil) ; let* so looking-at sees this
2790 (found (looking-at re))
2792 (while (not (or found dead))
2793 (condition-case nil ; in case no enclosing block
2794 (nsi-goto-block-up 'no-mark)
2795 (error (setq dead t)))
2796 (or dead (setq found (looking-at re))))
2800(defun nsi-suck-up-leading-text ()
2801 "Return string in buffer from start of indentation to end of line.
2802Prefix with \"...\" if leading whitespace was skipped."
2804 (back-to-indentation)
2806 (if (bolp) "" "...")
2807 (buffer-substring (point) (progn (end-of-line) (point))))))
2809(defun nsi-suck-up-first-keyword ()
2810 "Return first keyword on the line as a Lisp symbol.
2811`Keyword' is defined (essentially) as the regular expression
2812([a-z]+). Returns nil if none was found."
2813 (let ((case-fold-search nil))
2814 (if (looking-at "[ \t]*\\([a-z]+\\)\\b")
2815 (intern (buffer-substring (match-beginning 1) (match-end 1)))
2818(defun nsi-current-defun ()
2819 "Nsi value for `add-log-current-defun-function'.
2820This tells add-log.el how to find the current function/method/variable."
2822 (if (re-search-backward nsi-defun-start-re nil t)
2823 (or (match-string 3)
2824 (let ((method (match-string 2)))
2825 (if (and (not (zerop (length (match-string 1))))
2826 (re-search-backward nsi-class-start-re nil t))
2827 (concat (match-string 1) "." method)
2832(defconst nsi-help-address "nsi-mode@nsi.org"
2833 "Address accepting submission of bug reports.")
2835(defun nsi-version ()
2836 "Echo the current version of `nsi-mode' in the minibuffer."
2838 (message "Using `nsi-mode' version %s" nsi-version)
2839 (nsi-keep-region-active))
2841;; only works under Emacs 19
2843; (require 'reporter))
2845(defun nsi-submit-bug-report (enhancement-p)
2846 "Submit via mail a bug report on `nsi-mode'.
2847With \\[universal-argument] (programmatically, argument ENHANCEMENT-P
2848non-nil) just submit an enhancement request."
2850 (list (not (y-or-n-p
2851 "Is this a bug report (hit `n' to send other comments)? "))))
2852 (let ((reporter-prompt-for-summary-p (if enhancement-p
2853 "(Very) brief summary: "
2856 (reporter-submit-bug-report
2857 nsi-help-address ;address
2858 (concat "nsi-mode " nsi-version) ;pkgname
2860 (if enhancement-p nil
2863 nsi-block-comment-prefix
2865 nsi-beep-if-tab-change))
2868 "Dear Barry,") ;salutation
2869 (if enhancement-p nil
2872"Please replace this text with a sufficiently large code sample\n\
2873and an exact recipe so that I can reproduce your problem. Failure\n\
2874to do so may mean a greater delay in fixing your bug.\n\n")
2875 (exchange-point-and-mark)
2876 (nsi-keep-region-active))))
2879;; (defun nsi-kill-emacs-hook ()
2880;; "Delete files in `nsi-file-queue'.
2881;; These are Nsi temporary files awaiting execution."
2882;; (mapcar #'(lambda (filename)
2883;; (nsi-safe (delete-file filename)))
2886;; ;; arrange to kill temp files when Emacs exists
2887;; (add-hook 'kill-emacs-hook 'nsi-kill-emacs-hook)
2892;;; nsi-mode.el ends here