sml-mode-5.0/0000755005056000007300000000000011741417724014006 5ustar monnierparallelismesml-mode-5.0/sml-mode-startup.el0000644005056000007300000000333711741417724017553 0ustar monnierparallelisme;;; sml-mode-startup.el --- automatically extracted autoloads ;;; Code: (add-to-list 'load-path (or (file-name-directory load-file-name) (car load-path))) ;;;### (autoloads (sml-yacc-mode sml-lex-mode sml-cm-mode sml-mode) ;;;;;; "sml-mode" "sml-mode.el" (20358 8148)) ;;; Generated autoloads from sml-mode.el (add-to-list 'auto-mode-alist '("\\.s\\(ml\\|ig\\)\\'" . sml-mode)) (autoload 'sml-mode "sml-mode" "\ \\Major mode for editing ML code. This mode runs `sml-mode-hook' just before exiting. \\{sml-mode-map} \(fn)" t nil) (add-to-list 'completion-ignored-extensions ".cm/") (add-to-list 'auto-mode-alist '("\\.cm\\'" . sml-cm-mode)) (autoload 'sml-cm-mode "sml-mode" "\ Major mode for SML/NJ's Compilation Manager configuration files. \(fn)" t nil) (autoload 'sml-lex-mode "sml-mode" "\ Major Mode for editing ML-Lex files. \(fn)" t nil) (add-to-list 'auto-mode-alist '("\\.grm\\'" . sml-yacc-mode)) (autoload 'sml-yacc-mode "sml-mode" "\ Major Mode for editing ML-Yacc files. \(fn)" t nil) (if (fboundp 'register-definition-prefixes) (register-definition-prefixes "sml-mode" '("font-lock-type-def-face" "font-lock-module-def-face" "font-lock-interface-def-face"))) ;;;*** ;;;### (autoloads nil "sml-oldindent" "sml-oldindent.el" (20358 8148)) ;;; Generated autoloads from sml-oldindent.el (if (fboundp 'register-definition-prefixes) (register-definition-prefixes "sml-oldindent" '("sml-"))) ;;;*** ;;;### (autoloads nil "sml-proc" "sml-proc.el" (20358 8148)) ;;; Generated autoloads from sml-proc.el (autoload 'run-sml "sml-proc" nil t) (if (fboundp 'register-definition-prefixes) (register-definition-prefixes "sml-proc" '("sml-" "inferior-sml-" "font-lock-" "run-sml" "switch-to-sml"))) ;;;*** sml-mode-5.0/sml-mode.info0000644005056000007300000010433511741417724016406 0ustar monnierparallelismeThis is sml-mode.info, produced by makeinfo version 4.13 from sml-mode.texi. INFO-DIR-SECTION Emacs START-INFO-DIR-ENTRY * sml:(sml-mode). Emacs mode for editing SML END-INFO-DIR-ENTRY  File: sml-mode.info, Node: Top, Next: Copying, Prev: (dir), Up: (dir) 1 SML Mode Info *************** You are looking at the top node of the Info tree documenting SML-MODE (Version $Name$). Not all functions are documented here, but those that aren't you probably won't miss. All commands and settable variables have built-in documentation, as per usual Emacs conventions. * Menu: * Copying:: You can copy SML mode * Introduction:: Setting things up * SML Mode:: Editing SML source * Interaction Mode:: Running ML processes * Configuration:: Menus, highlighting, setting defaults Indexes * Command Index:: Commands you can invoke * Variable Index:: Variables you can set * Key Index:: Default keybindings Introduction * Contributors:: Who did what * Getting Started:: What to tell Emacs * Getting Help:: How Emacs can help SML Mode * Basics:: On entering SML mode * Indentation:: Prettying SML text * Magic Insertion:: Templates and electric keys * SML Mode Defaults:: Variables controlling indentation Interaction Mode * Running ML:: Commands to run the ML compiler in a buffer * ML Interaction:: Sending program fragments to the compiler * Tracking Errors:: Finding reported syntax errors * Process Defaults:: Setting defaults for process interaction Configuration * Hooks:: Creating hooks * Key Bindings:: Binding commands to keys * Highlighting:: Syntax colouring * Advanced Topics:: You may need to speak Emacs Lisp  File: sml-mode.info, Node: Copying, Next: Introduction, Prev: Top, Up: Top 2 Copying ********* You can freely copy, modify and redistribute SML mode because it's made available under the liberal terms of the GNU General Public License. GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. SML mode is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Emacs; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  File: sml-mode.info, Node: Introduction, Next: SML Mode, Prev: Copying, Up: Top 3 Introduction ************** SML mode is a major mode for Emacs for editing Standard ML. It has some novel bugs, and some nice features: * Automatic indentation of sml code--a number of variables to customise the indentation. * Easy insertion for commonly used templates like let, local, signature, and structure declarations, with minibuffer prompting for types and expressions. * Magic pipe insertion: `|' automatically determines if it is used in a case or fun construct, and indents the next line as appropriate, inserting `=>' or the name of the function. * Inferior shell for running ML. There's no need to leave Emacs, just keep on editing while the compiler runs in another window. * Automatic "use file" in the inferior shell--you can send files, buffers, or regions of code to the ML subprocess. * Menus, and syntax and keyword highlighting supported for Emacs 19 and derivatives. * Parsing errors from the inferior shell, and repositioning the source with next-error--just like in c-mode. * SML mode can be easily configured to work with a number of Standard ML compilers, and other SML based tools. * Menu: * Contributors:: Who did what * Getting Started:: What to tell Emacs * Getting Help:: How Emacs can help  File: sml-mode.info, Node: Contributors, Next: Getting Started, Prev: Introduction, Up: Introduction 3.1 Contributors to the SML mode ================================ Contributions to the package are welcome. I have limited time to work on this project, but I will gladly add any code that you contribute to me to this package. Although the history of sml-mode is obscure, it seems that the following persons have made contributions to sml-mode: * Lars Bo Nielsen wrote the original version of the code, providing the sml editing mode and the inferior-sml support. * Olin Shivers (`shivers@ai.mit.edu') hacked the inferior-sml support to use comint and call the whole thing ml-mode. * Steven Gilmore supposedly provided some early attempt at menubar support. * Matthew J. Morley (`matthew@verisity.com') was maintainer for a long time (until version 3.4) and provided many additions and fixes in all areas. * Frederick Knabe (`knabe@ecrc.de') provided the original code for font-lock and hilite support as well as for proper handling of nested comments and of all the string escape sequences. * Matthias Blume (`blume@kurims.kyoto-u.ac.jp') provided a sml-make which was replaced by sml-compile. * Monnier Stefan (`monnier@iro.umontreal.ca') completely reworked the indentation engine as well as most of the rest of the code and is the current maintainer since after version 3.4.  File: sml-mode.info, Node: Getting Started, Next: Getting Help, Prev: Contributors, Up: Introduction 3.2 Getting started =================== With luck your system administrator will have installed SML mode somewhere convenient, so it will just magically all work--you can skip the rest of this getting started section. Otherwise you will need to tell Emacs where to find all the SML mode `.el' files, and when to use them. The where is addressed by locating the Lisp code on your Emacs Lisp load path--you may have to create a directory for this, say `/home/mjm/elisp', and then insert the following lines in your `/home/mjm/.emacs' file: (add-to-list 'load-path "/home/mjm/elisp") (autoload 'sml-mode "sml-mode" "Major mode for editing SML." t) (autoload 'run-sml "sml-proc" "Run an inferior SML process." t) The first line adjusts Emacs' internal search path so it can locate the Lisp source you have copied to that directory; the second and third lines tell Emacs to load the code automatically when it is needed. You can then switch any Emacs buffer into SML mode by entering the command M-x sml-mode It is usually more convenient to have Emacs automatically place the buffer in SML mode whenever you visit a file containing ML programs. The simplest way of achieving this is to put something like (add-to-list 'auto-mode-alist '("\\.\\(sml\\|sig\\)\\'" . sml-mode)) also in your `.emacs' file. Subsequently (after a restart), any files with these extensions will be placed in SML mode buffers when you visit them. You may want to pre-compile the `sml-*.el' files (`M-x byte-compile-file') for greater speed--byte compiled code loads and runs somewhat faster.  File: sml-mode.info, Node: Getting Help, Prev: Getting Started, Up: Introduction 3.3 Help! ========= You're reading it. Apart from the on-line info tree (`C-h i' is the Emacs key to enter the `info' system--you should follow the brief tutorial if this is unfamiliar), there are further details on specific commands in their documentation strings. Only the most useful SML mode commands are documented in the info tree: to find out more use Emacs' help facilities. Briefly, to get help on a specific function use `C-h f' and enter the command name. All (almost all, then) SML mode commands begin with `sml-', so if you type this and press (for completion) you will get a list of all commands. Another way is to use `C-h a' and enter the string `sml'. This is command apropos; it will list all commands with that sub-string in their names, and any key binding they may have in the current buffer. Command apropos gives a one-line synopsis of what each command does. Some commands are also variables--such things are allowed in Lisp, if not in ML! *Note Command Index::, for a list of (info) documented functions. *Note Variable Index::, for a list of user settable variables to control the behaviour of SML mode. Before accessing this information on-line from within Emacs you may have to set the variable `sml-mode-info'. Put in your `.emacs' file something like: (setq sml-mode-info "/home/mjm/info/sml-mode.info") When different from the default this variable should be a string giving the absolute name of the `.info' file. Then `C-c C-i' in SML mode (i.e., the command `M-x sml-mode-info') will bring up the manual. This help is also accessible from the menu. (Resetting this variable will not be necessary if your site administrator has been kind enough to install SML mode and its attendant documentation in the Emacs hierarchy.)  File: sml-mode.info, Node: SML Mode, Next: Interaction Mode, Prev: Introduction, Up: Top 4 Editing with SML Mode *********************** Now SML mode provides just a few additional editing commands. Most of the work has gone into implementing the indentation algorithm which, if you think about it, has to be complicated for a language like ML. *Note Indentation Defaults: SML Mode Defaults, for details on how to control some of the behaviour of the indentation algorithm. Principal goodies are the `electric pipe' feature, and the ability to insert common SML forms (macros or templates). * Menu: * Basics:: On entering SML mode * Indentation:: Prettying SML text * Magic Insertion:: Templates and electric keys * SML Mode Defaults:: Variables controlling indentation  File: sml-mode.info, Node: Basics, Next: Indentation, Prev: SML Mode, Up: SML Mode 4.1 On entering SML mode ======================== -- Command: sml-mode This switches a buffer into SML mode. This is a _major mode_ in Emacs. To get out of SML mode the buffer's major mode must be set to something else, like text-mode. *Note Getting Started::, for details on how to set this up automatically when visiting an SML file. Emacs is all hooks of course. A hook is a variable: if the variable is non-nil it binds a list of Emacs Lisp functions to be run in some order (usually left to right). You can customise SML mode with these hooks: -- Hook: sml-mode-hook Default: `nil' This is run every time a new SML mode buffer is created (or if you type `M-x sml-mode'). This is one place to put your preferred key bindings. *Note Configuration::, for some examples.  File: sml-mode.info, Node: Indentation, Next: Magic Insertion, Prev: Basics, Up: SML Mode 4.2 Automatic indentation ========================= ML is a complicated language to parse, let alone compile. The indentation algorithm is a little wooden (for some tastes), and the best advice is not to fight it! There are several variables that can be adjusted to control the indentation algorithm (*note Customising SML Mode: SML Mode Defaults, below). -- Command: indent-for-tab-command Key: This command indents the current line. If you set the indentation of the previous line by hand, `indent-for-tab-command' will indent relative to this setting. -- Command: indent-region Key: `C-M-\' Indent the current region. Be patient if the region is large (like the whole buffer). -- Command: sml-back-to-outer-indent Key: `M-' Unindents the line to the next outer level of indentation. Further indentation commands that Emacs provides (generically, for all modes) that you may like to recall: - `M-x newline-and-indent' On by default. Insert a newline, then indent according to the major mode. *Note Indentation for Programs: (emacs)Program Indent, for details. - `M-x indent-rigidly' On `C-x ' by default. Moves all lines in the region right by its argument (left, for negative arguments). *Note Indentation: (emacs)Indentation. - `M-x indent-for-comment' On `M-;' by default. Indent this line's comment to comment column, or insert an empty comment. *Note Comment Commands: (emacs)Comment Commands. - `M-x indent-new-comment-line' On `M-' by default. Break line at point and indent, continuing comment if within one. *Note Multi-Line Comments: (emacs)Multi-Line Comments. As with other language modes, `M-;' gives you a comment at the end of the current line. The column where the comment starts is determined by the variable `comment-column'--default is 40, but it can be changed with `set-comment-column' (on `C-x ;' by default).  File: sml-mode.info, Node: Magic Insertion, Next: SML Mode Defaults, Prev: Indentation, Up: SML Mode 4.3 Electric features ===================== Electric keys are generally pretty irritating, so those provided by SML mode are fairly muted. The only truly electric key is `;', and this has to be enabled to take effect. -- Command: sml-electric-pipe Key: `M-|' When the point is in a `case' statement this opens a new line, indents and inserts `| =>' leaving point just before the double arrow; if the enclosing construct is a `fun' declaration, the newline is indented and the function name copied at the appropriate column. Generally, try it whenever a `|' is wanted--you'll like it! -- Command: sml-electric-space Key: `M-SPC' When the point is after a keyword like `let', this inserts the corresponding predefined skeleton if one exists. Else it just inserts a space. Another way to insert those skeletons is to use `sml-insert-form', described below. -- Command: sml-electric-semi Key: `;' Just inserts a semi-colon, usually. The behaviour of this command is governed by the variable `sml-electric-semi-mode'. -- Variable: sml-electric-semi-mode Default: `nil' If this variable is `nil', `sml-electric-semi' just inserts a semi-colon, otherwise it inserts a semi-colon and a newline, and indents the newline for SML. -- Command: sml-insert-form Key: `C-c ' Interactive short-cut to insert common ML forms (a.k.a. macros, or templates). Recognised forms are `let', `local', `case', `abstype', `datatype', `signature', `structure', and `functor'. Except for `let' and `local', these will prompt for appropriate parameters like functor name and signature, etc.. This command prompts in the mini-buffer, with completion. By default `C-c ' will insert at point, with the indentation of the current column; if you give a prefix argument (i.e., `C-u C-c ') the command will insert a newline first, indent, and then insert the template. `sml-insert-form' is also extensible: see *note Configuration:: for further details.  File: sml-mode.info, Node: SML Mode Defaults, Prev: Magic Insertion, Up: SML Mode 4.4 Indentation defaults ======================== Several variables try to control the indentation algorithm and other features of SML mode. Most of them are still in flux so they are not described here yet. If the default values are not acceptable you can set these variables permanently in your `.emacs' file. *Note Configuration::, for details and examples. -- Variable: sml-indent-level Default: `4' This variable controls the block indentation level.  File: sml-mode.info, Node: Interaction Mode, Next: Configuration, Prev: SML Mode, Up: Top 5 Running ML under Emacs ************************ The most useful feature of SML mode is that it provides a convenient interface to the compiler. How serious users of ML put up with a teletype interface to the compiler is beyond me... but perhaps there are other interfaces to compilers that require one to part with serious money. Such remarks can quickly become dated--in this case, let's hope so! Anyway, SML mode provides an interaction mode, `inferior-sml-mode', where the compiler runs in a separate buffer in a window or frame of its own. You can use this buffer just like a terminal, but it's usually more convenient to mark some text in the SML mode buffer and have Emacs communicate with the sub-process. The features discussed below are syntax-independent, so they should work with a wide range of ML-like tools and compilers. *Note Process Defaults::, for some hints. `inferior-sml-mode' is a specialisation of the `comint' package that comes with Emacs and XEmacs. * Menu: * Running ML:: Commands to run the ML compiler in a buffer * ML Interaction:: Sending program fragments to the compiler * Tracking Errors:: Finding reported syntax errors * Process Defaults:: Setting defaults for process interaction  File: sml-mode.info, Node: Running ML, Next: ML Interaction, Prev: Interaction Mode, Up: Interaction Mode 5.1 Starting the compiler ========================= Start your favourite ML compiler with the command M-x run-sml This creates a process interaction buffer that inherits some key bindings from SML mode and from `comint' (*note Shell Mode: (emacs)Shell Mode.). Starting the ML compiler adds some functions to SML mode buffers so that program text can be communicated between editor and compiler (*note ML Interaction::). The name of the ML compiler is the first thing you should know how to specify: -- Variable: sml-program-name Default: `"sml"' The program to run as ML. You might need to specify the full path name of the program. -- Variable: sml-default-arg Default: `""' Useful for Poly/ML users who may supply a database file, or others who have wrappers for setting various options around the command to run the compiler. Moscow ML people might set this to `"-P full"', etc.. The variable `sml-program-name' is a string holding the name of the program _as you would type it at the shell_. You can always choose a program different to the default by invoking C-u M-x run-sml With the prefix argument Emacs will prompt for the command name and any command line arguments to pass to the compiler. Thereafter Emacs will use this new name as the default, but for a permanent change you should set this in your `.emacs' with, e.g.: (setq sml-program-name "nj-sml") -- Command: run-sml Launches ML as an inferior process in another buffer; if an ML process already exists, just switch to the process buffer. A prefix argument allows you to edit the command line to specify the program, and any command line options. -- Hook: inferior-sml-mode-hook Default: `nil' `M-x run-sml' runs `comint-mode-hook' and `inferior-sml-mode-hook' hooks in that order, but _after_ the compiler is started. Use `inferior-sml-mode-hook' to set any `comint' buffer-local configurations for SML mode you like. -- Command: switch-to-sml Key: `C-c C-s' Switch from the SML buffer to the interaction buffer. By default point will be placed at the end of the process buffer, but a prefix argument will leave point wherever it was before. If you try `C-c C-s' before an ML process has been started, you'll just get an error message to the effect that there's no current process buffer. -- Command: sml-cd When started, the ML compiler's default working directory is the current buffer's default directory. This command allows the working directory to be changed, if the compiler can do this. The variable `sml-cd-command' specifies the compiler command to invoke (*note Process Defaults::).  File: sml-mode.info, Node: ML Interaction, Next: Tracking Errors, Prev: Running ML, Up: Interaction Mode 5.2 Speaking to the compiler ============================ Several commands are defined for sending program fragments to the running compiler. Each of the following commands takes a prefix argument that will switch the input focus to the process buffer afterwards (leaving point at the end of the buffer): -- Command: sml-load-file Key: `C-c C-l' Send a `use file' command to the current ML process. The variable `sml-use-command' is used to define the correct template for the command to invoke (*note Process Defaults::). The default file is the file associated with the current buffer, or the last file loaded if you are in the interaction buffer. -- Command: sml-send-region Key: `C-c C-r' Send the current region of text in the SML buffer. `sml-send-region-and-go' is a similar command for you to bind in SML mode if you wish: it'll send the region and then switch-to-sml. -- Command: sml-send-buffer Key: `C-c C-b' Send the contents of the current buffer to ML.  File: sml-mode.info, Node: Tracking Errors, Next: Process Defaults, Prev: ML Interaction, Up: Interaction Mode 5.3 Finding errors ================== SML mode provides one customisable function for locating the source position of errors reported by the compiler. This should work whether you type `use "puzzle.sml";' into the interaction buffer, or use one of the mechanisms provided for sending programs directly to the compiler--*note ML Interaction::. -- Command: next-error Key: `C-x`' Jump to the source location of the next error reported by the compiler. All the usual error-navigation commands are available, see *note Compilation Mode: (emacs)Compilation Mode.  File: sml-mode.info, Node: Process Defaults, Prev: Tracking Errors, Up: Interaction Mode 5.4 Process defaults ==================== The process interaction code is independent of the compiler used, deliberately, so SML mode will work with a variety of ML compilers and ML-based tools. There are therefore a number of variables that may need to be set correctly before SML mode can speak to the compiler. Things are by default set up for Standard ML of New Jersey, but switching to a new system is quite easy. -- Variable: sml-use-command Default: `"use \"%s\""' Use file command template. Emacs will replace the `%s' with a file name. Note that Emacs requires double quote characters inside strings to be quoted with a backslash. -- Variable: sml-cd-command Default: `"OS.FileSys.chDir \"%s\""' Compiler command to change the working directory. Not all ML systems support this feature (well, Edinburgh (core) ML didn't), but they should. -- Variable: sml-prompt-regexp Default: `"^[-=>#] *"' Matches the ML compiler's prompt: `comint' uses this for various purposes. To customise error reportage for different ML compilers you need to set two further variables before `next-error' can be useful: -- Variable: sml-error-regexp-alist Alist that specifies how to match errors in compiler output. Each elt has the form (REGEXP FILE-IDX LINE-IDX [COLUMN-IDX FILE-FORMAT...]) If REGEXP matches, the FILE-IDX'th subexpression gives the file name, and the LINE-IDX'th subexpression gives the line number. If COLUMN-IDX is given, the COLUMN-IDX'th subexpression gives the column number on that line. If any FILE-FORMAT is given, each is a format string to produce a file name to try; %s in the string is replaced by the text matching the FILE-IDX'th subexpression.  File: sml-mode.info, Node: Configuration, Prev: Interaction Mode, Up: Top 6 Configuration Summary *********************** This (sort of pedagogic) section gives more information on how to configure SML mode: menus, key bindings, hooks and highlighting are discussed, along with a few other random topics. * Menu: * Hooks:: Creating them * Key Bindings:: Binding commands to keys * Highlighting:: Syntax colouring * Advanced Topics:: You may need to speak Emacs Lisp  File: sml-mode.info, Node: Hooks, Next: Key Bindings, Prev: Configuration, Up: Configuration 6.1 Hooks ========= One way to set SML mode variables (*note Indentation Defaults: SML Mode Defaults.), and other defaults, is through the `sml-mode-hook' in your `.emacs'. A simple example: (defun my-sml-mode-hook () "Local defaults for SML mode" (setq sml-indent-level 2) ; conserve on horizontal space (setq words-include-escape t) ; \ loses word break status (setq indent-tabs-mode nil)) ; never ever indent with tabs (add-hook 'sml-mode-hook 'my-sml-mode-hook) The body of `my-sml-mode-hook' is a sequence of assignments. In this case it is not really necessary to set `sml-indent-level' in a hook because this variable is global (most SML mode variables are). With similar effect: (setq sml-indent-level 2) anywhere in your `.emacs' file. The variable `indent-tabs-mode' is automatically made local to the current buffer whenever it is set explicitly, so it _must_ be set in a hook if you always want SML mode to behave like this. Another hook is `inferior-sml-mode-hook'. This can be used to control the behaviour of the interaction buffer through various variables meaningful to `comint'-based packages: (defun my-inf-sml-mode-hook () "Local defaults for inferior SML mode" (add-hook 'comint-output-filter-functions 'comint-truncate-buffer) (setq comint-scroll-show-maximum-output t) (setq comint-input-autoexpand nil)) (add-hook 'inferior-sml-mode-hook 'my-inf-sml-mode-hook) Again, the body is a sequence of assignments. Unless you run several ML compilers simultaneously under one Emacs, this hook will normally only get run once. You might want to look up the documentation (`C-h v' and `C-h f') for these buffer-local `comint' things.  File: sml-mode.info, Node: Key Bindings, Next: Highlighting, Prev: Hooks, Up: Configuration 6.2 Key bindings ================ Customisation (in Emacs) usually entails putting favourite commands on easily remembered keys. Two `keymaps' are defined in SML mode: one is effective in program text buffers (`sml-mode-map') and the other is effective in interaction buffers (`inferior-sml-mode-map'). The initial design ensures that (many of) the default key bindings from the former keymap will also be available in the latter (e.g., `C-c`'). Type `C-h m' in an SML mode buffer to find the default key bindings (and similarly in an ML interaction buffer), and use the hooks provided to install your preferred key bindings. Given that the keymaps are global (variables): (defun my-sml-mode-hook () "Global defaults for SML mode" (define-key sml-mode-map "\C-cd" 'sml-cd)) (add-hook 'sml-mode-hook 'my-sml-mode-hook) This has the effect of binding `sml-cd' to the key `C-c d'. If you want the same behaviour from `C-c d' in the ML buffer: (defun my-inf-sml-mode-hook () "Global defaults for inferior SML mode" (define-key inferior-sml-mode-map "\C-cd" 'sml-cd) ;; NB. for SML/NJ '96 (setq sml-cd-command "OS.FileSys.chDir \"%s\"")) (add-hook 'inferior-sml-mode-hook 'my-inf-sml-mode-hook) There is nothing to stop you rebuilding the entire keymap for SML mode and the ML interaction buffer in your `.emacs' of course: SML mode won't define `sml-mode-map' or `inferior-sml-mode-map' if you have already done so.  File: sml-mode.info, Node: Highlighting, Next: Advanced Topics, Prev: Key Bindings, Up: Configuration 6.3 Syntax colouring ==================== Highlighting is very handy for picking out keywords in the program text, spotting misspelled kewyords, and, if you have Emacs' `ps-print' package installed (you usually do these days), obtaining pretty, even colourful code listings--quite properly for your colourful ML programs. The indentation scheme (strangely enough) also relies on the highlighting code to properly handle nested comments, which is yet another reason to turn on highlighting. To turn on highlighting, use either of: M-x font-lock-mode (add-hook 'sml-mode-hook 'turn-on-font-lock) (global-font-lock-mode 1) The first will turn it on in the current buffer. The second will turn it on in all sml-mode buffers. The last will turn it on everywhere. This is valid for Emacs but maybe not for XEmacs. Check font-lock documentation if you encounter problems.  File: sml-mode.info, Node: Advanced Topics, Prev: Highlighting, Up: Configuration 6.4 Advanced Topics =================== _These forms are bloody useless; can't we have better ones?_ You can indeed. `sml-insert-form' is extensible so all you need to do is create the macros yourself. Define a _keybord macro_ (`C-x (' `C-x )') and give it a suitable name: `sml-addto-forms-alist' prompts for a name, say `NAME', and binds the macro `sml-form-NAME'. Thereafter `C-c NAME' will insert the macro at point, and `C-u C-c NAME' will insert the macro after a `newline-and-indent'. If you want to keep your macros from one editing session to the next, go to your `.emacs' file and call `insert-kbd-macro'; you'll need to add `NAME' to `sml-forms-alist' permanently yourself: (defun my-sml-mode-hook () "Global defaults for SML mode" ;; whatever else you do (add-to-list 'sml-forms-alist '("NAME" . FUNCTION))) If you want to create templates like `case' that prompt for parameters you'll have to do some Lisp programming. The `skeleton' package is a good stating point. Better yet, you can reuse the wrappers used by sml-mode itself in your sml-mode-hook: (add-hook 'sml-mode-hook (lambda () (sml-def-skeleton "case" "Case expr: " str " of" \n _ " => "))) This will redefine `case' in order to leave the `of' on the first line. See the documentation of `skeleton-insert' to get a better understanding of how this works. _I hate that indentation algorithm; can't I tweak it?_ Ah, yes, of course, but this manual will not tell you how. _Can SML mode handle more than one compiler running at once?_ Sure, just rename the `*sml*' buffer and then use `run-sml' as usual. _What needs to be done to support other ML compilers?_ Not much really. Just add the right regular expressions to `sml-error-regexp-alist' and that should be all.  File: sml-mode.info, Node: Command Index, Next: Variable Index, Up: Top Command Index ************* [index] * Menu: * indent-for-tab-command: Indentation. (line 13) * indent-region: Indentation. (line 20) * inferior-sml-mode: Interaction Mode. (line 21) * next-error: Tracking Errors. (line 13) * run-sml: Running ML. (line 47) * sml-back-to-outer-indent: Indentation. (line 26) * sml-cd: Running ML. (line 71) * sml-electric-pipe: Magic Insertion. (line 11) * sml-electric-semi: Magic Insertion. (line 29) * sml-electric-space: Magic Insertion. (line 21) * sml-indent-level: SML Mode Defaults. (line 13) * sml-insert-form: Magic Insertion. (line 42) * sml-load-file: ML Interaction. (line 12) * sml-mode: Basics. (line 9) * sml-mode-info: Getting Help. (line 31) * sml-send-buffer: ML Interaction. (line 28) * sml-send-region: ML Interaction. (line 21) * sml-send-region-and-go: ML Interaction. (line 21) * switch-to-sml: Running ML. (line 61)  File: sml-mode.info, Node: Variable Index, Next: Key Index, Prev: Command Index, Up: Top Variable Index ************** [index] * Menu: * inferior-sml-mode-hook: Running ML. (line 53) * sml-cd-command: Process Defaults. (line 21) * sml-default-arg: Running ML. (line 26) * sml-electric-semi-mode: Magic Insertion. (line 35) * sml-error-regexp-alist: Process Defaults. (line 37) * sml-indent-level: SML Mode Defaults. (line 13) * sml-mode-hook: Basics. (line 21) * sml-mode-info: Getting Help. (line 31) * sml-program-name: Running ML. (line 20) * sml-prompt-regexp: Process Defaults. (line 28) * sml-use-command: Process Defaults. (line 14)  File: sml-mode.info, Node: Key Index, Prev: Variable Index, Up: Top Key Index ********* [index] * Menu: * ;: Magic Insertion. (line 29) * : Indentation. (line 35) * : Indentation. (line 13) * C-c : Magic Insertion. (line 42) * C-c C-b: ML Interaction. (line 28) * C-c C-i: Getting Help. (line 31) * C-c C-l: ML Interaction. (line 12) * C-c C-r: ML Interaction. (line 21) * C-c C-s: Running ML. (line 61) * C-M-\: Indentation. (line 20) * C-x ;: Indentation. (line 57) * C-x : Indentation. (line 41) * C-x`: Tracking Errors. (line 13) * M-;: Indentation. (line 47) * M-: Indentation. (line 53) * M-: Indentation. (line 26) * M-SPC: Magic Insertion. (line 21) * M-|: Magic Insertion. (line 11)  Tag Table: Node: Top187 Node: Copying1782 Node: Introduction2595 Node: Contributors4011 Node: Getting Started5484 Node: Getting Help7192 Node: SML Mode9060 Node: Basics9868 Node: Indentation10787 Node: Magic Insertion12897 Node: SML Mode Defaults15115 Node: Interaction Mode15676 Node: Running ML17025 Node: ML Interaction19894 Node: Tracking Errors21047 Node: Process Defaults21750 Node: Configuration23633 Node: Hooks24141 Node: Key Bindings25998 Node: Highlighting27574 Node: Advanced Topics28579 Node: Command Index30523 Node: Variable Index32039 Node: Key Index32991  End Tag Table sml-mode-5.0/testcases.sml0000644005056000007300000003462111741417724016527 0ustar monnierparallelisme(* Copyright 1999,2004,2007,2010-2012 Stefan Monnier *) (* sml-mode here treats the second `=' as an equal op because it * thinks it's seeing something like "... type t = (s.t = ...)". FIXME! *) functor foo (structure s : S) where type t = s.t = struct (* fixindent *) val bar = fn a1 a2 a3 a5 a6 a4 => 1 val rec bar = fn a1 a2 a3 a5 a6 a4 => 1 val bar = fn a1 a2 a3 a5 a6 a4 => (1 ;( w , s , s , s , a , a , s , a , a ) ;( w ,s ,a ) ;( w , s , a ) ;( w , s , a ) ;( w ,s ,a ) ;3 + a * 4 + let val x = 3 in toto end + if a then b else c ;4) val ber = 1; val sdfg = 1 val tut = fn (x,y) z y e r => body val tut = fn (x,y) => fn z y => fn e r => body val tut = fn (x,y) z y e r => body val tut = (let local val x = 1 in val x = x end val a = 1 val b = 2 local val x = 1 in val x = x end local val x = 1 in val x = x end local val x = 1 in val x = x end (* fixindent *) local val x = 1 in val x = x end val c = 3 in let val x = 3 in x + a * b * c end end) val x = (* From "Christopher Dutchyn" *) (case foo of (* This is actually not valid SML anyway. *) | BAR => baz | BAR => baz) val x = (x := 1; x := 2; (* Testing obedience to user overrides: *) x := 3; (* fixindent *) case x of FOO => 1 | BAR => 2; case x of FOO => 1 | BAR => case y of FAR => 2 | FRA => 3; hello); datatype foobar = FooB of int | FooA of bool * int datatype foo = FOO | BAR of baz and baz = BAZ | QUUX of foo fun toto = if a then b else c datatype foo = FOO | BAR of baz and baz = BAZ (* fixindent *) | QUUX of foo and b = g datatype foo = datatype M.foo val _ = 42 val x = 5 signature S = S' where type foo = int val _ = 42 val foo = [ "blah" , let val x = f 42 in g (x,x,44) end ] val foo = [ "blah", let val x = f 42 in g (x,x,44) end ] val foo = [ "blah", let val x = f 42 in g (x,x,44) end ] val foo = [ "blah" , let val x = f 42 in g (x,x,44) end , foldl (fn ((p,q),s) => g (p,q,Vector.length q) ^ ":" ^ s) "" (Beeblebrox.masterCountList mlist2) , if null mlist2 then ";" else "" ] fun foo (true::rest) = 1 + 2 * foo rest | foo (false::rest) = let val _ = 1 in 2 end + 2 * foo rest val x = if foo then 1 else if bar then 2 else 3 val y = if foo then 1 else if foo then 2 (* Could also be indented by a basic offset. *) else 3 val yt = 4 val x = (if a then b else c; case M.find(m,f) of SOME(fl, filt) => F.APP(F.VAR fl, OU.filter filt vs) | NONE => le | NONE => le | NONE => le; x := x + 1; (case foo of a => f )) val y = ( let fun f1 = let fun g1 x = 2 fun g2 y = 4 local fun toto y = 1 (* val x = 5 *) in fun g3 z = z end in toto end in a;( ( let val f =1 in toto end ) ) foo("(*") * 2; end; let in a ; b end; let in a + b + c ; b end; let in if a then b else c end; let in case a of F => 1 | D => 2 end; let in case a of F => 1 | D => 2 end; let in if a then b else c end; let in if a then b else c end) end; structure Foo = struct val x = 1 end structure Foo = struct val x = 1 end signature FSPLIT = sig type flint = FLINT.prog val split: flint -> flint * flint option end structure FSplit :> FSPLIT = struct local structure F = FLINT structure S = IntRedBlackSet structure M = FLINTIntMap structure O = Option structure OU = OptUtils structure FU = FlintUtil structure LT = LtyExtern structure PO = PrimOp structure PP = PPFlint structure CTRL = FLINT_Control in val say = Control_Print.say fun bug msg = ErrorMsg.impossible ("FSplit: "^msg) fun buglexp (msg,le) = (say "\n"; PP.printLexp le; say " "; bug msg) fun bugval (msg,v) = (say "\n"; PP.printSval v; say " "; bug msg) fun assert p = if p then () else bug ("assertion failed") type flint = F.prog val mklv = LambdaVar.mkLvar val cplv = LambdaVar.dupLvar fun S_rmv(x, s) = S.delete(s, x) handle NotFound => s fun addv (s,F.VAR lv) = S.add(s, lv) | addv (s,_) = s fun addvs (s,vs) = foldl (fn (v,s) => addv(s, v)) s vs fun rmvs (s,lvs) = foldl (fn (l,s) => S_rmv(l, s)) s lvs exception Unknown fun split (fdec as (fk,f,args,body)) = let val {getLty,addLty,...} = Recover.recover (fdec, false) val m = Intmap.new(64, Unknown) fun addpurefun f = Intmap.add m (f, false) fun funeffect f = (Intmap.map m f) handle Uknown => true (* sexp: env -> lexp -> (leE, leI, fvI, leRet) * - env: IntSetF.set current environment * - lexp: lexp expression to split * - leRet: lexp the core return expression of lexp * - leE: lexp -> lexp recursively split lexp: leE leRet == lexp * - leI: lexp option inlinable part of lexp (if any) * - fvI: IntSetF.set free variables of leI: FU.freevars leI == fvI * * sexp splits the lexp into an expansive part and an inlinable part. * The inlinable part is guaranteed to be side-effect free. * The expansive part doesn't bother to eliminate unused copies of * elements copied to the inlinable part. * If the inlinable part cannot be constructed, leI is set to F.RET[]. * This implies that fvI == S.empty, which in turn prevents us from * mistakenly adding anything to leI. *) fun sexp env lexp = (* fixindent *) let (* non-side effecting binds are copied to leI if exported *) fun let1 (le,lewrap,lv,vs,effect) = let val (leE,leI,fvI,leRet) = sexp (S.add(env, lv)) le val leE = lewrap o leE in if effect orelse not (S.member(fvI, lv)) then (leE, leI, fvI, leRet) else (leE, lewrap leI, addvs(S_rmv(lv, fvI), vs), leRet) end in case lexp (* we can completely move both RET and TAPP to the I part *) of F.RECORD (rk,vs,lv,le as F.RET [F.VAR lv']) => if lv' = lv then (fn e => e, lexp, addvs(S.empty, vs), lexp) else (fn e => e, le, S.singleton lv', le) | F.RET vs => (fn e => e, lexp, addvs(S.empty, vs), lexp) | F.TAPP (F.VAR tf,tycs) => (fn e => e, lexp, S.singleton tf, lexp) (* recursive splittable lexps *) | F.FIX (fdecs,le) => sfix env (fdecs, le) | F.TFN (tfdec,le) => stfn env (tfdec, le) (* binding-lexps *) | F.CON (dc,tycs,v,lv,le) => let1(le, fn e => F.CON(dc, tycs, v, lv, e), lv, [v], false) | F.RECORD (rk,vs,lv,le) => let1(le, fn e => F.RECORD(rk, vs, lv, e), lv, vs, false) | F.SELECT (v,i,lv,le) => let1(le, fn e => F.SELECT(v, i, lv, e), lv, [v], false) | F.PRIMOP (po,vs,lv,le) => let1(le, fn e => F.PRIMOP(po, vs, lv, e), lv, vs, PO.effect(#2 po)) (* IMPROVEME: lvs should not be restricted to [lv] *) | F.LET(lvs as [lv],body as F.TAPP (v,tycs),le) => let1(le, fn e => F.LET(lvs, body, e), lv, [v], false) | F.LET (lvs as [lv],body as F.APP (v as F.VAR f,vs),le) => let1(le, fn e => F.LET(lvs, body, e), lv, v::vs, funeffect f) | F.SWITCH (v,ac,[(dc as F.DATAcon(_,_,lv),le)],NONE) => let1(le, fn e => F.SWITCH(v, ac, [(dc, e)], NONE), lv, [v], false) | F.LET (lvs,body,le) => let val (leE,leI,fvI,leRet) = sexp (S.union(S.addList(S.empty, lvs), env)) le in (fn e => F.LET(lvs, body, leE e), leI, fvI, leRet) end (* useless sophistication *) | F.APP (F.VAR f,args) => if funeffect f then (fn e => e, F.RET[], S.empty, lexp) else (fn e => e, lexp, addvs(S.singleton f, args), lexp) (* other non-binding lexps result in unsplittable functions *) | (F.APP _ | F.TAPP _) => bug "strange (T)APP" | (F.SWITCH _ | F.RAISE _ | F.BRANCH _ | F.HANDLE _) => (fn e => e, F.RET[], S.empty, lexp) end (* Functions definitions fall into the following categories: * - inlinable: if exported, copy to leI * - (mutually) recursive: don't bother * - non-inlinable non-recursive: split recursively *) and sfix env (fdecs,le) = let val nenv = S.union(S.addList(S.empty, map #2 fdecs), env) val (leE,leI,fvI,leRet) = sexp nenv le val nleE = fn e => F.FIX(fdecs, leE e) in case fdecs of [({inline=inl as (F.IH_ALWAYS | F.IH_MAYBE _),...},f,args,body)] => let val min = case inl of F.IH_MAYBE(n,_) => n | _ => 0 in if not(S.member(fvI, f)) orelse min > !CTRL.splitThreshold then (nleE, leI, fvI, leRet) else (nleE, F.FIX(fdecs, leI), rmvs(S.union(fvI, FU.freevars body), f::(map #1 args)), leRet) end | [fdec as (fk as {cconv=F.CC_FCT,...},_,_,_)] => sfdec env (leE,leI,fvI,leRet) fdec | _ => (nleE, leI, fvI, leRet) end and sfdec env (leE,leI,fvI,leRet) (fk,f,args,body) = let val benv = S.union(S.addList(S.empty, map #1 args), env) val (bodyE,bodyI,fvbI,bodyRet) = sexp benv body in case bodyI of F.RET[] => (fn e => F.FIX([(fk, f, args, bodyE bodyRet)], e), leI, fvI, leRet) | _ => let val fvbIs = S.listItems(S.difference(fvbI, benv)) val (nfk,fkE) = OU.fk_wrap(fk, NONE) (* fdecE *) val fE = cplv f val fErets = (map F.VAR fvbIs) val bodyE = bodyE(F.RET fErets) (* val tmp = mklv() val bodyE = bodyE(F.RECORD(F.RK_STRUCT, map F.VAR fvbIs, tmp, F.RET[F.VAR tmp])) *) val fdecE = (fkE, fE, args, bodyE) val fElty = LT.ltc_fct(map #2 args, map getLty fErets) val _ = addLty(fE, fElty) (* fdecI *) val fkI = {inline=F.IH_ALWAYS, cconv=F.CC_FCT, known=true, isrec=NONE} val argsI = (map (fn lv => (lv, getLty(F.VAR lv))) fvbIs) @ args val fdecI as (_,fI,_,_) = FU.copyfdec(fkI,f,argsI,bodyI) val _ = addpurefun fI (* nfdec *) val nargs = map (fn (v,t) => (cplv v, t)) args val argsv = map (fn (v,t) => F.VAR v) nargs val nbody = let val lvs = map cplv fvbIs in F.LET(lvs, F.APP(F.VAR fE, argsv), F.APP(F.VAR fI, (map F.VAR lvs)@argsv)) end (* let val lv = mklv() in F.LET([lv], F.APP(F.VAR fE, argsv), F.APP(F.VAR fI, (F.VAR lv)::argsv)) end *) val nfdec = (nfk, f, nargs, nbody) (* and now, for the whole F.FIX *) fun nleE e = F.FIX([fdecE], F.FIX([fdecI], F.FIX([nfdec], leE e))) in if not(S.member(fvI, f)) then (nleE, leI, fvI, leRet) else (nleE, F.FIX([fdecI], F.FIX([nfdec], leI)), S.add(S.union(S_rmv(f, fvI), S.intersection(env, fvbI)), fE), leRet) end end (* TFNs are kinda like FIX except there's no recursion *) and stfn env (tfdec as (tfk,tf,args,body),le) = let val (bodyE,bodyI,fvbI,bodyRet) = if #inline tfk = F.IH_ALWAYS then (fn e => body, body, FU.freevars body, body) else sexp env body val nenv = S.add(env, tf) val (leE,leI,fvI,leRet) = sexp nenv le in case (bodyI, S.listItems(S.difference(fvbI, env))) of ((F.RET _ | F.RECORD(_,_,_,F.RET _)),_) => (* split failed *) (fn e => F.TFN((tfk, tf, args, bodyE bodyRet), leE e), leI, fvI, leRet) | (_,[]) => (* everything was split out *) let val ntfdec = ({inline=F.IH_ALWAYS}, tf, args, bodyE bodyRet) val nlE = fn e => F.TFN(ntfdec, leE e) in if not(S.member(fvI, tf)) then (nlE, leI, fvI, leRet) else (nlE, F.TFN(ntfdec, leI), S_rmv(tf, S.union(fvI, fvbI)), leRet) end | (_,fvbIs) => let (* tfdecE *) val tfE = cplv tf val tfEvs = map F.VAR fvbIs val bodyE = bodyE(F.RET tfEvs) val tfElty = LT.lt_nvpoly(args, map getLty tfEvs) val _ = addLty(tfE, tfElty) (* tfdecI *) val tfkI = {inline=F.IH_ALWAYS} val argsI = map (fn (v,k) => (cplv v, k)) args (* val tmap = ListPair.map (fn (a1,a2) => * (#1 a1, LT.tcc_nvar(#1 a2))) * (args, argsI) *) val bodyI = FU.copy tmap M.empty (F.LET(fvbIs, F.TAPP(F.VAR tfE, map #2 tmap), bodyI)) (* F.TFN *) fun nleE e = F.TFN((tfk, tfE, args, bodyE), F.TFN((tfkI, tf, argsI, bodyI), leE e)) in if not(S.member(fvI, tf)) then (nleE, leI, fvI, leRet) else (nleE, F.TFN((tfkI, tf, argsI, bodyI), leI), S.add(S.union(S_rmv(tf, fvI), S.intersection(env, fvbI)), tfE), leRet) end end (* here, we use B-decomposition, so the args should not be * considered as being in scope *) val (bodyE,bodyI,fvbI,bodyRet) = sexp S.empty body in case (bodyI, bodyRet) of (F.RET _,_) => ((fk, f, args, bodyE bodyRet), NONE) | (_,F.RECORD (rk,vs,lv,F.RET[lv'])) => let val fvbIs = S.listItems fvbI (* fdecE *) val bodyE = bodyE(F.RECORD(rk, vs@(map F.VAR fvbIs), lv, F.RET[lv'])) val fdecE as (_,fE,_,_) = (fk, cplv f, args, bodyE) (* fdecI *) val argI = mklv() val argLtys = (map getLty vs) @ (map (getLty o F.VAR) fvbIs) val argsI = [(argI, LT.ltc_str argLtys)] val (_,bodyI) = foldl (fn (lv,(n,le)) => (n+1, F.SELECT(F.VAR argI, n, lv, le))) (length vs, bodyI) fvbIs val fdecI as (_,fI,_,_) = FU.copyfdec (fk, f, argsI, bodyI) val nargs = map (fn (v,t) => (cplv v, t)) args in (fdecE, SOME fdecI) (* ((fk, f, nargs, F.FIX([fdecE], F.FIX([fdecI], F.LET([argI], F.APP(F.VAR fE, map (F.VAR o #1) nargs), F.APP(F.VAR fI, [F.VAR argI]))))), NONE) *) end | _ => (fdec, NONE) (* sorry, can't do that *) (* (PPFlint.printLexp bodyRet; bug "couldn't find the returned record") *) end end end sml-mode-5.0/INSTALL0000644005056000007300000000340711741417724015043 0ustar monnierparallelismeSML-MODE shouldn't require any special external support package, as far as I can rememebr. Just a recent copy of Emacs or XEmacs. Installation of the program =================================== 1. Edit the file `Makefile' to reflect the situation at your site. The only things you have to change is the definition of `lispdir' and `infodir'. The elisp files will be copied to `lispdir', and the info file to `infodir'. 2. Have some sorbet. 3. Type `make install' in the source directory. This will byte-compile all `.el' files and copy all into the directory you specified in step 1. It will also copy the info files (and add a corresponding entry to the info-dir file if install-info can be found). If you only want to create the compiled elisp files, you can just type `make elcfiles' instead. 4. Edit the file `site-start.el' in your emacs lisp directory (usually `/usr/local/share/emacs/site-lisp' or something similar) and make it load the file `sml-mode-startup.el'. It contains a couple of `auto-load's that facilitates the use of sml-mode. Alternatively, you can just use `make install_startup'. If you're only installing it for yourself rather than for the whole system, then use something like `make install_startup startupfile=$HOME/.emacs'. 5. If you had copied the contents of a previous sml-mode-startup.el file to your site-start.el (or .emacs), you might want to remove that. How to make typeset documentation from the TeXinfo manual ========================================================= If you have TeX installed at your site, you can make a typeset version of the manual typing ``make dvi''. If you prefer a postscript version of this file, just use ``make postscript''. sml-mode-5.0/sml-mode.texi0000644005056000007300000011033311741417724016417 0ustar monnierparallelisme\input texinfo @c -*-texinfo-*- @comment "@(#)$Name$:$Id$" @comment Documentation for the GNU Emacs SML mode. @comment Copyright (C) 1997-1999 Matthew J.@: Morley @comment This file is part of the sml-mode distribution. @comment sml-mode is free software; you can redistribute it and/or modify @comment it under the terms of the GNU General Public License as published by @comment the Free Software Foundation; either version 3 of the License, @comment or (at your option) any later version. @comment sml-mode is distributed in the hope that it will be useful, @comment but WITHOUT ANY WARRANTY; without even the implied warranty of @comment MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @comment GNU General Public License for more details. @comment You should have received a copy of the GNU General Public License @comment along with sml-mode; see the file COPYING. If not, write to @comment the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. @setfilename sml-mode.info @settitle SML mode - The Emacs SML editing mode @dircategory Emacs @direntry * sml:(sml-mode). Emacs mode for editing SML @end direntry @setchapternewpage on @titlepage @sp 5 @center @titlefont{Editing and Running Standard ML} @center @titlefont{under GNU Emacs} @sp 5 @center {SML mode, Version $Name$} @center {August 1999} @sp 2 @author Authors: Matthew J.@: Morley and Stefan Monnier @page @vskip 0pt plus 1filll Copyright @copyright{} (Anon) @sp 1 @noindent GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. @sp 1 @noindent SML mode is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. @sp 1 @noindent You should have received a copy of the GNU General Public License along with GNU Emacs; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. @end titlepage @setchapternewpage off @headings double @c ============================================================ TOP NODE @node Top, Copying, (dir), (dir) @ifinfo @chapter SML Mode Info @c == Top, Copying, (dir), (dir) ======================================= @noindent You are looking at the top node of the Info tree documenting @sc{sml-mode} (Version $Name$). Not all functions are documented here, but those that aren't you probably won't miss. All commands and settable variables have built-in documentation, as per usual Emacs conventions. @end ifinfo @menu * Copying:: You can copy SML mode * Introduction:: Setting things up * SML Mode:: Editing SML source * Interaction Mode:: Running ML processes * Configuration:: Menus, highlighting, setting defaults Indexes * Command Index:: Commands you can invoke * Variable Index:: Variables you can set * Key Index:: Default keybindings Introduction * Contributors:: Who did what * Getting Started:: What to tell Emacs * Getting Help:: How Emacs can help SML Mode * Basics:: On entering SML mode * Indentation:: Prettying SML text * Magic Insertion:: Templates and electric keys * SML Mode Defaults:: Variables controlling indentation Interaction Mode * Running ML:: Commands to run the ML compiler in a buffer * ML Interaction:: Sending program fragments to the compiler * Tracking Errors:: Finding reported syntax errors * Process Defaults:: Setting defaults for process interaction Configuration * Hooks:: Creating hooks * Key Bindings:: Binding commands to keys * Highlighting:: Syntax colouring * Advanced Topics:: You may need to speak Emacs Lisp @end menu @c ============================================================= COPYING @node Copying, Introduction, Top, Top @ifinfo @chapter Copying @c == Copying, Introduction, Top, Top ================================== @noindent You can freely copy, modify and redistribute SML mode because it's made available under the liberal terms of the GNU General Public License. GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. SML mode is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Emacs; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. @end ifinfo @c ======================================================== INTRODUCTION @node Introduction, SML Mode, Copying, Top @chapter Introduction @c == Introduction, SML Mode, Copying, Top ============================= @noindent SML mode is a major mode for Emacs for editing Standard ML. It has some novel bugs, and some nice features: @itemize @bullet @item Automatic indentation of sml code---a number of variables to customise the indentation. @item Easy insertion for commonly used templates like let, local, signature, and structure declarations, with minibuffer prompting for types and expressions. @item Magic pipe insertion: @code{|} automatically determines if it is used in a case or fun construct, and indents the next line as appropriate, inserting @code{=>} or the name of the function. @item Inferior shell for running ML. There's no need to leave Emacs, just keep on editing while the compiler runs in another window. @item Automatic ``use file'' in the inferior shell---you can send files, buffers, or regions of code to the ML subprocess. @item Menus, and syntax and keyword highlighting supported for Emacs 19 and derivatives. @item Parsing errors from the inferior shell, and repositioning the source with next-error---just like in c-mode. @item SML mode can be easily configured to work with a number of Standard ML compilers, and other SML based tools. @end itemize @menu * Contributors:: Who did what * Getting Started:: What to tell Emacs * Getting Help:: How Emacs can help @end menu @c ======================================================== CONTRIBUTORS @node Contributors, Getting Started, Introduction, Introduction @section Contributors to the SML mode @cindex Contributors @cindex Authors Contributions to the package are welcome. I have limited time to work on this project, but I will gladly add any code that you contribute to me to this package. Although the history of sml-mode is obscure, it seems that the following persons have made contributions to sml-mode: @itemize @bullet @item Lars Bo Nielsen wrote the original version of the code, providing the sml editing mode and the inferior-sml support. @item Olin Shivers (@samp{shivers@@ai.mit.edu}) hacked the inferior-sml support to use comint and call the whole thing ml-mode. @item Steven Gilmore supposedly provided some early attempt at menubar support. @item Matthew J. Morley (@samp{matthew@@verisity.com}) was maintainer for a long time (until version 3.4) and provided many additions and fixes in all areas. @item Frederick Knabe (@samp{knabe@@ecrc.de}) provided the original code for font-lock and hilite support as well as for proper handling of nested comments and of all the string escape sequences. @item Matthias Blume (@samp{blume@@kurims.kyoto-u.ac.jp}) provided a sml-make which was replaced by sml-compile. @item Monnier Stefan (@samp{monnier@@iro.umontreal.ca}) completely reworked the indentation engine as well as most of the rest of the code and is the current maintainer since after version 3.4. @end itemize @c ===================================================== GETTING STARTED @node Getting Started, Getting Help, Contributors, Introduction @section Getting started @c == Getting Started, Getting Help, Contributors, Introduction ======== @noindent With luck your system administrator will have installed SML mode somewhere convenient, so it will just magically all work---you can skip the rest of this getting started section. Otherwise you will need to tell Emacs where to find all the SML mode @file{.el} files, and when to use them. The where is addressed by locating the Lisp code on your Emacs Lisp load path---you may have to create a directory for this, say @file{/home/mjm/elisp}, and then insert the following lines in your @file{/home/mjm/.emacs} file: @lisp (add-to-list 'load-path "/home/mjm/elisp") (autoload 'sml-mode "sml-mode" "Major mode for editing SML." t) (autoload 'run-sml "sml-proc" "Run an inferior SML process." t) @end lisp @noindent The first line adjusts Emacs' internal search path so it can locate the Lisp source you have copied to that directory; the second and third lines tell Emacs to load the code automatically when it is needed. You can then switch any Emacs buffer into SML mode by entering the command @example M-x sml-mode @end example @noindent It is usually more convenient to have Emacs automatically place the buffer in SML mode whenever you visit a file containing ML programs. The simplest way of achieving this is to put something like @lisp (add-to-list 'auto-mode-alist '("\\.\\(sml\\|sig\\)\\'" . sml-mode)) @end lisp @noindent also in your @file{.emacs} file. Subsequently (after a restart), any files with these extensions will be placed in SML mode buffers when you visit them. You may want to pre-compile the @file{sml-*.el} files (@kbd{M-x byte-compile-file}) for greater speed---byte compiled code loads and runs somewhat faster. @c ======================================================== GETTING HELP @node Getting Help, , Getting Started, Introduction @section Help! @c == Getting Help, , Getting Started, Introduction ==================== @noindent You're reading it. Apart from the on-line info tree (@kbd{C-h i} is the Emacs key to enter the @code{info} system---you should follow the brief tutorial if this is unfamiliar), there are further details on specific commands in their documentation strings. Only the most useful SML mode commands are documented in the info tree: to find out more use Emacs' help facilities. Briefly, to get help on a specific function use @kbd{C-h f} and enter the command name. All (almost all, then) SML mode commands begin with @code{sml-}, so if you type this and press @key{TAB} (for completion) you will get a list of all commands. Another way is to use @kbd{C-h a} and enter the string @code{sml}. This is command apropos; it will list all commands with that sub-string in their names, and any key binding they may have in the current buffer. Command apropos gives a one-line synopsis of what each command does. Some commands are also variables---such things are allowed in Lisp, if not in ML! @xref{Command Index}, for a list of (info) documented functions. @xref{Variable Index}, for a list of user settable variables to control the behaviour of SML mode. Before accessing this information on-line from within Emacs you may have to set the variable @code{sml-mode-info}. Put in your @file{.emacs} file something like: @vindex sml-mode-info @findex sml-mode-info @kindex @kbd{C-c C-i} @lisp (setq sml-mode-info "/home/mjm/info/sml-mode.info") @end lisp @noindent When different from the default this variable should be a string giving the absolute name of the @file{.info} file. Then @kbd{C-c C-i} in SML mode (i.e., the command @kbd{M-x sml-mode-info}) will bring up the manual. This help is also accessible from the menu. (Resetting this variable will not be necessary if your site administrator has been kind enough to install SML mode and its attendant documentation in the Emacs hierarchy.) @c ============================================================ SML MODE @node SML Mode, Interaction Mode, Introduction, Top @chapter Editing with SML Mode @c == SML Mode, Interaction Mode, Introduction, Top ==================== @noindent Now SML mode provides just a few additional editing commands. Most of the work has gone into implementing the indentation algorithm which, if you think about it, has to be complicated for a language like ML. @xref{SML Mode Defaults,,Indentation Defaults}, for details on how to control some of the behaviour of the indentation algorithm. Principal goodies are the `electric pipe' feature, and the ability to insert common SML forms (macros or templates). @menu * Basics:: On entering SML mode * Indentation:: Prettying SML text * Magic Insertion:: Templates and electric keys * SML Mode Defaults:: Variables controlling indentation @end menu @c ============================================================== BASICS @node Basics, Indentation, SML Mode, SML Mode @section On entering SML mode @c == Basics, Indentation, SML Mode, SML Mode ========================== @noindent @deffn Command sml-mode This switches a buffer into SML mode. This is a @emph{major mode} in Emacs. To get out of SML mode the buffer's major mode must be set to something else, like @t{text-mode}. @xref{Getting Started}, for details on how to set this up automatically when visiting an SML file. @end deffn Emacs is all hooks of course. A hook is a variable: if the variable is non-nil it binds a list of Emacs Lisp functions to be run in some order (usually left to right). You can customise SML mode with these hooks: @defvr Hook sml-mode-hook Default: @code{nil} This is run every time a new SML mode buffer is created (or if you type @kbd{M-x sml-mode}). This is one place to put your preferred key bindings. @xref{Configuration}, for some examples. @end defvr @c ========================================================= INDENTATION @node Indentation, Magic Insertion, Basics, SML Mode @section Automatic indentation @c == Indentation, Magic Insertion, Basics, SML Mode =================== @noindent ML is a complicated language to parse, let alone compile. The indentation algorithm is a little wooden (for some tastes), and the best advice is not to fight it! There are several variables that can be adjusted to control the indentation algorithm (@pxref{SML Mode Defaults,,Customising SML Mode}, below). @deffn Command indent-for-tab-command Key: @key{TAB} @kindex @key{TAB} This command indents the current line. If you set the indentation of the previous line by hand, @code{indent-for-tab-command} will indent relative to this setting. @end deffn @deffn Command indent-region Key: @kbd{C-M-\} @kindex @kbd{C-M-\} Indent the current region. Be patient if the region is large (like the whole buffer). @end deffn @deffn Command sml-back-to-outer-indent Key: @kbd{M-@key{TAB}} @kindex @kbd{M-@key{TAB}} Unindents the line to the next outer level of indentation. @end deffn Further indentation commands that Emacs provides (generically, for all modes) that you may like to recall: @itemize @minus @item @kbd{M-x newline-and-indent} On @key{LFD} by default. @kindex @key{LFD} Insert a newline, then indent according to the major mode. @xref{Program Indent,,Indentation for Programs,emacs,The Emacs Editor Manual}, for details. @item @kbd{M-x indent-rigidly} On @kbd{C-x @key{TAB}} by default. @kindex @kbd{C-x @key{TAB}} Moves all lines in the region right by its argument (left, for negative arguments). @xref{Indentation,,,emacs,The Emacs Editor Manual}. @item @kbd{M-x indent-for-comment} On @kbd{M-;} by default. @kindex @kbd{M-;} Indent this line's comment to comment column, or insert an empty comment. @xref{Comment Commands,,,emacs,The Emacs Editor Manual}. @item @kbd{M-x indent-new-comment-line} On @kbd{M-@key{LFD}} by default. @kindex @kbd{M-@key{LFD}} Break line at point and indent, continuing comment if within one. @xref{Multi-Line Comments,,,emacs,The Emacs Editor Manual}. @end itemize @kindex @kbd{C-x ;} As with other language modes, @kbd{M-;} gives you a comment at the end of the current line. The column where the comment starts is determined by the variable @code{comment-column}---default is 40, but it can be changed with @code{set-comment-column} (on @kbd{C-x ;} by default). @c ===================================================== MAGIC INSERTION @node Magic Insertion, SML Mode Defaults, Indentation, SML Mode @section Electric features @c == Magic Insertion, SML Mode Defaults, Indentation, SML Mode ======== @noindent Electric keys are generally pretty irritating, so those provided by SML mode are fairly muted. The only truly electric key is @kbd{;}, and this has to be enabled to take effect. @deffn Command sml-electric-pipe Key: @kbd{M-|} @kindex @kbd{M-|} When the point is in a `case' statement this opens a new line, indents and inserts @code{| =>} leaving point just before the double arrow; if the enclosing construct is a `fun' declaration, the newline is indented and the function name copied at the appropriate column. Generally, try it whenever a @code{|} is wanted---you'll like it! @end deffn @deffn Command sml-electric-space Key: @kbd{M-SPC} @kindex @kbd{M-SPC} When the point is after a keyword like `let', this inserts the corresponding predefined skeleton if one exists. Else it just inserts a space. Another way to insert those skeletons is to use @code{sml-insert-form}, described below. @end deffn @deffn Command sml-electric-semi Key: @kbd{;} @kindex @kbd{;} Just inserts a semi-colon, usually. The behaviour of this command is governed by the variable @code{sml-electric-semi-mode}. @end deffn @defvr Variable sml-electric-semi-mode Default: @code{nil} If this variable is @code{nil}, @code{sml-electric-semi} just inserts a semi-colon, otherwise it inserts a semi-colon and a newline, and indents the newline for SML. @end defvr @deffn Command sml-insert-form Key: @kbd{C-c @key{RET}} @kindex @kbd{C-c @key{RET}} Interactive short-cut to insert common ML forms (a.k.a.@: macros, or templates). Recognised forms are `let', `local', `case', `abstype', `datatype', `signature', `structure', and `functor'. Except for `let' and `local', these will prompt for appropriate parameters like functor name and signature, etc.. This command prompts in the mini-buffer, with completion. By default @kbd{C-c @key{RET}} will insert at point, with the indentation of the current column; if you give a prefix argument (i.e., @kbd{C-u C-c @key{RET}}) the command will insert a newline first, indent, and then insert the template. @end deffn @code{sml-insert-form} is also extensible: see @ref{Configuration} for further details. @c ======================================================= MODE DEFAULTS @node SML Mode Defaults, , Magic Insertion, SML Mode @section Indentation defaults @c == SML Mode Defaults, , Magic Insertion, SML Mode =================== @noindent Several variables try to control the indentation algorithm and other features of SML mode. Most of them are still in flux so they are not described here yet. If the default values are not acceptable you can set these variables permanently in your @file{.emacs} file. @xref{Configuration}, for details and examples. @defvr Variable sml-indent-level @findex sml-indent-level Default: @code{4} This variable controls the block indentation level. @end defvr @c end vtable @c ========================================================= INTERACTION @node Interaction Mode, Configuration, SML Mode, Top @chapter Running ML under Emacs @c == Interaction Mode, Configuration, SML Mode, Top =================== @noindent The most useful feature of SML mode is that it provides a convenient interface to the compiler. How serious users of ML put up with a teletype interface to the compiler is beyond me@.@.@. but perhaps there are other interfaces to compilers that require one to part with serious money. Such remarks can quickly become dated---in this case, let's hope so! Anyway, SML mode provides an interaction mode, @code{inferior-sml-mode}, where the compiler runs in a separate buffer in a window or frame of its own. You can use this buffer just like a terminal, but it's usually more convenient to mark some text in the SML mode buffer and have Emacs communicate with the sub-process. The features discussed below are syntax-independent, so they should work with a wide range of ML-like tools and compilers. @xref{Process Defaults}, for some hints. @findex inferior-sml-mode @code{inferior-sml-mode} is a specialisation of the @file{comint} package that comes with Emacs and XEmacs. @menu * Running ML:: Commands to run the ML compiler in a buffer * ML Interaction:: Sending program fragments to the compiler * Tracking Errors:: Finding reported syntax errors * Process Defaults:: Setting defaults for process interaction @end menu @c ========================================================== RUNNING ML @node Running ML, ML Interaction, Interaction Mode, Interaction Mode @section Starting the compiler @c == Running ML, ML Interaction, Interaction Mode, Interaction Mode == @noindent Start your favourite ML compiler with the command @example @kbd{M-x run-sml} @end example @noindent This creates a process interaction buffer that inherits some key bindings from SML mode and from @file{comint} (@pxref{Shell Mode, , , emacs, The Emacs Editor Manual}). Starting the ML compiler adds some functions to SML mode buffers so that program text can be communicated between editor and compiler (@pxref{ML Interaction}). The name of the ML compiler is the first thing you should know how to specify: @defvar sml-program-name Default: @code{"sml"} The program to run as ML. You might need to specify the full path name of the program. @end defvar @defvar sml-default-arg Default: @code{""} Useful for Poly/ML users who may supply a database file, or others who have wrappers for setting various options around the command to run the compiler. Moscow ML people might set this to @code{"-P full"}, etc.. @end defvar The variable @code{sml-program-name} is a string holding the name of the program @emph{as you would type it at the shell}. You can always choose a program different to the default by invoking @example @kbd{C-u M-x run-sml} @end example @noindent With the prefix argument Emacs will prompt for the command name and any command line arguments to pass to the compiler. Thereafter Emacs will use this new name as the default, but for a permanent change you should set this in your @file{.emacs} with, e.g.: @lisp (setq sml-program-name "nj-sml") @end lisp @deffn Command run-sml Launches ML as an inferior process in another buffer; if an ML process already exists, just switch to the process buffer. A prefix argument allows you to edit the command line to specify the program, and any command line options. @end deffn @defvr Hook inferior-sml-mode-hook Default: @code{nil} @kbd{M-x run-sml} runs @code{comint-mode-hook} and @code{inferior-sml-mode-hook} hooks in that order, but @emph{after} the compiler is started. Use @code{inferior-sml-mode-hook} to set any @code{comint} buffer-local configurations for SML mode you like. @end defvr @deffn Command switch-to-sml Key: @kbd{C-c C-s} @kindex @kbd{C-c C-s} Switch from the SML buffer to the interaction buffer. By default point will be placed at the end of the process buffer, but a prefix argument will leave point wherever it was before. If you try @kbd{C-c C-s} before an ML process has been started, you'll just get an error message to the effect that there's no current process buffer. @end deffn @deffn Command sml-cd When started, the ML compiler's default working directory is the current buffer's default directory. This command allows the working directory to be changed, if the compiler can do this. The variable @code{sml-cd-command} specifies the compiler command to invoke (@pxref{Process Defaults}). @end deffn @c ======================================================== SENDING TEXT @node ML Interaction, Tracking Errors, Running ML, Interaction Mode @section Speaking to the compiler @c == ML Interaction, Tracking Errors, Running ML, Interaction Mode ==== @noindent Several commands are defined for sending program fragments to the running compiler. Each of the following commands takes a prefix argument that will switch the input focus to the process buffer afterwards (leaving point at the end of the buffer): @deffn Command sml-load-file Key: @kbd{C-c C-l} @kindex @kbd{C-c C-l} Send a `use file' command to the current ML process. The variable @code{sml-use-command} is used to define the correct template for the command to invoke (@pxref{Process Defaults}). The default file is the file associated with the current buffer, or the last file loaded if you are in the interaction buffer. @end deffn @deffn Command sml-send-region @findex sml-send-region-and-go Key: @kbd{C-c C-r} @kindex @kbd{C-c C-r} Send the current region of text in the SML buffer. @code{sml-send-region-and-go} is a similar command for you to bind in SML mode if you wish: it'll send the region and then switch-to-sml. @end deffn @c @deffn Command sml-send-function @c @findex sml-send-function-and-go @c Send the enclosing `function' definition. Contrary to the suggestive @c name, this command @emph{does not} try to determine the extent of the @c function definition because that is too difficult with ML. Instead @c this just sends the enclosing @emph{paragraph} (delimited by blank @c lines or form-feed characters). @c @end deffn @deffn Command sml-send-buffer Key: @kbd{C-c C-b} @kindex @kbd{C-c C-b} Send the contents of the current buffer to ML. @end deffn @c ===================================================== TRACKING ERRORS @node Tracking Errors, Process Defaults, ML Interaction, Interaction Mode @section Finding errors @c == Tracking Errors, Process Defaults, ML Interaction, Interaction Mode @noindent SML mode provides one customisable function for locating the source position of errors reported by the compiler. This should work whether you type @code{use "puzzle.sml";} into the interaction buffer, or use one of the mechanisms provided for sending programs directly to the compiler---@pxref{ML Interaction}. @deffn Command next-error @findex next-error Key: @kbd{C-x`} @kindex @kbd{C-x`} Jump to the source location of the next error reported by the compiler. All the usual error-navigation commands are available, see @pxref{Compilation Mode, , , emacs, The Emacs Editor Manual}. @end deffn @c ==================================================== PROCESS DEFAULTS @node Process Defaults, , Tracking Errors, Interaction Mode @section Process defaults @c == Process Defaults, , Tracking Errors, Interaction Mode ============ @noindent The process interaction code is independent of the compiler used, deliberately, so SML mode will work with a variety of ML compilers and ML-based tools. There are therefore a number of variables that may need to be set correctly before SML mode can speak to the compiler. Things are by default set up for Standard ML of New Jersey, but switching to a new system is quite easy. @defvar sml-use-command Default: @code{"use \"%s\""} Use file command template. Emacs will replace the @code{%s} with a file name. Note that Emacs requires double quote characters inside strings to be quoted with a backslash. @end defvar @defvar sml-cd-command Default: @code{"OS.FileSys.chDir \"%s\""} Compiler command to change the working directory. Not all ML systems support this feature (well, Edinburgh (core) ML didn't), but they should. @end defvar @defvar sml-prompt-regexp Default: @code{"^[-=>#] *"} Matches the ML compiler's prompt: @file{comint} uses this for various purposes. @end defvar To customise error reportage for different ML compilers you need to set two further variables before @code{next-error} can be useful: @defvar sml-error-regexp-alist Alist that specifies how to match errors in compiler output. Each elt has the form (REGEXP FILE-IDX LINE-IDX [COLUMN-IDX FILE-FORMAT...]) If REGEXP matches, the FILE-IDX'th subexpression gives the file name, and the LINE-IDX'th subexpression gives the line number. If COLUMN-IDX is given, the COLUMN-IDX'th subexpression gives the column number on that line. If any FILE-FORMAT is given, each is a format string to produce a file name to try; %s in the string is replaced by the text matching the FILE-IDX'th subexpression. @end defvar @c A typical way of (re)setting these variables correctly is to put @c something in your @file{.emacs} file that resembles @c @example @c (setq sml-use-command "PolyML.use \"%s\"") @c (setq sml-prompt-regexp "^[>#] *") @c @end example @c ======================================================= CONFIGURATION @node Configuration, , Interaction Mode, Top @chapter Configuration Summary @c @footnote{@url{http://www.ahl.co.uk/}} @c @footnote{@url{http://www.dina.kvl.dk/~sestoft/mosml.html}} @noindent This (sort of pedagogic) section gives more information on how to configure SML mode: menus, key bindings, hooks and highlighting are discussed, along with a few other random topics. @menu * Hooks:: Creating them * Key Bindings:: Binding commands to keys * Highlighting:: Syntax colouring * Advanced Topics:: You may need to speak Emacs Lisp @end menu @c =============================================================== HOOKS @node Hooks, Key Bindings, Configuration, Configuration @section Hooks @c == Hooks, Key Bindings, Configuration, Configuration ================ @noindent One way to set SML mode variables (@pxref{SML Mode Defaults,,Indentation Defaults}), and other defaults, is through the @code{sml-mode-hook} in your @file{.emacs}. A simple example: @lisp (defun my-sml-mode-hook () "Local defaults for SML mode" (setq sml-indent-level 2) ; conserve on horizontal space (setq words-include-escape t) ; \ loses word break status (setq indent-tabs-mode nil)) ; never ever indent with tabs (add-hook 'sml-mode-hook 'my-sml-mode-hook) @end lisp @noindent The body of @code{my-sml-mode-hook} is a sequence of assignments. In this case it is not really necessary to set @code{sml-indent-level} in a hook because this variable is global (most SML mode variables are). With similar effect: @lisp (setq sml-indent-level 2) @end lisp @noindent anywhere in your @file{.emacs} file. The variable @code{indent-tabs-mode} is automatically made local to the current buffer whenever it is set explicitly, so it @emph{must} be set in a hook if you always want SML mode to behave like this. Another hook is @code{inferior-sml-mode-hook}. This can be used to control the behaviour of the interaction buffer through various variables meaningful to @file{comint}-based packages: @lisp (defun my-inf-sml-mode-hook () "Local defaults for inferior SML mode" (add-hook 'comint-output-filter-functions 'comint-truncate-buffer) (setq comint-scroll-show-maximum-output t) (setq comint-input-autoexpand nil)) (add-hook 'inferior-sml-mode-hook 'my-inf-sml-mode-hook) @end lisp @noindent Again, the body is a sequence of assignments. Unless you run several ML compilers simultaneously under one Emacs, this hook will normally only get run once. You might want to look up the documentation (@kbd{C-h v} and @kbd{C-h f}) for these buffer-local @code{comint} things. @c ======================================================== Key Bindings @node Key Bindings, Highlighting, Hooks, Configuration @section Key bindings @noindent Customisation (in Emacs) usually entails putting favourite commands on easily remembered keys. Two `keymaps' are defined in SML mode: one is effective in program text buffers (@code{sml-mode-map}) and the other is effective in interaction buffers (@code{inferior-sml-mode-map}). The initial design ensures that (many of) the default key bindings from the former keymap will also be available in the latter (e.g., @kbd{C-c`}). Type @kbd{C-h m} in an SML mode buffer to find the default key bindings (and similarly in an ML interaction buffer), and use the hooks provided to install your preferred key bindings. Given that the keymaps are global (variables): @lisp (defun my-sml-mode-hook () "Global defaults for SML mode" (define-key sml-mode-map "\C-cd" 'sml-cd)) (add-hook 'sml-mode-hook 'my-sml-mode-hook) @end lisp @noindent This has the effect of binding @code{sml-cd} to the key @kbd{C-c d}. If you want the same behaviour from @kbd{C-c d} in the ML buffer: @lisp (defun my-inf-sml-mode-hook () "Global defaults for inferior SML mode" (define-key inferior-sml-mode-map "\C-cd" 'sml-cd) ;; NB. for SML/NJ '96 (setq sml-cd-command "OS.FileSys.chDir \"%s\"")) (add-hook 'inferior-sml-mode-hook 'my-inf-sml-mode-hook) @end lisp There is nothing to stop you rebuilding the entire keymap for SML mode and the ML interaction buffer in your @file{.emacs} of course: SML mode won't define @code{sml-mode-map} or @code{inferior-sml-mode-map} if you have already done so. @c ======================================================== Highlighting @node Highlighting, Advanced Topics, Key Bindings, Configuration @section Syntax colouring @noindent Highlighting is very handy for picking out keywords in the program text, spotting misspelled kewyords, and, if you have Emacs' @file{ps-print} package installed (you usually do these days), obtaining pretty, even colourful code listings---quite properly for your colourful ML programs. The indentation scheme (strangely enough) also relies on the highlighting code to properly handle nested comments, which is yet another reason to turn on highlighting. To turn on highlighting, use either of: @lisp M-x font-lock-mode (add-hook 'sml-mode-hook 'turn-on-font-lock) (global-font-lock-mode 1) @end lisp The first will turn it on in the current buffer. The second will turn it on in all sml-mode buffers. The last will turn it on everywhere. This is valid for Emacs but maybe not for XEmacs. Check font-lock documentation if you encounter problems. @c ===================================================== ADVANCED TOPICS @node Advanced Topics, , Highlighting, Configuration @section Advanced Topics @flushright @emph{These forms are bloody useless; can't we have better ones?} @end flushright @sp 1 @noindent You can indeed. @code{sml-insert-form} is extensible so all you need to do is create the macros yourself. Define a @emph{keybord macro} (@kbd{C-x (} @kbd{C-x )}) and give it a suitable name: @code{sml-addto-forms-alist} prompts for a name, say @code{NAME}, and binds the macro @code{sml-form-NAME}. Thereafter @kbd{C-c @key{RET} NAME} will insert the macro at point, and @kbd{C-u C-c @key{RET} NAME} will insert the macro after a @code{newline-and-indent}. If you want to keep your macros from one editing session to the next, go to your @file{.emacs} file and call @code{insert-kbd-macro}; you'll need to add @code{NAME} to @code{sml-forms-alist} permanently yourself: @lisp (defun my-sml-mode-hook () "Global defaults for SML mode" ;; whatever else you do (add-to-list 'sml-forms-alist '("NAME" . FUNCTION))) @end lisp If you want to create templates like `case' that prompt for parameters you'll have to do some Lisp programming. The @code{skeleton} package is a good stating point. Better yet, you can reuse the wrappers used by sml-mode itself in your sml-mode-hook: @lisp (add-hook 'sml-mode-hook (lambda () (sml-def-skeleton "case" "Case expr: " str " of" \n _ " => "))) @end lisp This will redefine `case' in order to leave the `of' on the first line. See the documentation of @code{skeleton-insert} to get a better understanding of how this works. @sp 1 @flushright @emph{I hate that indentation algorithm; can't I tweak it?} @end flushright @sp 1 @noindent Ah, yes, of course, but this manual will not tell you how. @sp 1 @flushright @emph{Can SML mode handle more than one compiler running at once?} @end flushright Sure, just rename the @samp{*sml*} buffer and then use @code{run-sml} as usual. @sp 1 @flushright @emph{What needs to be done to support other ML compilers?} @end flushright @sp 1 @noindent Not much really. Just add the right regular expressions to @code{sml-error-regexp-alist} and that should be all. @c ======================================================= COMMAND INDEX @headings singleafter @node Command Index, Variable Index, , Top @unnumbered Command Index @printindex fn @c ====================================================== VARIABLE INDEX @c node Variable Index, , Command Index, Top @node Variable Index, Key Index, Command Index, Top @unnumbered Variable Index @c == Variable Index, Key Index, Command Index, Top ==================== @printindex vr @c =========================================================== KEY INDEX @node Key Index, , Variable Index, Top @unnumbered Key Index @c == Key Index, , Variable Index, Top ================================= @printindex ky @contents @bye sml-mode-5.0/README0000644005056000007300000000124211741417724014665 0ustar monnierparallelismeSML-MODE is a major Emacs mode for editing Standard ML. It provides syntax highlighting and automatic indentation and comes with sml-proc which allows interaction with an inferior SML interactive loop. This release should work on any recent version of Emacs or XEmacs. If it doesn't: complain. Some more or less out of date documentation can be found in TeXinfo format. Check the INSTALL file for installation instructions. Check the NEWS file for a list of changes in this version. Check the BUGS and TODO file before sending me bug reports and requests for enhancements. Send any complaint/question/praise/ice-cream to me, Stefan Monnier sml-mode-5.0/BUGS0000644005056000007300000000051711741417724014474 0ustar monnierparallelisme-*- text -*- Here are the current known bugs. If you find any other, send it to . * M-x next-error and other compile.el support doesn't work on XEmacs. * indentation of a declaration after a long `datatype' is slow. * buggy indentation samples Try `make test' to see the known problems in testcases.sml sml-mode-5.0/Makefile0000644005056000007300000001235511741417724015454 0ustar monnierparallelisme# Makefile for emacs-lisp package # Copyright (C) 1998, 1999, 2004, 2007, 2010 Stefan Monnier # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 3, or (at your option) any # later version. # This file is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # You should have received a copy of the GNU General Public License # along with GNU Emacs; see the file COPYING. If not, write to # the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. # load the package-specific settings include makefile.pkg # set up the usual installation paths prefix = /usr/local datadir = $(prefix)/share # the directory where you install third-party emacs packges lispdir = $(datadir)/emacs/site-lisp # the directory where the .elc files will be installed elcdir = $(lispdir)/$(PACKAGE) # the directory where the .el files will be installed eldir = $(elcdir) # the file where the initialization goes. #startupfile = $(HOME/.emacs startupfile = $(lispdir)/site-start.el # the directory where you installed the elib .elc files. # This is only needed if your site-start.el (or default.el) does not # set up elib correctly. elibdir = $(lispdir)/elib # the directory where you install the info doc infodir = $(prefix)/info docdir = $(prefix)/doc EMACS = emacs MAKEINFO= makeinfo TEXI2DVI= texi2dvi SHELL = /bin/sh DVIPS = dvips CP = cp RM = rm -f MKDIR = mkdir -p ETAGS = etags ###################################################################### ### No changes below this line should be necessary ### ###################################################################### ELFLAGS = --eval '(setq load-path (append (list "." "$(elibdir)" "$(lispdir)") load-path))' ELC = $(EMACS) -batch $(ELFLAGS) -f batch-byte-compile ELCFILES = $(ELFILES:.el=.elc) TEXEXTS = *.cps *.fns *.kys *.vr *.tp *.pg *.log *.aux *.toc *.cp *.ky *.fn .SUFFIXES: .elc .el .info .ps .dvi .texi .PHONY: elcfiles info clean distclean default .PHONY: install_startup install_elc install install_el install_info .PHONY: dvi postscript .el.elc: $(ELC) $< .texi.info: $(MAKEINFO) $< .texi.dvi: $(TEXI2DVI) $< .dvi.ps: $(DVIPS) -f $< >$@ ###################################################################### default: elcfiles elcfiles: $(ELCFILES) info: $(PACKAGE).info install_elc: $(ELCFILES) $(PACKAGE)-startup.el $(MKDIR) $(elcdir) for f in $(ELCFILES) $(PACKAGE)-startup.el; do \ $(CP) $$f $(elcdir)/$$f ;\ done install_el: $(MKDIR) $(eldir) for f in $(ELFILES); do \ $(CP) $$f $(eldir)/$$f ;\ done install_info: $(PACKAGE).info $(MKDIR) $(infodir) $(CP) *.info* $(infodir)/ -[ ! -w $(infodir)/dir ] \ || install-info --info-dir=$(infodir)/dir $(PACKAGE).info install_startup: $(MKDIR) $(lispdir) @if grep $(PACKAGE) $(lispdir)/site-start.el >/dev/null 2>&1 || \ grep $(PACKAGE) $(startupfile) >/dev/null 2>&1 || \ grep $(PACKAGE) $(lispdir)/default.el >/dev/null 2>&1; \ then \ echo "**********************************************************" ;\ echo "*** It seems you already have some setup code" ;\ echo "*** for $(PACKAGE) in your startup files." ;\ echo "*** Check that it properly loads \"$(PACKAGE)-startup\"" ;\ echo "**********************************************************" ;\ else \ echo 'echo ";; load $(PACKAGE) setup code" >>$(startupfile)' ;\ echo ";; load $(PACKAGE) setup code" >>$(startupfile) ;\ echo 'echo "(load \"$(elcdir)/$(PACKAGE)-startup\")" >>$(startupfile)' ;\ echo "(load \"$(elcdir)/$(PACKAGE)-startup\")" >>$(startupfile) ;\ fi postscript: $(PACKAGE).ps dvi: $(PACKAGE).dvi install_dvi: dvi $(MKDIR) $(docdir) $(CP) `find . -type f -name '*.dvi' -print` $(docdir)/ install: install_elc install_info install_startup install_el clean: $(RM) *~ core .\#* $(TEXEXTS) TAGS tags: $(ETAGS) $(ELFILES) distclean: clean $(RM) *.elc *.dvi *.info* *.ps ###################################################################### ### don't look below ### ###################################################################### $(PACKAGE)-startup.el: $(ELFILES) echo "\ ;;; $@ --- automatically extracted autoloads\n\ ;;; Code:\n\ (add-to-list 'load-path\n\ (or (file-name-directory load-file-name) (car load-path)))\n\ " >$@ $(EMACS) --batch --eval '(setq generated-autoload-file "'`pwd`'/$@")' -f batch-update-autoloads "." ## #TAG = $(shell echo v$(VERSION) | tr '.' '_') URL=$(shell sed -n -e '5p' .svn/entries) TAG=$(shell dirname "$(URL)")/releases/$(PACKAGE)-$(VERSION) ftpdir=/u/monnier/html/elisp/ cvsmodule=$(shell cat CVS/Repository) cvsroot=$(shell cat CVS/Root) dist: svn cp . "$(TAG)" &&\ svn export "$(TAG)" "$(TMP)/$(PACKAGE)-$(VERSION)" &&\ cd "$(TMP)/$(PACKAGE)-$(VERSION)" &&\ $(MAKE) info $(PACKAGE)-startup.el &&\ cd .. &&\ ztar $(PACKAGE)-$(VERSION) &&\ rm -rf $(PACKAGE)-$(VERSION) mv $(TMP)/$(PACKAGE)-$(VERSION).tar.gz $(ftpdir)/ ln -sf $(PACKAGE)-$(VERSION).tar.gz $(ftpdir)/$(PACKAGE).tar.gz sml-mode-5.0/sml-mode.spec0000644005056000007300000000321011741417724016373 0ustar monnierparallelisme%define lispdir %{_datadir}/emacs/site-lisp %define startupfile %{lispdir}/site-start.el Summary: Emacs mode for editing Standard ML source code Name: sml-mode Version: $Name$ Release: 0.1 Group: Applications/Editors Copyright: GPL Packager: Stefan Monnier Source: http://iro.umontreal.ca/~monnier/elisp/%{name}.tar.gz Buildroot: %{_tmppath}/%{name}-buildroot BuildPreReq: emacs >= 20 xemacs >= 21 BuildArch: noarch %description SML-MODE is a major Emacs mode for editing Standard ML. It provides syntax highlighting and automatic indentation and comes with sml-proc which allows interaction with an inferior SML interactive loop. %prep %setup -q -n %{name} %install make install \ prefix=%{buildroot}%{_prefix} \ infodir=%{buildroot}%{_infodir} \ lispdir=%{buildroot}%{lispdir} gzip -9f %{buildroot}%{lispdir}/sml-mode/*.el texi2pdf sml-mode.texi %post cat >> %{lispdir}/site-start.el < (aka ) ;; Matthias Blume (aka ) ;; (Stefan Monnier) ;; Maintainer: (Stefan Monnier) ;; Keywords: SML ;; This file is not part of GNU Emacs, but it is distributed under the ;; same conditions. ;; This program is free software; you can redistribute it and/or ;; modify it under the terms of the GNU General Public License as ;; published by the Free Software Foundation; either version 3, or (at ;; your option) any later version. ;; This program is distributed in the hope that it will be useful, but ;; WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;; General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to the ;; Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ;;; Commentary: ;;; HISTORY ;; Still under construction: History obscure, needs a biographer as ;; well as a M-x doctor. Change Log on request. ;; Hacked by Olin Shivers for comint from Lars Bo Nielsen's sml.el. ;; Hacked by Matthew Morley to incorporate Fritz Knabe's hilite and ;; font-lock patterns, some of Steven Gilmore's (reduced) easy-menus, ;; and numerous bugs and bug-fixes. ;;; DESCRIPTION ;; See accompanying info file: sml-mode.info ;;; FOR YOUR .EMACS FILE ;; If sml-mode.el lives in some non-standard directory, you must tell ;; emacs where to get it. This may or may not be necessary: ;; (add-to-list 'load-path "~jones/lib/emacs/") ;; Then to access the commands autoload sml-mode with that command: ;; (load "sml-mode-startup") ;; sml-mode-hook is run whenever a new sml-mode buffer is created. ;; Finally, there are inferior-sml-{mode,load}-hooks -- see comments ;; in sml-proc.el. For much more information consult the mode's *info* ;; tree. ;;; Code: (eval-when-compile (require 'cl)) (require 'smie nil 'noerror) (condition-case nil (require 'skeleton) (error nil)) (defgroup sml () "Editing SML code." :group 'languages) ;;; VARIABLES CONTROLLING INDENTATION (defcustom sml-indent-level 4 "Indentation of blocks in ML (see also `sml-indent-rule')." :type '(integer)) (defcustom sml-indent-args sml-indent-level "Indentation of args placed on a separate line." :type '(integer)) ;; (defvar sml-indent-align-args t ;; "*Whether the arguments should be aligned.") ;; (defvar sml-case-indent nil ;; "*How to indent case-of expressions. ;; If t: case expr If nil: case expr of ;; of exp1 => ... exp1 => ... ;; | exp2 => ... | exp2 => ... ;; The first seems to be the standard in SML/NJ, but the second ;; seems nicer...") (defcustom sml-electric-semi-mode nil "If non-nil, `\;' will self insert, reindent the line, and do a newline. If nil, just insert a `\;'. (To insert while t, do: \\[quoted-insert] \;)." :type 'boolean) (when (fboundp 'electric-layout-mode) (make-obsolete-variable 'sml-electric-semi-mode 'electric-layout-mode "Emacs-24")) (defcustom sml-rightalign-and t "If non-nil, right-align `and' with its leader. If nil: If t: datatype a = A datatype a = A and b = B and b = B" :type 'boolean) ;;; OTHER GENERIC MODE VARIABLES (defvar sml-mode-info "sml-mode" "*Where to find Info file for `sml-mode'. The default assumes the info file \"sml-mode.info\" is on Emacs' info directory path. If it is not, either put the file on the standard path or set the variable `sml-mode-info' to the exact location of this file (setq sml-mode-info \"/usr/me/lib/info/sml-mode\") in your .emacs file. You can always set it interactively with the set-variable command.") (defvar sml-mode-hook nil "*Run upon entering `sml-mode'. This is a good place to put your preferred key bindings.") ;;; CODE FOR SML-MODE (defun sml-mode-info () "Command to access the TeXinfo documentation for `sml-mode'. See doc for the variable `sml-mode-info'." (interactive) (require 'info) (condition-case nil (info sml-mode-info) (error (progn (describe-variable 'sml-mode-info) (message "Can't find it... set this variable first!"))))) ;;; Autoload functions -- no-doc is another idea cribbed from AucTeX! (let ((sml-no-doc "This function is part of sml-proc, and has not yet been loaded. Full documentation will be available after autoloading the function.")) (autoload 'sml-compile "sml-proc" sml-no-doc t) (autoload 'sml-load-file "sml-proc" sml-no-doc t) (autoload 'switch-to-sml "sml-proc" sml-no-doc t) (autoload 'sml-send-region "sml-proc" sml-no-doc t) (autoload 'sml-send-buffer "sml-proc" sml-no-doc t)) ;; font-lock setup (defvar sml-outline-regexp ;; `st' and `si' are to match structure and signature. " \\|s[ti]\\|[ \t]*\\(let[ \t]+\\)?\\(fun\\|and\\)\\>" "Regexp matching a major heading. This actually can't work without extending `outline-minor-mode' with the notion of \"the end of an outline\".") ;; ;; Internal defines ;; (defvar sml-mode-map (let ((map (make-sparse-keymap))) ;; Smarter cursor movement. ;; (define-key map [remap forward-sexp] 'sml-user-forward-sexp) ;; (define-key map [remap backward-sexp] 'sml-user-backward-sexp) ;; Text-formatting commands: (define-key map "\C-c\C-m" 'sml-insert-form) (define-key map "\C-c\C-i" 'sml-mode-info) (define-key map "\M-|" 'sml-electric-pipe) (define-key map "\M-\ " 'sml-electric-space) (define-key map "\;" 'sml-electric-semi) (define-key map [backtab] 'sml-back-to-outer-indent) ;; Process commands added to sml-mode-map -- these should autoload. (define-key map "\C-c\C-l" 'sml-load-file) (define-key map "\C-c\C-c" 'sml-compile) (define-key map "\C-c\C-s" 'switch-to-sml) (define-key map "\C-c\C-r" 'sml-send-region) (define-key map "\C-c\C-b" 'sml-send-buffer) map) "The keymap used in `sml-mode'.") (defconst sml-builtin-nested-comments-flag (ignore-errors (not (equal (let ((st (make-syntax-table))) (modify-syntax-entry ?\* ". 23n" st) st) (let ((st (make-syntax-table))) (modify-syntax-entry ?\* ". 23" st) st)))) "Non-nil means this Emacs understands the `n' in syntax entries.") (defvar sml-mode-syntax-table (let ((st (make-syntax-table))) (modify-syntax-entry ?\* (if sml-builtin-nested-comments-flag ". 23n" ". 23") st) (modify-syntax-entry ?\( "()1" st) (modify-syntax-entry ?\) ")(4" st) (mapc (lambda (c) (modify-syntax-entry c "_" st)) "._'") (mapc (lambda (c) (modify-syntax-entry c "." st)) ",;") ;; `!' is not really a prefix-char, oh well! (mapc (lambda (c) (modify-syntax-entry c "'" st)) "~#!") (mapc (lambda (c) (modify-syntax-entry c "." st)) "%&$+-/:<=>?@`^|") st) "The syntax table used in `sml-mode'.") (easy-menu-define sml-mode-menu sml-mode-map "Menu used in `sml-mode'." '("SML" ("Process" ["Start default ML compiler" run-sml t] ["-" nil nil] ["run CM.make" sml-compile t] ["load ML source file" sml-load-file t] ["switch to ML buffer" switch-to-sml t] ["--" nil nil] ["send buffer contents" sml-send-buffer t] ["send region" sml-send-region t] ["send paragraph" sml-send-function t] ["goto next error" next-error (featurep 'sml-proc)] ["---" nil nil] ;; ["Standard ML of New Jersey" sml-smlnj (fboundp 'sml-smlnj)] ;; ["Poly/ML" sml-poly-ml (fboundp 'sml-poly-ml)] ;; ["Moscow ML" sml-mosml (fboundp 'sml-mosml)] ["Help for Inferior ML" (describe-function 'inferior-sml-mode) :active (featurep 'sml-proc)]) ["electric pipe" sml-electric-pipe t] ["insert SML form" sml-insert-form t] ("Forms" :filter sml-forms-menu) ("Format/Mode Variables" ["indent region" indent-region t] ["outdent" sml-back-to-outer-indent t] ;; ["-" nil nil] ;; ["set indent-level" sml-indent-level t] ;; ["set pipe-indent" sml-pipe-indent t] ;; ["--" nil nil] ;; ["toggle type-of-indent" sml-type-of-indent t] ;; ["toggle nested-if-indent" sml-nested-if-indent t] ;; ["toggle electric-semi-mode" sml-electric-semi-mode t] ) ["-----" nil nil] ["SML mode help (brief)" describe-mode t] ["SML mode *info*" sml-mode-info t] ["Remove overlay" (sml-error-overlay 'undo) :visible (or (and (boundp 'sml-error-overlay) sml-error-overlay) (not (fboundp 'compilation-fake-loc))) :active (and (boundp 'sml-error-overlay) (overlayp sml-error-overlay) (overlay-start sml-error-overlay)) ])) ;; Make's sure they appear in the menu bar when sml-mode-map is active. ;; On the hook for XEmacs only -- see easy-menu-add in auc-menu.el. ;; (defun sml-mode-menu-bar () ;; "Make sure menus appear in the menu bar as well as under mouse 3." ;; (and (eq major-mode 'sml-mode) ;; (easy-menu-add sml-mode-menu sml-mode-map))) ;; (add-hook 'sml-mode-hook 'sml-mode-menu-bar) ;; ;; regexps ;; (defun sml-syms-re (syms) (concat "\\<" (regexp-opt syms t) "\\>")) ;; (defconst sml-module-head-syms '("signature" "structure" "functor" "abstraction")) (defconst sml-=-starter-syms (list* "|" "val" "fun" "and" "datatype" "type" "abstype" "eqtype" sml-module-head-syms) "Symbols that can be followed by a `='.") (defconst sml-=-starter-re (concat "\\S.|\\S.\\|" (sml-syms-re (cdr sml-=-starter-syms))) "Symbols that can be followed by a `='.") (defconst sml-non-nested-of-starter-re (sml-syms-re '("datatype" "abstype" "exception")) "Symbols that can introduce an `of' that shouldn't behave like a paren.") (defconst sml-starters-syms (append sml-module-head-syms '("abstype" "datatype" "exception" "fun" "local" "infix" "infixr" "sharing" "nonfix" "open" "type" "val" "and" "withtype" "with")) "The starters of new expressions.") (defconst sml-pipeheads '("|" "of" "fun" "fn" "and" "handle" "datatype" "abstype") "A `|' corresponds to one of these.") (defconst sml-keywords-regexp (sml-syms-re '("abstraction" "abstype" "and" "andalso" "as" "before" "case" "datatype" "else" "end" "eqtype" "exception" "do" "fn" "fun" "functor" "handle" "if" "in" "include" "infix" "infixr" "let" "local" "nonfix" "of" "op" "open" "orelse" "overload" "raise" "rec" "sharing" "sig" "signature" "struct" "structure" "then" "type" "val" "where" "while" "with" "withtype" "o")) "A regexp that matches any and all keywords of SML.") (defconst sml-tyvarseq-re "\\(\\('+\\(\\sw\\|\\s_\\)+\\|(\\([,']\\|\\sw\\|\\s_\\|\\s-\\)+)\\)\\s-+\\)?") ;;; Font-lock settings ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defcustom sml-font-lock-symbols nil "Display \\ and -> and such using symbols in fonts. This may sound like a neat trick, but be extra careful: it changes the alignment and can thus lead to nasty surprises w.r.t layout. If t, try to use whichever font is available. Otherwise you can set it to a particular font of your preference among `japanese-jisx0208' and `unicode'." :type '(choice (const nil) (const t) (const unicode) (const japanese-jisx0208))) (defconst sml-font-lock-symbols-alist (append ;; The symbols can come from a JIS0208 font. (and (fboundp 'make-char) (charsetp 'japanese-jisx0208) (memq sml-font-lock-symbols '(t japanese-jisx0208)) (list (cons "fn" (make-char 'japanese-jisx0208 38 75)) (cons "andalso" (make-char 'japanese-jisx0208 34 74)) (cons "orelse" (make-char 'japanese-jisx0208 34 75)) ;; (cons "as" (make-char 'japanese-jisx0208 34 97)) (cons "not" (make-char 'japanese-jisx0208 34 76)) (cons "div" (make-char 'japanese-jisx0208 33 96)) ;; (cons "*" (make-char 'japanese-jisx0208 33 95)) (cons "->" (make-char 'japanese-jisx0208 34 42)) (cons "=>" (make-char 'japanese-jisx0208 34 77)) (cons "<-" (make-char 'japanese-jisx0208 34 43)) (cons "<>" (make-char 'japanese-jisx0208 33 98)) (cons ">=" (make-char 'japanese-jisx0208 33 102)) (cons "<=" (make-char 'japanese-jisx0208 33 101)) (cons "..." (make-char 'japanese-jisx0208 33 68)) ;; Some greek letters for type parameters. (cons "'a" (make-char 'japanese-jisx0208 38 65)) (cons "'b" (make-char 'japanese-jisx0208 38 66)) (cons "'c" (make-char 'japanese-jisx0208 38 67)) (cons "'d" (make-char 'japanese-jisx0208 38 68)) )) ;; Or a unicode font. (and (fboundp 'decode-char) (memq sml-font-lock-symbols '(t unicode)) (list (cons "fn" (decode-char 'ucs 955)) (cons "andalso" (decode-char 'ucs 8896)) (cons "orelse" (decode-char 'ucs 8897)) ;; (cons "as" (decode-char 'ucs 8801)) (cons "not" (decode-char 'ucs 172)) (cons "div" (decode-char 'ucs 247)) (cons "*" (decode-char 'ucs 215)) (cons "o" (decode-char 'ucs 9675)) (cons "->" (decode-char 'ucs 8594)) (cons "=>" (decode-char 'ucs 8658)) (cons "<-" (decode-char 'ucs 8592)) (cons "<>" (decode-char 'ucs 8800)) (cons ">=" (decode-char 'ucs 8805)) (cons "<=" (decode-char 'ucs 8804)) (cons "..." (decode-char 'ucs 8943)) ;; (cons "::" (decode-char 'ucs 8759)) ;; Some greek letters for type parameters. (cons "'a" (decode-char 'ucs 945)) (cons "'b" (decode-char 'ucs 946)) (cons "'c" (decode-char 'ucs 947)) (cons "'d" (decode-char 'ucs 948)) )))) (defun sml-font-lock-compose-symbol (alist) "Compose a sequence of ascii chars into a symbol. Regexp match data 0 points to the chars." ;; Check that the chars should really be composed into a symbol. (let* ((start (match-beginning 0)) (end (match-end 0)) (syntaxes (if (eq (char-syntax (char-after start)) ?w) '(?w) '(?. ?\\)))) (if (or (memq (char-syntax (or (char-before start) ?\ )) syntaxes) (memq (char-syntax (or (char-after end) ?\ )) syntaxes) (memq (get-text-property start 'face) '(font-lock-doc-face font-lock-string-face font-lock-comment-face))) ;; No composition for you. Let's actually remove any composition ;; we may have added earlier and which is now incorrect. (remove-text-properties start end '(composition)) ;; That's a symbol alright, so add the composition. (compose-region start end (cdr (assoc (match-string 0) alist))))) ;; Return nil because we're not adding any face property. nil) (defun sml-font-lock-symbols-keywords () (when (fboundp 'compose-region) (let ((alist nil)) (dolist (x sml-font-lock-symbols-alist) (when (and (if (fboundp 'char-displayable-p) (char-displayable-p (cdr x)) t) (not (assoc (car x) alist))) ;Not yet in alist. (push x alist))) (when alist `((,(regexp-opt (mapcar 'car alist) t) (0 (sml-font-lock-compose-symbol ',alist)))))))) ;; The font lock regular expressions. (defconst sml-font-lock-keywords `(;;(sml-font-comments-and-strings) (,(concat "\\<\\(fun\\|and\\)\\s-+" sml-tyvarseq-re "\\(\\sw+\\)\\s-+[^ \t\n=]") (1 font-lock-keyword-face) (6 font-lock-function-name-face)) (,(concat "\\<\\(\\(data\\|abs\\|with\\|eq\\)?type\\)\\s-+" sml-tyvarseq-re "\\(\\sw+\\)") (1 font-lock-keyword-face) (7 font-lock-type-def-face)) ("\\<\\(val\\)\\s-+\\(\\sw+\\>\\s-*\\)?\\(\\sw+\\)\\s-*[=:]" (1 font-lock-keyword-face) ;;(6 font-lock-variable-def-face nil t) (3 font-lock-variable-name-face)) ("\\<\\(structure\\|functor\\|abstraction\\)\\s-+\\(\\sw+\\)" (1 font-lock-keyword-face) (2 font-lock-module-def-face)) ("\\<\\(signature\\)\\s-+\\(\\sw+\\)" (1 font-lock-keyword-face) (2 font-lock-interface-def-face)) (,sml-keywords-regexp . font-lock-keyword-face) ,@(sml-font-lock-symbols-keywords)) "Regexps matching standard SML keywords.") (defface font-lock-type-def-face '((t (:bold t))) "Font Lock mode face used to highlight type definitions." :group 'font-lock-highlighting-faces) (defvar font-lock-type-def-face 'font-lock-type-def-face "Face name to use for type definitions.") (defface font-lock-module-def-face '((t (:bold t))) "Font Lock mode face used to highlight module definitions." :group 'font-lock-highlighting-faces) (defvar font-lock-module-def-face 'font-lock-module-def-face "Face name to use for module definitions.") (defface font-lock-interface-def-face '((t (:bold t))) "Font Lock mode face used to highlight interface definitions." :group 'font-lock-highlighting-faces) (defvar font-lock-interface-def-face 'font-lock-interface-def-face "Face name to use for interface definitions.") ;; ;; Code to handle nested comments and unusual string escape sequences ;; (defvar sml-syntax-prop-table (let ((st (make-syntax-table))) (modify-syntax-entry ?\\ "." st) (modify-syntax-entry ?* "." st) st) "Syntax table for text-properties") ;; For Emacsen that have no built-in support for nested comments (defun sml-get-depth-st () (save-excursion (let* ((disp (if (eq (char-before) ?\)) (progn (backward-char) -1) nil)) (_ (backward-char)) (disp (if (eq (char-before) ?\() (progn (backward-char) 0) disp)) (pt (point))) (when disp (let* ((depth (save-match-data (if (re-search-backward "\\*)\\|(\\*" nil t) (+ (or (get-char-property (point) 'comment-depth) 0) (case (char-after) (?\( 1) (?* 0)) disp) 0))) (depth (if (> depth 0) depth))) (put-text-property pt (1+ pt) 'comment-depth depth) (when depth sml-syntax-prop-table)))))) (defconst sml-font-lock-syntactic-keywords `(("^\\s-*\\(\\\\\\)" (1 ',sml-syntax-prop-table)) ,@(unless sml-builtin-nested-comments-flag '(("(?\\(\\*\\))?" (1 (sml-get-depth-st))))))) (defconst sml-font-lock-defaults '(sml-font-lock-keywords nil nil ((?_ . "w") (?' . "w")) nil (font-lock-syntactic-keywords . sml-font-lock-syntactic-keywords))) ;;; Indentation with SMIE (defvar sml-use-smie t) (defconst sml-smie-grammar (when (fboundp 'smie-prec2->grammar) ;; We have several problem areas where SML's syntax can't be handled by an ;; operator precedence grammar: ;; ;; "= A before B" is "= A) before B" if this is the ;; `boolean-=' but it is "= (A before B)" if it's the `definitional-='. ;; We can work around the problem by tweaking the lexer to return two ;; different tokens for the two different kinds of `='. ;; "of A | B" in a "case" we want "of (A | B, but in a `datatype' ;; we want "of A) | B". ;; "= A | B" can be "= A ) | B" if the = is from a "fun" definition, ;; but it is "= (A | B" if it is a `datatype' definition (of course, if ;; the previous token introducing the = is `and', deciding whether ;; it's a datatype or a function requires looking even further back). ;; "functor foo (...) where type a = b = ..." the first `=' looks very much ;; like a `definitional-=' even tho it's just an equality constraint. ;; Currently I don't even try to handle `where' at all. (smie-prec2->grammar (smie-merge-prec2s (smie-bnf->prec2 '((exp ("if" exp "then" exp "else" exp) ("case" exp "of" branches) ("let" decls "in" cmds "end") ("struct" decls "end") ("sig" decls "end") (sexp) (sexp "handle" branches) ("fn" sexp "=>" exp)) ;; "simple exp"s are the ones that can appear to the left of `handle'. (sexp (sexp ":" type) ("(" exps ")") (sexp "orelse" sexp) (marg ":>" type) (sexp "andalso" sexp)) (cmds (cmds ";" cmds) (exp)) (exps (exps "," exps) (exp)) ; (exps ";" exps) (branches (sexp "=>" exp) (branches "|" branches)) ;; Operator precedence grammars handle separators much better then ;; starters/terminators, so let's pretend that let/fun are separators. (decls (sexp "d=" exp) (sexp "d=" databranches) (funbranches "|" funbranches) (sexp "=of" type) ;After "exception". ;; FIXME: Just like PROCEDURE in Pascal and Modula-2, this ;; interacts poorly with the other constructs since I ;; can't make "local" a separator like fun/val/type/... ("local" decls "in" decls "end") ;; (decls "local" decls "in" decls "end") (decls "functor" decls) (decls "signature" decls) (decls "structure" decls) (decls "type" decls) (decls "open" decls) (decls "and" decls) (decls "infix" decls) (decls "infixr" decls) (decls "nonfix" decls) (decls "abstype" decls) (decls "datatype" decls) (decls "exception" decls) (decls "fun" decls) (decls "val" decls)) (type (type "->" type) (type "*" type)) (funbranches (sexp "d=" exp)) (databranches (sexp "=of" type) (databranches "d|" databranches)) ;; Module language. ;; (mexp ("functor" marg "d=" mexp) ;; ("structure" marg "d=" mexp) ;; ("signature" marg "d=" mexp)) (marg (marg ":" type) (marg ":>" type)) (toplevel (decls) (exp) (toplevel ";" toplevel))) ;; '(("local" . opener)) ;; '((nonassoc "else") (right "handle")) '((nonassoc "of") (assoc "|")) ; "case a of b => case c of d => e | f" '((nonassoc "handle") (assoc "|")) ; Idem for "handle". '((assoc "->") (assoc "*")) '((assoc "val" "fun" "type" "datatype" "abstype" "open" "infix" "infixr" "nonfix" "functor" "signature" "structure" "exception" ;; "local" ) (assoc "and")) '((assoc "orelse") (assoc "andalso") (nonassoc ":")) '((assoc ";")) '((assoc ",")) '((assoc "d|"))) (smie-precs->prec2 '((nonassoc "andalso") ;To anchor the prec-table. (assoc "before") ;0 (assoc ":=" "o") ;3 (nonassoc ">" ">=" "<>" "<" "<=" "=") ;4 (assoc "::" "@") ;5 (assoc "+" "-" "^") ;6 (assoc "/" "*" "quot" "rem" "div" "mod") ;7 (nonassoc " -dummy- "))) ;Bogus anchor at the end. )))) (defvar sml-indent-separator-outdent 2) (defun sml-smie-rules (kind token) ;; I much preferred the pcase version of the code, especially while ;; edebugging the code. But that will have to wait until we get rid of ;; support for Emacs-23. (case kind (:elem (case token (basic sml-indent-level) (args sml-indent-args))) (:list-intro (member token '("fn"))) (:after (cond ((equal token "struct") 0) ((equal token "=>") (if (smie-rule-hanging-p) 0 2)) ((equal token "in") (if (smie-rule-parent-p "local") 0)) ((equal token "of") 3) ((member token '("(" "{" "[")) (if (not (smie-rule-hanging-p)) 2)) ((equal token "else") (if (smie-rule-hanging-p) 0)) ;; (:next "if" 0) ((member token '("|" "d|" ";" ",")) (smie-rule-separator kind)) ((equal token "d=") (if (and (smie-rule-parent-p "val") (smie-rule-next-p "fn")) -3)))) (:before (cond ((equal token "=>") (if (smie-rule-parent-p "fn") 3)) ((equal token "of") 1) ;; In case the language is extended to allow a | directly after of. ((and (equal token "|") (smie-rule-prev-p "of")) 1) ((member token '("|" "d|" ";" ",")) (smie-rule-separator kind)) ;; Treat purely syntactic block-constructs as being part of their parent, ;; when the opening statement is hanging. ((member token '("let" "(" "[" "{")) (if (smie-rule-hanging-p) (smie-rule-parent))) ;; Treat if ... else if ... as a single long syntactic construct. ;; Similarly, treat fn a => fn b => ... as a single construct. ((member token '("if" "fn")) (and (not (smie-rule-bolp)) (smie-rule-prev-p (if (equal token "if") "else" "=>")) (smie-rule-parent))) ((equal token "and") ;; FIXME: maybe "and" (c|sh)ould be handled as an smie-separator. (cond ((smie-rule-parent-p "datatype") (if sml-rightalign-and 5 0)) ((smie-rule-parent-p "fun" "val") 0))) ((equal token "d=") (cond ((smie-rule-parent-p "datatype") (if (smie-rule-bolp) 2)) ((smie-rule-parent-p "structure" "signature") 0))) ;; Indent an expression starting with "local" as if it were starting ;; with "fun". ((equal token "local") (smie-indent-keyword "fun")) ;; FIXME: type/val/fun/... are separators but "local" is not, even though ;; it appears in the same list. Try to fix up the problem by hand. ;; ((or (equal token "local") ;; (equal (cdr (assoc token smie-grammar)) ;; (cdr (assoc "fun" smie-grammar)))) ;; (let ((parent (save-excursion (smie-backward-sexp)))) ;; (when (or (and (equal (nth 2 parent) "local") ;; (null (car parent))) ;; (progn ;; (setq parent (save-excursion (smie-backward-sexp "fun"))) ;; (eq (car parent) (nth 1 (assoc "fun" smie-grammar))))) ;; (goto-char (nth 1 parent)) ;; (cons 'column (smie-indent-virtual))))) )))) (defun sml-smie-definitional-equal-p () "Figure out which kind of \"=\" this is. Assumes point is right before the = sign." ;; The idea is to look backward for the first occurrence of a token that ;; requires a definitional "=" and then see if there's such a definitional ;; equal between that token and ourselves (in which case we're not ;; a definitional = ourselves). ;; The "search for =" is naive and will match "=>" and "<=", but it turns ;; out to be OK in practice because such tokens very rarely (if ever) appear ;; between the =-starter and the corresponding definitional equal. ;; One known problem case is code like: ;; "functor foo (structure s : S) where type t = s.t =" ;; where the "type t = s.t" is mistaken for a type definition. (let ((re (concat "\\(" sml-=-starter-re "\\)\\|="))) (save-excursion (and (re-search-backward re nil t) (or (match-beginning 1) ;; If we first hit a "=", then that = is probably definitional ;; and we're an equality, but not necessarily. One known ;; problem case is code like: ;; "functor foo (structure s : S) where type t = s.t =" ;; where the first = is more like an equality (tho it doesn't ;; matter much) and the second is definitional. ;; ;; FIXME: The test below could be used to recognize that the ;; second = is not a mere equality, but that's not enough to ;; parse the construct properly: we'd need something ;; like a third kind of = token for structure definitions, in ;; order for the parser to be able to skip the "type t = s.t" ;; as a sub-expression. ;; ;; (and (not (looking-at "=>")) ;; (not (eq ?< (char-before))) ;Not a <= ;; (re-search-backward re nil t) ;; (match-beginning 1) ;; (equal "type" (buffer-substring (- (match-end 1) 4) ;; (match-end 1)))) ))))) (defun sml-smie-non-nested-of-p () ;; FIXME: Maybe datatype-|-p makes this nested-of business unnecessary. "Figure out which kind of \"of\" this is. Assumes point is right before the \"of\" symbol." (save-excursion (and (re-search-backward (concat "\\(" sml-non-nested-of-starter-re "\\)\\|\\") nil t) (match-beginning 1)))) (defun sml-smie-datatype-|-p () "Figure out which kind of \"|\" this is. Assumes point is right before the | symbol." (save-excursion (forward-char 1) ;Skip the |. (let ((after-type-def '("|" "of" "in" "datatype" "and" "exception" "abstype" "infix" "infixr" "nonfix" "local" "val" "fun" "structure" "functor" "signature"))) (or (member (sml-smie-forward-token-1) after-type-def) ;Skip the tag. (member (sml-smie-forward-token-1) after-type-def))))) (defun sml-smie-forward-token-1 () (forward-comment (point-max)) (buffer-substring-no-properties (point) (progn (or (/= 0 (skip-syntax-forward "'w_")) (skip-syntax-forward ".'")) (point)))) (defun sml-smie-forward-token () (let ((sym (sml-smie-forward-token-1))) (cond ((equal "op" sym) (concat "op " (sml-smie-forward-token-1))) ((member sym '("|" "of" "=")) ;; The important lexer for indentation's performance is the backward ;; lexer, so for the forward lexer we delegate to the backward one. (save-excursion (sml-smie-backward-token))) (t sym)))) (defun sml-smie-backward-token-1 () (forward-comment (- (point))) (buffer-substring-no-properties (point) (progn (or (/= 0 (skip-syntax-backward ".'")) (skip-syntax-backward "'w_")) (point)))) (defun sml-smie-backward-token () (let ((sym (sml-smie-backward-token-1))) (unless (zerop (length sym)) ;; FIXME: what should we do if `sym' = "op" ? (let ((point (point))) (if (equal "op" (sml-smie-backward-token-1)) (concat "op " sym) (goto-char point) (cond ((string= sym "=") (if (sml-smie-definitional-equal-p) "d=" "=")) ((string= sym "of") (if (sml-smie-non-nested-of-p) "=of" "of")) ((string= sym "|") (if (sml-smie-datatype-|-p) "d|" "|")) (t sym))))))) ;;;; ;;;; Imenu support ;;;; (defvar sml-imenu-regexp (concat "^[ \t]*\\(let[ \t]+\\)?" (regexp-opt (append sml-module-head-syms '("and" "fun" "datatype" "abstype" "type")) t) "\\>")) (defun sml-imenu-create-index () (let (alist) (goto-char (point-max)) (while (re-search-backward sml-imenu-regexp nil t) (save-excursion (let ((kind (match-string 2)) (column (progn (goto-char (match-beginning 2)) (current-column))) (location (progn (goto-char (match-end 0)) (forward-comment (point-max)) (when (looking-at sml-tyvarseq-re) (goto-char (match-end 0))) (point))) (name (sml-smie-forward-token))) ;; Eliminate trivial renamings. (when (or (not (member kind '("structure" "signature"))) (progn (search-forward "=") (forward-comment (point-max)) (looking-at "sig\\|struct"))) (push (cons (concat (make-string (/ column 2) ?\ ) name) location) alist))))) alist)) ;;; MORE CODE FOR SML-MODE ;;;###autoload (add-to-list 'auto-mode-alist '("\\.s\\(ml\\|ig\\)\\'" . sml-mode)) (unless (fboundp 'prog-mode) (defalias 'prog-mode 'fundamental-mode)) (defvar comment-quote-nested) (defvar electric-indent-chars) (defvar electric-layout-rules) ;;;###autoload (define-derived-mode sml-mode prog-mode "SML" "\\Major mode for editing ML code. This mode runs `sml-mode-hook' just before exiting. \\{sml-mode-map}" (set (make-local-variable 'font-lock-defaults) sml-font-lock-defaults) (set (make-local-variable 'outline-regexp) sml-outline-regexp) (set (make-local-variable 'imenu-create-index-function) 'sml-imenu-create-index) (set (make-local-variable 'add-log-current-defun-function) 'sml-current-fun-name) ;; Treat paragraph-separators in comments as paragraph-separators. (set (make-local-variable 'paragraph-separate) (concat "\\([ \t]*\\*)?\\)?\\(" paragraph-separate "\\)")) (set (make-local-variable 'require-final-newline) t) (set (make-local-variable 'electric-indent-chars) (cons ?\; (if (boundp 'electric-indent-chars) electric-indent-chars '(?\n)))) (set (make-local-variable 'electric-layout-rules) `((?\; . ,(lambda () (save-excursion (skip-chars-backward " \t;") (unless (or (bolp) (progn (skip-chars-forward " \t;") (eolp))) 'after)))))) ;; For XEmacs (easy-menu-add sml-mode-menu) ;; Compatibility. FIXME: we should use `-' in Emacs-CVS. (unless (boundp 'skeleton-positions) (set (make-local-variable '@) nil)) (sml-mode-variables)) (defun sml-mode-variables () (set-syntax-table sml-mode-syntax-table) (setq local-abbrev-table sml-mode-abbrev-table) ;; Setup indentation and sexp-navigation. (when (fboundp 'smie-setup) (smie-setup sml-smie-grammar #'sml-smie-rules :backward-token #'sml-smie-backward-token :forward-token #'sml-smie-forward-token)) (unless (and sml-use-smie (fboundp 'smie-setup)) (set (make-local-variable 'forward-sexp-function) 'sml-user-forward-sexp) (set (make-local-variable 'indent-line-function) 'sml-indent-line)) (set (make-local-variable 'parse-sexp-ignore-comments) t) (set (make-local-variable 'comment-start) "(* ") (set (make-local-variable 'comment-end) " *)") (set (make-local-variable 'comment-start-skip) "(\\*+\\s-*") (set (make-local-variable 'comment-end-skip) "\\s-*\\*+)") ;; No need to quote nested comments markers. (set (make-local-variable 'comment-quote-nested) nil)) (defun sml-funname-of-and () "Name of the function this `and' defines, or nil if not a function. Point has to be right after the `and' symbol and is not preserved." (forward-comment (point-max)) (if (looking-at sml-tyvarseq-re) (goto-char (match-end 0))) (let ((sym (sml-smie-forward-token))) (forward-comment (point-max)) (unless (or (member sym '(nil "d=")) (member (sml-smie-forward-token) '("d="))) sym))) (defun sml-find-forward (re) (while (progn (forward-comment (point-max)) (not (looking-at re))) (or (ignore-errors (forward-sexp 1) t) (forward-char 1)))) (defun sml-electric-pipe () "Insert a \"|\". Depending on the context insert the name of function, a \"=>\" etc." ;; FIXME: Make it a skeleton. (interactive) (unless (save-excursion (skip-chars-backward "\t ") (bolp)) (insert "\n")) (insert "| ") (let ((text (save-excursion (backward-char 2) ;back over the just inserted "| " (let ((sym (sml-find-matching-starter sml-pipeheads ;; (sml-op-prec "|" 'back) ))) (sml-smie-forward-token) (forward-comment (point-max)) (cond ((string= sym "|") (let ((f (sml-smie-forward-token))) (sml-find-forward "\\(=>\\|=\\||\\)\\S.") (cond ((looking-at "|") "") ;probably a datatype ((looking-at "=>") " => ") ;`case', or `fn' or `handle' ((looking-at "=") (concat f " = "))))) ;a function ((string= sym "and") ;; could be a datatype or a function (setq sym (sml-funname-of-and)) (if sym (concat sym " = ") "")) ;; trivial cases ((string= sym "fun") (while (and (setq sym (sml-smie-forward-token)) (string-match "^'" sym)) (forward-comment (point-max))) (concat sym " = ")) ((member sym '("case" "handle" "fn" "of")) " => ") ;;((member sym '("abstype" "datatype")) "") (t "")))))) (insert text) (indent-according-to-mode) (beginning-of-line) (skip-chars-forward "\t |") (skip-syntax-forward "w") (skip-chars-forward "\t ") (when (eq ?= (char-after)) (backward-char)))) (defun sml-electric-semi () "Insert a \;. If variable `sml-electric-semi-mode' is t, indent the current line, insert a newline, and indent." (interactive) (self-insert-command 1) (if sml-electric-semi-mode (reindent-then-newline-and-indent))) ;;; Misc (defun sml-mark-function () "Mark the surrounding function. Or try to at least." (interactive) (if (not (fboundp 'smie-setup)) (mark-paragraph) ;; FIXME: Provide beginning-of-defun-function so mark-defun "just works". (let ((start (point))) (sml-beginning-of-defun) (let ((beg (point))) (smie-forward-sexp 'halfsexp) (if (or (< start beg) (> start (point))) (progn (goto-char start) (mark-paragraph)) (push-mark nil t t) (goto-char beg)))))) (defun sml-back-to-outer-indent () "Unindents to the next outer level of indentation." (interactive) (save-excursion (beginning-of-line) (skip-chars-forward "\t ") (let ((start-column (current-column)) (indent (current-column))) (if (> start-column 0) (progn (save-excursion (while (>= indent start-column) (if (re-search-backward "^[^\n]" nil t) (setq indent (current-indentation)) (setq indent 0)))) (backward-delete-char-untabify (- start-column indent))))))) (defun sml-smie-find-matching-starter (syms) (let ((halfsexp nil) tok) ;;(sml-smie-forward-token) (while (not (or (bobp) (member (nth 2 (setq tok (smie-backward-sexp halfsexp))) syms))) (cond ((null (car tok)) nil) ((numberp (car tok)) (setq halfsexp 'half)) (t (goto-char (cadr tok))))) (if (nth 2 tok) (goto-char (cadr tok))) (nth 2 tok))) (defun sml-find-matching-starter (syms) (cond ((and sml-use-smie (fboundp 'smie-backward-sexp)) (sml-smie-find-matching-starter syms)) ((fboundp 'sml-old-find-matching-starter) (sml-old-find-matching-starter syms)))) (defun sml-smie-skip-siblings () (let (tok) (while (and (not (bobp)) (progn (setq tok (smie-backward-sexp 'half)) (cond ((null (car tok)) t) ((numberp (car tok)) t) (t nil))))) (if (nth 2 tok) (goto-char (cadr tok))) (nth 2 tok))) (defun sml-skip-siblings () (cond ((and sml-use-smie (fboundp 'smie-backward-sexp)) (sml-smie-skip-siblings)) ((fboundp 'sml-old-skip-siblings) (sml-old-skip-siblings)) (t (up-list -1)))) (defun sml-beginning-of-defun () (let ((sym (sml-find-matching-starter sml-starters-syms))) (if (member sym '("fun" "and" "functor" "signature" "structure" "abstraction" "datatype" "abstype")) (save-excursion (sml-smie-forward-token) (forward-comment (point-max)) (sml-smie-forward-token)) ;; We're inside a "non function declaration": let's skip all other ;; declarations that we find at the same level and try again. (sml-skip-siblings) ;; Obviously, let's not try again if we're at bobp. (unless (bobp) (sml-beginning-of-defun))))) (defcustom sml-max-name-components 3 "Maximum number of components to use for the current function name." :type 'integer) (defun sml-current-fun-name () (save-excursion (let ((count sml-max-name-components) fullname name) (end-of-line) (while (and (> count 0) (setq name (sml-beginning-of-defun))) (decf count) (setq fullname (if fullname (concat name "." fullname) name)) ;; Skip all other declarations that we find at the same level. (sml-skip-siblings)) fullname))) ;;; INSERTING PROFORMAS (COMMON SML-FORMS) (defvar sml-forms-alist nil "*Alist of code templates. You can extend this alist to your heart's content. For each additional template NAME in the list, declare a keyboard macro or function (or interactive command) called 'sml-form-NAME'. If 'sml-form-NAME' is a function it takes no arguments and should insert the template at point\; if this is a command it may accept any sensible interactive call arguments\; keyboard macros can't take arguments at all. Apropos keyboard macros, see `name-last-kbd-macro' and `sml-addto-forms-alist'. `sml-forms-alist' understands let, local, case, abstype, datatype, signature, structure, and functor by default.") (defmacro sml-def-skeleton (name interactor &rest elements) (when (fboundp 'define-skeleton) (let ((fsym (intern (concat "sml-form-" name)))) ;; TODO: don't do the expansion in comments and strings. `(progn (add-to-list 'sml-forms-alist ',(cons name fsym)) (condition-case err ;; Try to use the new `system' flag. (define-abbrev sml-mode-abbrev-table ,name "" ',fsym nil 'system) (wrong-number-of-arguments (define-abbrev sml-mode-abbrev-table ,name "" ',fsym))) (when (fboundp 'abbrev-put) (let ((abbrev (abbrev-symbol ,name sml-mode-abbrev-table))) (abbrev-put abbrev :case-fixed t) (abbrev-put abbrev :enable-function (lambda () (not (nth 8 (syntax-ppss))))))) (define-skeleton ,fsym ,(format "SML-mode skeleton for `%s..' expressions" name) ,interactor ,(concat name " ") > ,@elements))))) (put 'sml-def-skeleton 'lisp-indent-function 2) (sml-def-skeleton "let" nil @ "\nin " > _ "\nend" >) (sml-def-skeleton "if" nil @ " then " > _ "\nelse " > _) (sml-def-skeleton "local" nil @ "\nin" > _ "\nend" >) (sml-def-skeleton "case" "Case expr: " str "\nof " > _ " => ") (sml-def-skeleton "signature" "Signature name: " str " =\nsig" > "\n" > _ "\nend" >) (sml-def-skeleton "structure" "Structure name: " str " =\nstruct" > "\n" > _ "\nend" >) (sml-def-skeleton "functor" "Functor name: " str " () : =\nstruct" > "\n" > _ "\nend" >) (sml-def-skeleton "datatype" "Datatype name and type params: " str " =" \n) (sml-def-skeleton "abstype" "Abstype name and type params: " str " =" \n _ "\nwith" > "\nend" >) ;; (sml-def-skeleton "struct" nil _ "\nend" >) (sml-def-skeleton "sig" nil _ "\nend" >) (sml-def-skeleton "val" nil @ " = " > _) (sml-def-skeleton "fn" nil @ " =>" > _) (sml-def-skeleton "fun" nil @ " =" > _) ;; (defun sml-forms-menu (menu) (mapcar (lambda (x) (vector (car x) (cdr x) t)) sml-forms-alist)) (defvar sml-last-form "let") (defun sml-electric-space () "Expand a symbol into an SML form, or just insert a space. If the point directly precedes a symbol for which an SML form exists, the corresponding form is inserted." (interactive) (let ((abbrev-mode (not abbrev-mode)) (last-command-event ?\ ) ;; Bind `this-command' to fool skeleton's special abbrev handling. (this-command 'self-insert-command)) (call-interactively 'self-insert-command))) (defun sml-insert-form (name newline) "Interactive short-cut to insert the NAME common ML form. If a prefix argument is given insert a NEWLINE and indent first, or just move to the proper indentation if the line is blank\; otherwise insert at point (which forces indentation to current column). The default form to insert is 'whatever you inserted last time' \(just hit return when prompted\)\; otherwise the command reads with completion from `sml-forms-alist'." (interactive (list (completing-read (format "Form to insert: (default %s) " sml-last-form) sml-forms-alist nil t nil) current-prefix-arg)) ;; default is whatever the last insert was... (if (string= name "") (setq name sml-last-form) (setq sml-last-form name)) (unless (or (not newline) (save-excursion (beginning-of-line) (looking-at "\\s-*$"))) (insert "\n")) (unless (/= ?w (char-syntax (preceding-char))) (insert " ")) (let ((f (cdr (assoc name sml-forms-alist)))) (cond ((commandp f) (command-execute f)) (f (funcall f)) (t (error "Undefined form: %s" name))))) ;; See also macros.el in emacs lisp dir. (defun sml-addto-forms-alist (name) "Assign a name to the last keyboard macro defined. Argument NAME is transmogrified to sml-form-NAME which is the symbol actually defined. The symbol's function definition becomes the keyboard macro string. If that works, NAME is added to `sml-forms-alist' so you'll be able to reinvoke the macro through \\[sml-insert-form]. You might want to save the macro to use in a later editing session -- see `insert-kbd-macro' and add these macros to your .emacs file. See also `edit-kbd-macro' which is bound to \\[edit-kbd-macro]." (interactive "sName for last kbd macro (\"sml-form-\" will be added): ") (when (string= name "") (error "No command name given")) (let ((fsym (intern (concat "sml-form-" name)))) (name-last-kbd-macro fsym) (message "Macro bound to %s" fsym) (add-to-list 'sml-forms-alist (cons name fsym)))) ;;; ;;; MLton support ;;; (defvar sml-mlton-command "mlton" "Command to run MLton. Can include arguments.") (defvar sml-mlton-mainfile nil) (defconst sml-mlton-error-regexp-alist ;; I wish they just changed MLton to use one of the standard ;; error formats. `(("^\\(?:Error\\|\\(Warning\\)\\): \\(.+\\) \\([0-9]+\\)\\.\\([0-9]+\\)\\.$" 2 3 4 ;; If subgroup 1 matched, then it's a warning, otherwise it's an error. ,@(if (fboundp 'compilation-fake-loc) '((1)))))) (defvar compilation-error-regexp-alist) (eval-after-load "compile" '(dolist (x sml-mlton-error-regexp-alist) (add-to-list 'compilation-error-regexp-alist x))) (defun sml-mlton-typecheck (mainfile) "typecheck using MLton." (interactive (list (if (and mainfile (not current-prefix-arg)) mainfile (read-file-name "Main file: ")))) (save-some-buffers) (require 'compile) (dolist (x sml-mlton-error-regexp-alist) (add-to-list 'compilation-error-regexp-alist x)) (with-current-buffer (find-file-noselect mainfile) (compile (concat sml-mlton-command " -stop tc " ;Stop right after type checking. (shell-quote-argument (file-relative-name buffer-file-name)))))) ;;; ;;; MLton's def-use info. ;;; (defvar sml-defuse-file nil) (defun sml-defuse-file () (or sml-defuse-file (sml-defuse-set-file))) (defun sml-defuse-set-file () "Specify the def-use file to use." (interactive) (setq sml-defuse-file (read-file-name "Def-use file: "))) (defun sml-defuse-symdata-at-point () (save-excursion (sml-smie-forward-token) (let ((symname (sml-smie-backward-token))) (if (equal symname "op") (save-excursion (setq symname (sml-smie-forward-token)))) (when (string-match "op " symname) (setq symname (substring symname (match-end 0))) (forward-word) (forward-comment (point-max))) (list symname ;; Def-use files seem to count chars, not columns. ;; We hope here that they don't actually count bytes. ;; Also they seem to start counting at 1. (1+ (- (point) (progn (beginning-of-line) (point)))) (save-restriction (widen) (1+ (count-lines (point-min) (point)))) buffer-file-name)))) (defconst sml-defuse-def-regexp "^[[:alpha:]]+ \\([^ \n]+\\) \\(.+\\) \\([0-9]+\\)\\.\\([0-9]+\\)$") (defconst sml-defuse-use-regexp-format "^ %s %d\\.%d $") (defun sml-defuse-jump-to-def () "Jump to the definition corresponding to the symbol at point." (interactive) (let ((symdata (sml-defuse-symdata-at-point))) (if (null (car symdata)) (error "Not on a symbol") (with-current-buffer (find-file-noselect (sml-defuse-file)) (goto-char (point-min)) (unless (re-search-forward (format sml-defuse-use-regexp-format (concat "\\(?:" ;; May be an absolute file name. (regexp-quote (nth 3 symdata)) "\\|" ;; Or a relative file name. (regexp-quote (file-relative-name (nth 3 symdata))) "\\)") (nth 2 symdata) (nth 1 symdata)) nil t) ;; FIXME: This is typically due to editing: any minor editing will ;; mess everything up. We should try to fail more gracefully. (error "Def-use info not found")) (unless (re-search-backward sml-defuse-def-regexp nil t) ;; This indicates a bug in this code. (error "Internal failure while looking up def-use")) (unless (equal (match-string 1) (nth 0 symdata)) ;; FIXME: This again is most likely due to editing. (error "Incoherence in the def-use info found")) (let ((line (string-to-number (match-string 3))) (char (string-to-number (match-string 4)))) (pop-to-buffer (find-file-noselect (match-string 2))) (goto-char (point-min)) (forward-line (1- line)) (forward-char (1- char))))))) ;;; ;;; SML/NJ's Compilation Manager support ;;; (defvar sml-cm-mode-syntax-table sml-mode-syntax-table) (defvar sml-cm-font-lock-keywords `(,(concat "\\<" (regexp-opt '("library" "group" "is" "structure" "functor" "signature" "funsig") t) "\\>"))) ;;;###autoload (add-to-list 'completion-ignored-extensions ".cm/") ;; This was used with the old compilation manager. (add-to-list 'completion-ignored-extensions "CM/") ;;;###autoload (add-to-list 'auto-mode-alist '("\\.cm\\'" . sml-cm-mode)) ;;;###autoload (define-derived-mode sml-cm-mode fundamental-mode "SML-CM" "Major mode for SML/NJ's Compilation Manager configuration files." (local-set-key "\C-c\C-c" 'sml-compile) (set (make-local-variable 'font-lock-defaults) '(sml-cm-font-lock-keywords nil t nil nil))) ;;; ;;; ML-Lex support ;;; (defvar sml-lex-font-lock-keywords (append '(("^%\\sw+" . font-lock-builtin-face) ("^%%" . font-lock-module-def-face)) sml-font-lock-keywords)) (defconst sml-lex-font-lock-defaults (cons 'sml-lex-font-lock-keywords (cdr sml-font-lock-defaults))) ;;;###autoload (define-derived-mode sml-lex-mode sml-mode "SML-Lex" "Major Mode for editing ML-Lex files." (set (make-local-variable 'font-lock-defaults) sml-lex-font-lock-defaults)) ;;; ;;; ML-Yacc support ;;; (defface sml-yacc-bnf-face '((t (:foreground "darkgreen"))) "Face used to highlight (non)terminals in `sml-yacc-mode'.") (defvar sml-yacc-bnf-face 'sml-yacc-bnf-face) (defcustom sml-yacc-indent-action 16 "Indentation column of the opening paren of actions." :type 'integer) (defcustom sml-yacc-indent-pipe nil "Indentation column of the pipe char in the BNF. If nil, align it with `:' or with previous cases." :type 'integer) (defcustom sml-yacc-indent-term nil "Indentation column of the (non)term part. If nil, align it with previous cases." :type 'integer) (defvar sml-yacc-font-lock-keywords (cons '("^\\(\\sw+\\s-*:\\|\\s-*|\\)\\(\\s-*\\sw+\\)*\\s-*\\(\\(%\\sw+\\)\\s-+\\sw+\\|\\)" (0 (save-excursion (save-match-data (goto-char (match-beginning 0)) (unless (or (re-search-forward "\\" (match-end 0) 'move) (progn (forward-comment (point-max)) (not (looking-at "(")))) sml-yacc-bnf-face)))) (4 font-lock-builtin-face t t)) sml-lex-font-lock-keywords)) (defconst sml-yacc-font-lock-defaults (cons 'sml-yacc-font-lock-keywords (cdr sml-font-lock-defaults))) (defun sml-yacc-indent-line () "Indent current line of ML-Yacc code." (let ((savep (> (current-column) (current-indentation))) (indent (max (or (ignore-errors (sml-yacc-indentation)) 0) 0))) (if savep (save-excursion (indent-line-to indent)) (indent-line-to indent)))) (defun sml-yacc-indentation () (save-excursion (back-to-indentation) (or (and (looking-at "%\\|\\(\\sw\\|\\s_\\)+\\s-*:") 0) (when (save-excursion (condition-case nil (progn (up-list -1) nil) (scan-error t))) ;; We're outside an action. (cond ;; Special handling of indentation inside %term and %nonterm ((save-excursion (and (re-search-backward "^%\\(\\sw+\\)" nil t) (member (match-string 1) '("term" "nonterm")))) (if (numberp sml-yacc-indent-term) sml-yacc-indent-term (let ((offset (if (looking-at "|") -2 0))) (forward-line -1) (looking-at "\\s-*\\(%\\sw*\\||\\)?\\s-*") (goto-char (match-end 0)) (+ offset (current-column))))) ((looking-at "(") sml-yacc-indent-action) ((looking-at "|") (if (numberp sml-yacc-indent-pipe) sml-yacc-indent-pipe (backward-sexp 1) (while (progn (forward-comment (- (point))) (/= 0 (skip-syntax-backward "w_")))) (forward-comment (- (point))) (if (not (looking-at "\\s-$")) (1- (current-column)) (skip-syntax-forward " ") (- (current-column) 2)))))) ;; default to SML rules (cond ((and sml-use-smie (fboundp 'smie-indent-calculate)) (smie-indent-calculate)) ((fboundp 'sml-calculate-indentation) (sml-calculate-indentation)))))) ;;;###autoload (add-to-list 'auto-mode-alist '("\\.grm\\'" . sml-yacc-mode)) ;;;###autoload (define-derived-mode sml-yacc-mode sml-mode "SML-Yacc" "Major Mode for editing ML-Yacc files." (set (make-local-variable 'indent-line-function) 'sml-yacc-indent-line) (set (make-local-variable 'font-lock-defaults) sml-yacc-font-lock-defaults)) (provide 'sml-mode) (unless (and sml-use-smie (fboundp 'smie-setup)) (require 'sml-oldindent)) ;;; sml-mode.el ends here sml-mode-5.0/sml-proc.el0000644005056000007300000007631011741417724016073 0ustar monnierparallelisme;;; sml-proc.el --- Comint based interaction mode for Standard ML. ;; Copyright (C) 1999,2000,2003,2004,2005,2007,2012 Stefan Monnier ;; Copyright (C) 1994-1997 Matthew J. Morley ;; Copyright (C) 1989 Lars Bo Nielsen ;; ==================================================================== ;; This file is not part of GNU Emacs, but it is distributed under the ;; same conditions. ;; This program is free software; you can redistribute it and/or ;; modify it under the terms of the GNU General Public License as ;; published by the Free Software Foundation; either version 3, or (at ;; your option) any later version. ;; This program is distributed in the hope that it will be useful, but ;; WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;; General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to the ;; Free Software Foundation, 675 Mass Ave, Cambridge, MA 0139, USA. ;; (See sml-mode.el for HISTORY.) ;; ==================================================================== ;; [MJM 10/94] Separating this from sml-mode means sml-mode will run ;; under 18.59 (or anywhere without comint, if there are such places). ;; See sml-mode.el for further information. ;;; Commentary: ;; Inferior-sml-mode is for interacting with an ML process run under ;; emacs. This uses the comint package so you get history, expansion, ;; backup and all the other benefits of comint. Interaction is ;; achieved by M-x run-sml which starts a sub-process under emacs. You may ;; need to set this up for autoloading in your .emacs: ;; (autoload 'run-sml "sml-proc" "Run an inferior ML process." t) ;; Exactly what process is governed by the variable sml-program-name ;; -- just "sml" by default. If you give a prefix argument (C-u M-x ;; run-sml) you will be prompted for a different program to execute from ;; the default -- if you just hit RETURN you get the default anyway -- ;; along with the option to specify any command line arguments. Once ;; you select the ML program name in this manner, it remains the ;; default (unless you set in a hook, or otherwise). ;; NOTE: inferior-sml-mode-hook is run AFTER the ML program has been ;; launched. inferior-sml-load-hook is run only when sml-proc.el is ;; loaded into Emacs. ;; When running an ML process some further key-bindings are effective ;; in sml-mode buffer(s). C-c C-s (switch-to-sml) will split the ;; screen into two windows if necessary and place you in the ML ;; process buffer. In the interaction buffer, C-c C-s is bound to the ;; `sml' command by default (in case you need to restart). ;; C-c C-l (sml-load-file) will load an SML source file into the ;; inferior process, C-c C-r (sml-send-region) will send the current ;; region of text to the ML process, etc. Given a prefix argument to ;; these commands will switch you from the SML buffer to the ML ;; process buffer as well as sending the text. If you get errors ;; reported by the compiler, C-x ` (next-error) will step through ;; the errors with you. ;; NOTE. There is only limited support for this as it obviously ;; depends on the compiler's error messages being recognised by the ;; mode. Error reporting is currently only geared up for SML/NJ, ;; Moscow ML, and Poly/ML. For other compilers, add the relevant ;; regexp to sml-error-regexp-alist and send it to me. ;; To send pieces of code to the underlying compiler, we never send the text ;; directly but use a temporary file instead. This breaks if the compiler ;; does not understand `use', but has the benefit of allowing better error ;; reporting. ;; Bugs: ;; Todo: ;; - Keep improving `sml-compile'. ;; - ignore warnings (if requested) for next-error ;;; Code: (eval-when-compile (require 'cl)) (require 'sml-mode) (require 'comint) (require 'compile) (defgroup sml-proc () "Interacting with an SML process." :group 'sml) (defcustom sml-program-name "sml" "Program to run as ML." :type '(string)) (defcustom sml-default-arg "" "Default command line option to pass, if any." :type '(string)) (defcustom sml-host-name "" "Host on which to run ML." :type '(string)) (defcustom sml-config-file "~/.smlproc.sml" "File that should be fed to the ML process when started." :type '(string)) (defcustom sml-compile-command "CM.make()" "The command used by default by `sml-compile'. See also `sml-compile-commands-alist'.") (defcustom sml-compile-commands-alist '(("CMB.make()" . "all-files.cm") ("CMB.make()" . "pathconfig") ("CM.make()" . "sources.cm") ("use \"load-all\"" . "load-all")) "Commands used by default by `sml-compile'. Each command is associated with its \"main\" file. It is perfectly OK to associate several files with a command or several commands with the same file.") (defvar inferior-sml-mode-hook nil "*This hook is run when the inferior ML process is started. All buffer local customisations for the interaction buffers go here.") (defvar sml-error-overlay nil "*Non-nil means use an overlay to highlight errorful code in the buffer. The actual value is the name of a face to use for the overlay. Instead of setting this variable to 'region, you can also simply keep it NIL and use (transient-mark-mode) which will provide similar benefits (but with several side effects).") (defvar sml-buffer nil "*The current ML process buffer. MULTIPLE PROCESS SUPPORT (Whoever wants multi-process support anyway?) ===================================================================== `sml-mode' supports, in a fairly simple fashion, running multiple ML processes. To run multiple ML processes, you start the first up with \\[sml]. It will be in a buffer named *sml*. Rename this buffer with \\[rename-buffer]. You may now start up a new process with another \\[sml]. It will be in a new buffer, named *sml*. You can switch between the different process buffers with \\[switch-to-buffer]. NB *sml* is just the default name for the buffer. It actually gets it's name from the value of `sml-program-name' -- *poly*, *smld*,... If you have more than one ML process around, commands that send text from source buffers to ML processes -- like `sml-send-function' or `sml-send-region' -- have to choose a process to send it to. This is determined by the global variable `sml-buffer'. Suppose you have three inferior ML's running: Buffer Process sml # mosml # *sml* #> If you do a \\[sml-send-function] command on some ML source code, what process do you send it to? - If you're in a process buffer (sml, mosml, or *sml*), you send it to that process (usually makes sense only to `sml-load-file'). - If you're in some other buffer (e.g., a source file), you send it to the process attached to buffer `sml-buffer'. This process selection is performed by function `sml-proc' which looks at the value of `sml-buffer' -- which must be a Lisp buffer object, or a string \(or nil\). Whenever \\[sml] fires up a new process, it resets `sml-buffer' to be the new process's buffer. If you only run one process, this will do the right thing. If you run multiple processes, you can change `sml-buffer' to another process buffer with \\[set-variable], or use the command \\[sml-buffer] in the interaction buffer of choice.") ;;; ALL STUFF THAT DEFAULTS TO THE SML/NJ COMPILER (0.93) (defvar sml-use-command "use \"%s\"" "*Template for loading a file into the inferior ML process. Set to \"use \\\"%s\\\"\" for SML/NJ or Edinburgh ML; set to \"PolyML.use \\\"%s\\\"\" for Poly/ML, etc.") (defvar sml-cd-command "OS.FileSys.chDir \"%s\"" "*Command template for changing working directories under ML. Set this to nil if your compiler can't change directories. The format specifier \"%s\" will be converted into the directory name specified when running the command \\[sml-cd].") (defcustom sml-prompt-regexp "^[-=>#] *" "Regexp used to recognise prompts in the inferior ML process." :type '(regexp)) (defvar sml-error-regexp-alist `( ;; Poly/ML messages ("^\\(Error\\|Warning:\\) in '\\(.+\\)', line \\([0-9]+\\)" 2 3) ;; Moscow ML ("^File \"\\([^\"]+\\)\", line \\([0-9]+\\)\\(-\\([0-9]+\\)\\)?, characters \\([0-9]+\\)-\\([0-9]+\\):" 1 2 5) ;; SML/NJ: the file-pattern is anchored to avoid ;; pathological behavior with very long lines. ("^[-= ]*\\(.*[^\n)]\\)\\( (.*)\\)?:\\([0-9]+\\)\\.\\([0-9]+\\)\\(-\\([0-9]+\\)\\.\\([0-9]+\\)\\)? \\(Error\\|Warnin\\(g\\)\\): .*" 1 ,@(if (fboundp 'compilation-fake-loc) ;New compile.el. '((3 . 6) (4 . 7) (9)) '(sml-make-error 3 4 6 7))) ;; SML/NJ's exceptions: see above. ("^ +\\(raised at: \\)?\\(.+\\):\\([0-9]+\\)\\.\\([0-9]+\\)\\(-\\([0-9]+\\)\\.\\([0-9]+\\)\\)" 2 ,@(if (fboundp 'compilation-fake-loc) ;New compile.el. '((3 . 6) (4 . 7)) '(sml-make-error 3 4 6 7)))) "Alist that specifies how to match errors in compiler output. See `compilation-error-regexp-alist' for a description of the format.") ;; font-lock support (defconst inferior-sml-font-lock-keywords `(;; prompt and following interactive command ;; FIXME: Actually, this should already be taken care of by comint. (,(concat "\\(" sml-prompt-regexp "\\)\\(.*\\)") (1 font-lock-prompt-face) (2 font-lock-command-face keep)) ;; CM's messages ("^\\[\\(.*GC #.*\n\\)*.*\\]" . font-lock-comment-face) ;; SML/NJ's irritating GC messages ("^GC #.*" . font-lock-comment-face) ;; error messages ,@(unless (fboundp 'compilation-fake-loc) (mapcar (lambda (ra) (cons (car ra) 'font-lock-warning-face)) sml-error-regexp-alist))) "Font-locking specification for inferior SML mode.") (defface font-lock-prompt-face '((t (:bold t))) "Font Lock mode face used to highlight prompts." :group 'font-lock-highlighting-faces) (defvar font-lock-prompt-face 'font-lock-prompt-face "Face name to use for prompts.") (defface font-lock-command-face '((t (:bold t))) "Font Lock mode face used to highlight interactive commands." :group 'font-lock-highlighting-faces) (defvar font-lock-command-face 'font-lock-command-face "Face name to use for interactive commands.") (defconst inferior-sml-font-lock-defaults '(inferior-sml-font-lock-keywords nil nil nil nil)) ;;; CODE (defvar inferior-sml-mode-map (let ((map (make-sparse-keymap))) (set-keymap-parent map comint-mode-map) (define-key map "\C-c\C-s" 'run-sml) (define-key map "\C-c\C-l" 'sml-load-file) (define-key map "\t" (if (fboundp 'completion-at-point) 'completion-at-point 'comint-dynamic-complete)) map) "Keymap for inferior-sml mode") ;; buffer-local (defvar sml-temp-file nil) ;;(defvar sml-error-file nil) ; file from which the last error came (defvar sml-error-cursor nil) ; ditto (defun sml-proc-buffer () "Return the current ML process buffer. or the current buffer if it is in `inferior-sml-mode'. Raises an error if the variable `sml-buffer' does not appear to point to an existing buffer." (or (and (eq major-mode 'inferior-sml-mode) (current-buffer)) (and sml-buffer (let ((buf (get-buffer sml-buffer))) ;; buffer-name returns nil if the buffer has been killed (and buf (buffer-name buf) buf))) ;; no buffer found, make a new one (save-excursion (call-interactively 'run-sml)))) (defun sml-buffer (echo) "Make the current buffer the current `sml-buffer' if that is sensible. Lookup variable `sml-buffer' to see why this might be useful. If prefix argument ECHO is set, then it only reports on the current state." (interactive "P") (when (not echo) (setq sml-buffer (if (eq major-mode 'inferior-sml-mode) (current-buffer) (read-buffer "Set ML process buffer to: " nil t)))) (message "ML process buffer is now %s." (or (ignore-errors (buffer-name (get-buffer sml-buffer))) "undefined"))) (defun sml-proc () "Return the current ML process. See variable `sml-buffer'." (assert (eq major-mode 'inferior-sml-mode)) (or (get-buffer-process (current-buffer)) (progn (call-interactively 'run-sml) (get-buffer-process (current-buffer))))) (defun sml-proc-comint-input-filter-function (str) ;; `compile.el' in Emacs-22 fails to notice that file location info from ;; errors should be recomputed afresh (without using stale info from ;; earlier compilations). We used to cause a refresh in sml-send-string, ;; but this doesn't catch the case when the user types commands directly ;; at the prompt. (compilation-forget-errors) ;Has to run before compilation-fake-loc. (if (and (fboundp 'compilation-fake-loc) sml-temp-file) (compilation-fake-loc (cdr sml-temp-file) (car sml-temp-file))) str) (defun inferior-sml-next-error-hook () ;; Try to recognize SML/NJ type error message and to highlight finely the ;; difference between the two types (in case they're large, it's not ;; always obvious to spot it). ;; ;; Sample messages: ;; ;; Data.sml:31.9-33.33 Error: right-hand-side of clause doesn't agree with function result type [tycon mismatch] ;; expression: Hstring ;; result type: Hstring * int ;; in declaration: ;; des2hs = (fn SYM_ID hs => hs ;; | SYM_OP hs => hs ;; | SYM_CHR hs => hs) ;; Data.sml:35.44-35.63 Error: operator and operand don't agree [tycon mismatch] ;; operator domain: Hstring * Hstring ;; operand: (Hstring * int) * (Hstring * int) ;; in expression: ;; HSTRING.ieq (h1,h2) ;; vparse.sml:1861.6-1922.14 Error: case object and rules don't agree [tycon mismatch] ;; rule domain: STConstraints list list option ;; object: STConstraints list option ;; in expression: (save-current-buffer (when (and (derived-mode-p 'sml-mode 'inferior-sml-mode) (boundp 'next-error-last-buffer) (bufferp next-error-last-buffer) (set-buffer next-error-last-buffer) (derived-mode-p 'inferior-sml-mode) ;; The position of `point' is not guaranteed :-( (looking-at (concat ".*\\[tycon mismatch\\]\n" " \\(operator domain\\|expression\\|rule domain\\): +"))) (ignore-errors (require 'smerge-mode)) (if (not (fboundp 'smerge-refine-subst)) (remove-hook 'next-error-hook 'inferior-sml-next-error-hook) (save-excursion (let ((b1 (match-end 0)) e1 b2 e2) (when (re-search-forward "\n in \\(expression\\|declaration\\):\n" nil t) (setq e2 (match-beginning 0)) (when (re-search-backward "\n \\(operand\\|result type\\|object\\): +" b1 t) (setq e1 (match-beginning 0)) (setq b2 (match-end 0)) (smerge-refine-subst b1 e1 b2 e2 '((face . smerge-refined-change))))))))))) (define-derived-mode inferior-sml-mode comint-mode "Inferior-SML" "Major mode for interacting with an inferior ML process. The following commands are available: \\{inferior-sml-mode-map} An ML process can be fired up (again) with \\[sml]. Customisation: Entry to this mode runs the hooks on `comint-mode-hook' and `inferior-sml-mode-hook' (in that order). Variables controlling behaviour of this mode are `sml-program-name' (default \"sml\") Program to run as ML. `sml-use-command' (default \"use \\\"%s\\\"\") Template for loading a file into the inferior ML process. `sml-cd-command' (default \"System.Directory.cd \\\"%s\\\"\") ML command for changing directories in ML process (if possible). `sml-prompt-regexp' (default \"^[\\-=] *\") Regexp used to recognise prompts in the inferior ML process. You can send text to the inferior ML process from other buffers containing ML source. `switch-to-sml' switches the current buffer to the ML process buffer. `sml-send-function' sends the current *paragraph* to the ML process. `sml-send-region' sends the current region to the ML process. Prefixing the sml-send- commands with \\[universal-argument] causes a switch to the ML process buffer after sending the text. For information on running multiple processes in multiple buffers, see documentation for variable `sml-buffer'. Commands: RET after the end of the process' output sends the text from the end of process to point. RET before the end of the process' output copies the current line to the end of the process' output, and sends it. DEL converts tabs to spaces as it moves back. TAB file name completion, as in shell-mode, etc.." (setq comint-prompt-regexp sml-prompt-regexp) (sml-mode-variables) ;; We have to install it globally, 'cause it's run in the *source* buffer :-( (add-hook 'next-error-hook 'inferior-sml-next-error-hook) ;; Make TAB add a " rather than a space at the end of a file name. (set (make-local-variable 'comint-completion-addsuffix) '(?/ . ?\")) (add-hook 'comint-input-filter-functions 'sml-proc-comint-input-filter-function nil t) (set (make-local-variable 'font-lock-defaults) inferior-sml-font-lock-defaults) ;; For sequencing through error messages: (set (make-local-variable 'sml-error-cursor) (point-max-marker)) (set-marker-insertion-type sml-error-cursor nil) ;; Compilation support (used for `next-error'). ;; The keymap of compilation-minor-mode is too unbearable, so we ;; just can't use the minor-mode if we can't override the map. (when (boundp 'minor-mode-overriding-map-alist) (set (make-local-variable 'compilation-error-regexp-alist) sml-error-regexp-alist) (compilation-minor-mode 1) ;; Eliminate compilation-minor-mode's map. (let ((map (make-sparse-keymap))) (dolist (keys '([menu-bar] [follow-link])) ;; Preserve some of the bindings. (define-key map keys (lookup-key compilation-minor-mode-map keys))) (add-to-list 'minor-mode-overriding-map-alist (cons 'compilation-minor-mode map))) ;; I'm sure people might kill me for that (setq compilation-error-screen-columns nil) (make-local-variable 'sml-endof-error-alist)) ;;(make-local-variable 'sml-error-overlay) (setq mode-line-process '(": %s"))) ;;; FOR RUNNING ML FROM EMACS ;;;###autoload (autoload 'run-sml "sml-proc" nil t) (defalias 'run-sml 'sml-run) (defun sml-run (cmd arg &optional host) "Run the program CMD with given arguments ARG. The command is run in buffer *CMD* using mode `inferior-sml-mode'. If the buffer already exists and has a running process, then just go to this buffer. This updates `sml-buffer' to the new buffer. You can have several inferior M(or L process running, but only one (> s current one -- given by `sml-buffer' (qv). If a prefix argument is used, the user is also prompted for a HOST on which to run CMD using `remote-shell-program'. \(Type \\[describe-mode] in the process buffer for a list of commands.)" (interactive (list (read-string "ML command: " sml-program-name) (if (or current-prefix-arg (> (length sml-default-arg) 0)) (read-string "Any args: " sml-default-arg) sml-default-arg) (if (or current-prefix-arg (> (length sml-host-name) 0)) (read-string "On host: " sml-host-name) sml-host-name))) (let* ((pname (file-name-nondirectory cmd)) (args (if (equal arg "") () (split-string arg))) (file (when (and sml-config-file (file-exists-p sml-config-file)) sml-config-file))) ;; and this -- to keep these as defaults even if ;; they're set in the mode hooks. (setq sml-program-name cmd) (setq sml-default-arg arg) (setq sml-host-name host) ;; For remote execution, use `remote-shell-program' (when (> (length host) 0) (setq args (list* host "cd" default-directory ";" cmd args)) (setq cmd remote-shell-program)) ;; go for it (let ((exec-path (if (file-name-directory cmd) ;; If the command has slashes, make sure we ;; first look relative to the current directory. ;; Emacs-21 does it for us, but not Emacs-20. (cons default-directory exec-path) exec-path))) (setq sml-buffer (apply 'make-comint pname cmd file args))) (pop-to-buffer sml-buffer) ;;(message (format "Starting \"%s\" in background." pname)) (inferior-sml-mode) (goto-char (point-max)) sml-buffer)) (defun switch-to-sml (eobp) "Switch to the ML process buffer. Move point to the end of buffer unless prefix argument EOBP is set." (interactive "P") (pop-to-buffer (sml-proc-buffer)) (unless eobp (push-mark (point) t) (goto-char (point-max)))) ;; Fakes it with a "use ;" if necessary. (defun sml-send-region (start end &optional and-go) "Send current region START..END to the inferior ML process. Prefix AND-GO argument means switch-to-sml afterwards. The region is written out to a temporary file and a \"use \" command is sent to the compiler. See variables `sml-use-command'." (interactive "r\nP") (if (= start end) (message "The region is zero (ignored)") (let* ((buf (sml-proc-buffer)) (marker (copy-marker start)) (tmp (make-temp-file "sml"))) (write-region start end tmp nil 'silently) (with-current-buffer buf (when sml-temp-file (ignore-errors (delete-file (car sml-temp-file))) (set-marker (cdr sml-temp-file) nil)) (setq sml-temp-file (cons tmp marker)) (sml-send-string (format sml-use-command tmp) nil and-go))))) ;; This is quite bogus, so it isn't bound to a key by default. ;; Anyone coming up with an algorithm to recognise fun & local ;; declarations surrounding point will do everyone a favour! (defun sml-send-function (&optional and-go) "Send current paragraph to the inferior ML process. With a prefix argument AND-GO switch to the sml buffer as well \(cf. `sml-send-region'\)." (interactive "P") (save-excursion (sml-mark-function) (sml-send-region (point) (mark))) (if and-go (switch-to-sml nil))) (defvar sml-source-modes '(sml-mode) "*Used to determine if a buffer contains ML source code. If it's loaded into a buffer that is in one of these major modes, it's considered an ML source file by `sml-load-file'. Used by these commands to determine defaults.") (defun sml-send-buffer (&optional and-go) "Send buffer to inferior shell running ML process. With a prefix argument AND-GO switch to the sml buffer as well \(cf. `sml-send-region'\)." (interactive "P") (if (memq major-mode sml-source-modes) (sml-send-region (point-min) (point-max) and-go))) ;; Since sml-send-function/region take an optional prefix arg, these ;; commands are redundant. But they are kept around for the user to ;; bind if she wishes, since its easier to type C-c r than C-u C-c C-r. (defun sml-send-region-and-go (start end) "Send current region START..END to the inferior ML process, and go there." (interactive "r") (sml-send-region start end t)) (defun sml-send-function-and-go () "Send current paragraph to the inferior ML process, and go there." (interactive) (sml-send-function t)) ;;; LOADING AND IMPORTING SOURCE FILES: (defvar sml-prev-dir/file nil "Cache for (DIRECTORY . FILE) pair last. Set in `sml-load-file' and `sml-cd' commands. Used to determine the default in the next `ml-load-file'.") (defun sml-load-file (&optional and-go) "Load an ML file into the current inferior ML process. With a prefix argument AND-GO switch to sml buffer as well. This command uses the ML command template `sml-use-command' to construct the command to send to the ML process\; a trailing \"\;\\n\" will be added automatically." (interactive "P") (let ((file (car (comint-get-source "Load ML file: " sml-prev-dir/file sml-source-modes t)))) (with-current-buffer (sml-proc-buffer) ;; Check if buffer needs saved. Should (save-some-buffers) instead? (comint-check-source file) (setq sml-prev-dir/file (cons (file-name-directory file) (file-name-nondirectory file))) (sml-send-string (format sml-use-command file) nil and-go)))) (defun sml-cd (dir) "Change the working directory of the inferior ML process. The default directory of the process buffer is changed to DIR. If the variable `sml-cd-command' is non-nil it should be an ML command that will be executed to change the compiler's working directory\; a trailing \"\;\\n\" will be added automatically." (interactive "DSML Directory: ") (let ((dir (expand-file-name dir))) (with-current-buffer (sml-proc-buffer) (sml-send-string (format sml-cd-command dir) t) (setq default-directory dir)) (setq sml-prev-dir/file (cons dir nil)))) (defun sml-send-string (str &optional print and-go) (let ((proc (sml-proc)) (str (concat str ";\n")) (win (get-buffer-window (current-buffer) 'visible))) (when win (select-window win)) (goto-char (point-max)) (when print (insert str)) (sml-update-cursor) (set-marker (process-mark proc) (point-max)) (setq compilation-last-buffer (current-buffer)) (comint-send-string proc str) (when and-go (switch-to-sml nil)))) (defun sml-compile (command &optional and-go) "Pass a COMMAND to the SML process to compile the current program. You can then use the command \\[next-error] to find the next error message and move to the source code that caused it. Interactively, prompts for the command if `compilation-read-command' is non-nil. With prefix arg, always prompts. Prefix arg AND-GO also means to `switch-to-sml' afterwards." (interactive (let* ((dir default-directory) (cmd "cd \".")) ;; look for files to determine the default command (while (and (stringp dir) (dolist (cf sml-compile-commands-alist 1) (when (file-exists-p (expand-file-name (cdr cf) dir)) (setq cmd (concat cmd "\"; " (car cf))) (return nil)))) (let ((newdir (file-name-directory (directory-file-name dir)))) (setq dir (unless (equal newdir dir) newdir)) (setq cmd (concat cmd "/..")))) (setq cmd (cond ((local-variable-p 'sml-compile-command) sml-compile-command) ((string-match "^\\s-*cd\\s-+\"\\.\"\\s-*;\\s-*" cmd) (substring cmd (match-end 0))) ((string-match "^\\s-*cd\\s-+\"\\(\\./\\)" cmd) (replace-match "" t t cmd 1)) ((string-match ";" cmd) cmd) (t sml-compile-command))) ;; code taken from compile.el (if (or compilation-read-command current-prefix-arg) (list (read-from-minibuffer "Compile command: " cmd nil nil '(compile-history . 1))) (list cmd)))) ;; ;; now look for command's file to determine the directory ;; (setq dir default-directory) ;; (while (and (stringp dir) ;; (dolist (cf sml-compile-commands-alist t) ;; (when (and (equal cmd (car cf)) ;; (file-exists-p (expand-file-name (cdr cf) dir))) ;; (return nil)))) ;; (let ((newdir (file-name-directory (directory-file-name dir)))) ;; (setq dir (unless (equal newdir dir) newdir)))) ;; (setq dir (or dir default-directory)) ;; (list cmd dir))) (set (make-local-variable 'sml-compile-command) command) (save-some-buffers (not compilation-ask-about-save) nil) (let ((dir default-directory)) (when (string-match "^\\s-*cd\\s-+\"\\([^\"]+\\)\"\\s-*;" command) (setq dir (match-string 1 command)) (setq command (replace-match "" t t command))) (setq dir (expand-file-name dir)) (with-current-buffer (sml-proc-buffer) (setq default-directory dir) (sml-send-string (concat (format sml-cd-command dir) "; " command) t and-go)))) ;;; PARSING ERROR MESSAGES ;; This should need no modification to support other compilers. ;; Update the buffer-local error-cursor in proc-buffer to be its ;; current proc mark. (defvar sml-endof-error-alist nil) (defun sml-update-cursor () ;; Update buffer local variable. (set-marker sml-error-cursor (1- (process-mark (sml-proc)))) (setq sml-endof-error-alist nil) ;; This is now done in comint-input-filter-functions. ;; (compilation-forget-errors) ;Has to run before compilation-fake-loc. ;; (if (and (fboundp 'compilation-fake-loc) sml-temp-file) ;; (compilation-fake-loc (cdr sml-temp-file) (car sml-temp-file))) (if (markerp compilation-parsing-end) (set-marker compilation-parsing-end sml-error-cursor) (setq compilation-parsing-end sml-error-cursor))) (defun sml-make-error (f c) (let ((err (point-marker)) (linenum (string-to-number c)) (filename (list (first f) (second f))) (column (string-to-number (match-string (third f))))) ;; record the end of error, if any (when (fourth f) (let ((endlinestr (match-string (fourth f)))) (when endlinestr (let* ((endline (string-to-number endlinestr)) (endcol (string-to-number (or (match-string (fifth f)) "0"))) (linediff (- endline linenum))) (push (list err linediff (if (= 0 linediff) (- endcol column) endcol)) sml-endof-error-alist))))) ;; build the error descriptor (if (string= (car sml-temp-file) (first f)) ;; special case for code sent via sml-send-region (let ((marker (cdr sml-temp-file))) (with-current-buffer (marker-buffer marker) (goto-char marker) (forward-line (1- linenum)) (forward-char (1- column)) ;; A pair of markers is the right thing to return, but some ;; code in compile.el doesn't like it (when we reach the end ;; of the errors). So we could try to avoid it, but we don't ;; because that doesn't work correctly if the current buffer ;; has unsaved modifications. And it's fixed in Emacs-21. ;; (if buffer-file-name ;; (list err buffer-file-name ;; (count-lines (point-min) (point)) (current-column)) (cons err (point-marker)))) ;; ) ;; taken from compile.el (list err filename linenum column)))) (unless (fboundp 'compilation-fake-loc) (defadvice compilation-goto-locus (after sml-endof-error activate) (let* ((next-error (ad-get-arg 0)) (err (car next-error)) (pos (cdr next-error)) (endof (with-current-buffer (marker-buffer err) (assq err sml-endof-error-alist)))) (if (not endof) (sml-error-overlay 'undo) (with-current-buffer (marker-buffer pos) (goto-char pos) (let ((linediff (second endof)) (coldiff (third endof))) (when (> 0 linediff) (forward-line linediff)) (forward-char coldiff)) (sml-error-overlay nil pos (point)) (push-mark nil t (not sml-error-overlay)) (goto-char pos)))))) (defun sml-error-overlay (undo &optional beg end) "Move `sml-error-overlay' to the text region in the current buffer. If the buffer-local variable `sml-error-overlay' is non-nil it should be an overlay \(or extent, in XEmacs speak\)\; this function moves the overlay over the current region. If the optional BUFFER argument is given, move the overlay in that buffer instead of the current buffer. Called interactively, the optional prefix argument UNDO indicates that the overlay should simply be removed: \\[universal-argument] \ \\[sml-error-overlay]." (interactive "P") (when sml-error-overlay (unless (overlayp sml-error-overlay) (let ((ol sml-error-overlay)) (setq sml-error-overlay (make-overlay (point) (point))) (overlay-put sml-error-overlay 'face (if (symbolp ol) ol 'region)))) (if undo (delete-overlay sml-error-overlay) ;; If active regions, signals mark not active if no region set. (move-overlay sml-error-overlay (or beg (region-beginning)) (or end (region-end)) (current-buffer))))) (provide 'sml-proc) ;;; sml-proc.el ends here sml-mode-5.0/sml-oldindent.el0000644005056000007300000006002711741417724017106 0ustar monnierparallelisme;;; sml-oldindent.el --- Navigation and indentation for SML without SMIE ;; Copyright (C) 1999,2000,2004,2007,2012 Stefan Monnier ;; ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 3 of the License, or ;; (at your option) any later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software ;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ;;; Commentary: ;;; Code: (eval-when-compile (require 'cl)) (require 'sml-mode) (defun sml-preproc-alist (al) "Expand an alist AL where keys can be lists of keys into a normal one." (apply #'nconc (mapcar (lambda (x) (let ((k (car x)) (v (cdr x))) (if (consp k) (mapcar (lambda (y) (cons y v)) k) (list x)))) al))) (defconst sml-begin-syms '("let" "abstype" "local" "struct" "sig") "Symbols matching the `end' symbol.") (defconst sml-begin-syms-re (sml-syms-re sml-begin-syms) "Symbols matching the `end' symbol.") ;; (defconst sml-user-begin-symbols-re ;; (sml-syms-re '("let" "abstype" "local" "struct" "sig" "in" "with")) ;; "Symbols matching (loosely) the `end' symbol.") (defconst sml-sexp-head-symbols-re (sml-syms-re `("let" "abstype" "local" "struct" "sig" "in" "with" "if" "then" "else" "case" "of" "fn" "fun" "val" "and" "datatype" "type" "exception" "open" "infix" "infixr" "nonfix" ,@sml-module-head-syms "handle" "raise")) "Symbols starting an sexp.") ;; (defconst sml-not-arg-start-re ;; (sml-syms-re '("in" "of" "end" "andalso")) ;; "Symbols that can't be found at the head of an arg.") ;; (defconst sml-not-arg-re ;; (sml-syms-re '("in" "of" "end" "andalso")) ;; "Symbols that should not be confused with an arg.") (defconst sml-indent-rule (sml-preproc-alist `(("struct" . 0) (,sml-module-head-syms "d=" 0) ("local" "in" 0) ;;("of" . (3 nil)) ;;("else" . (sml-indent-level 0)) ;;(("in" "fun" "and" "of") . (sml-indent-level nil)) ("if" "else" 0) (,sml-=-starter-syms nil) (("abstype" "case" "datatype" "if" "then" "else" "sharing" "infix" "infixr" "let" "local" "nonfix" "open" "raise" "sig" "struct" "type" "val" "while" "do" "with" "withtype"))))) (defconst sml-delegate (sml-preproc-alist `((("of" "else" "then" "d=") . (not (sml-bolp))) ("in" . t))) "Words which might delegate indentation to their parent.") (defcustom sml-symbol-indent '(("fn" . -3) ("of" . 1) ("|" . -2) ("," . -2) (";" . -2) ;;("in" . 1) ("d=" . 2)) "Special indentation alist for some symbols. An entry like (\"in\" . 1) indicates that a line starting with the symbol `in' should be indented one char further to the right. This is only used in a few specific cases, so it does not work for all symbols and in all lines starting with the given symbol." :group 'sml :type '(repeat (cons string integer))) (defconst sml-open-paren (sml-preproc-alist `((,(list* "with" "in" sml-begin-syms) ,sml-begin-syms-re "\\"))) "Symbols that should behave somewhat like opening parens.") (defconst sml-close-paren `(("in" "\\") ("with" "\\") ("withtype" "\\<\\(abs\\|data\\)type\\>") ("end" ,sml-begin-syms-re) ("then" "\\") ("else" "\\" (sml-bolp)) ("of" "\\") ("d=" nil)) "Symbols that should behave somewhat like close parens.") (defconst sml-agglomerate-re "\\" "Regexp of compound symbols (pairs of symbols to be considered as one).") (defvar sml-internal-syntax-table (let ((st (make-syntax-table sml-mode-syntax-table))) (modify-syntax-entry ?_ "w" st) (modify-syntax-entry ?' "w" st) (modify-syntax-entry ?. "w" st) ;; Treating `~' as a word constituent is not quite right, but ;; close enough. Think about 12.3E~2 for example. Also `~' on its ;; own *is* a nonfix symbol. (modify-syntax-entry ?~ "w" st) st) "Syntax table used for internal sml-mode operation.") ;;; ;;; various macros ;;; (defmacro sml-with-ist (&rest r) (let ((ost-sym (make-symbol "oldtable"))) `(let ((,ost-sym (syntax-table)) (case-fold-search nil) (parse-sexp-lookup-properties t) (parse-sexp-ignore-comments t)) (unwind-protect (progn (set-syntax-table sml-internal-syntax-table) . ,r) (set-syntax-table ,ost-sym))))) (def-edebug-spec sml-with-ist t) (defmacro sml-move-if (&rest body) (let ((pt-sym (make-symbol "point")) (res-sym (make-symbol "result"))) `(let ((,pt-sym (point)) (,res-sym ,(cons 'progn body))) (unless ,res-sym (goto-char ,pt-sym)) ,res-sym))) (def-edebug-spec sml-move-if t) (defmacro sml-point-after (&rest body) `(save-excursion ,@body (point))) (def-edebug-spec sml-point-after t) ;; (defvar sml-op-prec (sml-preproc-alist '(("before" . 0) ((":=" "o") . 3) ((">" ">=" "<>" "<" "<=" "=") . 4) (("::" "@") . 5) (("+" "-" "^") . 6) (("/" "*" "quot" "rem" "div" "mod") . 7))) "Alist of SML infix operators and their precedence.") (defconst sml-syntax-prec (sml-preproc-alist `((("in" "with") . 10) ((";" ",") . 20) (("=>" "d=" "=of") . (65 . 40)) ("|" . (47 . 30)) (("case" "of" "fn") . 45) (("if" "then" "else" "while" "do" "raise") . 50) ("handle" . 60) ("orelse" . 70) ("andalso" . 80) ((":" ":>") . 90) ("->" . 95) (,(cons "end" sml-begin-syms) . 10000))) "Alist of pseudo-precedence of syntactic elements.") (defun sml-op-prec (op dir) "Return the precedence of OP or nil if it's not an infix. DIR should be set to BACK if you want to precedence w.r.t the left side and to FORW for the precedence w.r.t the right side. This assumes that we are `looking-at' the OP." (when op (let ((sprec (cdr (assoc op sml-syntax-prec)))) (cond ((consp sprec) (if (eq dir 'back) (car sprec) (cdr sprec))) (sprec sprec) (t (let ((prec (cdr (assoc op sml-op-prec)))) (when prec (+ prec 100)))))))) ;; (defun sml-forward-spaces () (forward-comment 100000)) (defun sml-backward-spaces () (forward-comment -100000)) ;; ;; moving forward around matching symbols ;; (defun sml-looking-back-at (re) (save-excursion (when (= 0 (skip-syntax-backward "w_")) (backward-char)) (looking-at re))) (defun sml-find-match-forward (this match) "Only works for word matches." (let ((level 1) (forward-sexp-function nil) (either (concat this "\\|" match))) (while (and (not (eobp)) (> level 0)) (forward-sexp 1) (while (not (or (eobp) (sml-looking-back-at either))) (condition-case () (forward-sexp 1) (error (forward-char 1)))) (setq level (cond ((sml-looking-back-at this) (1+ level)) ((sml-looking-back-at match) (1- level)) (t (error "Unbalanced"))))) t)) (defun sml-find-match-backward (this match) (let ((level 1) (forward-sexp-function nil) (either (concat this "\\|" match))) (while (> level 0) (backward-sexp 1) (while (not (or (bobp) (looking-at either))) (condition-case () (backward-sexp 1) (error (backward-char 1)))) (setq level (cond ((looking-at this) (1+ level)) ((looking-at match) (1- level)) (t (error "Unbalanced"))))) t)) ;;; ;;; Read a symbol, including the special "op " case ;;; (defmacro sml-move-read (&rest body) (let ((pt-sym (make-symbol "point"))) `(let ((,pt-sym (point))) ,@body (when (/= (point) ,pt-sym) (buffer-substring-no-properties (point) ,pt-sym))))) (def-edebug-spec sml-move-read t) (defun sml-poly-equal-p () (< (sml-point-after (re-search-backward sml-=-starter-re nil 'move)) (sml-point-after (re-search-backward "=" nil 'move)))) (defun sml-nested-of-p () (< (sml-point-after (re-search-backward sml-non-nested-of-starter-re nil 'move)) (sml-point-after (re-search-backward "\\" nil 'move)))) (defun sml-forward-sym-1 () (or (/= 0 (skip-syntax-forward "'w_")) (/= 0 (skip-syntax-forward ".'")))) (defun sml-forward-sym () (let ((sym (sml-move-read (sml-forward-sym-1)))) (cond ((equal "op" sym) (sml-forward-spaces) (concat "op " (or (sml-move-read (sml-forward-sym-1)) ""))) ((equal sym "=") (save-excursion (sml-backward-sym-1) (if (sml-poly-equal-p) "=" "d="))) ((equal sym "of") (save-excursion (sml-backward-sym-1) (if (sml-nested-of-p) "of" "=of"))) ;; ((equal sym "datatype") ;; (save-excursion ;; (sml-backward-sym-1) ;; (sml-backward-spaces) ;; (if (eq (preceding-char) ?=) "=datatype" sym))) (t sym)))) (defun sml-backward-sym-1 () (or (/= 0 (skip-syntax-backward ".'")) (/= 0 (skip-syntax-backward "'w_")))) (defun sml-backward-sym () (let ((sym (sml-move-read (sml-backward-sym-1)))) (when sym ;; FIXME: what should we do if `sym' = "op" ? (let ((point (point))) (sml-backward-spaces) (if (equal "op" (sml-move-read (sml-backward-sym-1))) (concat "op " sym) (goto-char point) (cond ((string= sym "=") (if (sml-poly-equal-p) "=" "d=")) ((string= sym "of") (if (sml-nested-of-p) "of" "=of")) ;; ((string= sym "datatype") ;; (save-excursion (sml-backward-spaces) ;; (if (eq (preceding-char) ?=) "=datatype" sym))) (t sym))))))) (defun sml-backward-sexp (prec) "Move one sexp backward if possible, or one char else. Returns t if the move indeed moved through one sexp and nil if not. PREC is the precedence currently looked for." (let ((parse-sexp-lookup-properties t) (parse-sexp-ignore-comments t)) (sml-backward-spaces) (let* ((op (sml-backward-sym)) (op-prec (sml-op-prec op 'back)) match) (cond ((not op) (let ((point (point))) (ignore-errors (let ((forward-sexp-function nil)) (backward-sexp 1))) (if (/= point (point)) t (ignore-errors (backward-char 1)) nil))) ;; stop as soon as precedence is smaller than `prec' ((and prec op-prec (>= prec op-prec)) nil) ;; special rules for nested constructs like if..then..else ((and (or (not prec) (and prec op-prec)) (setq match (second (assoc op sml-close-paren)))) (sml-find-match-backward (concat "\\<" op "\\>") match)) ;; don't back over open-parens ((assoc op sml-open-paren) nil) ;; infix ops precedence ((and prec op-prec) (< prec op-prec)) ;; [ prec = nil ] a new operator, let's skip the sexps until the next (op-prec (while (sml-move-if (sml-backward-sexp op-prec))) t) ;; special symbols indicating we're getting out of a nesting level ((string-match sml-sexp-head-symbols-re op) nil) ;; if the op was not alphanum, then we still have to do the backward-sexp ;; this reproduces the usual backward-sexp, but it might be bogus ;; in this case since !@$% is a perfectly fine symbol (t t))))) ;(or (string-match "\\sw" op) (sml-backward-sexp prec)) (defun sml-forward-sexp (prec) "Moves one sexp forward if possible, or one char else. Returns T if the move indeed moved through one sexp and NIL if not." (let ((parse-sexp-lookup-properties t) (parse-sexp-ignore-comments t)) (sml-forward-spaces) (let* ((op (sml-forward-sym)) (op-prec (sml-op-prec op 'forw)) match) (cond ((not op) (let ((point (point))) (ignore-errors (let ((forward-sexp-function nil)) (forward-sexp 1))) (if (/= point (point)) t (forward-char 1) nil))) ;; stop as soon as precedence is smaller than `prec' ((and prec op-prec (>= prec op-prec)) nil) ;; special rules for nested constructs like if..then..else ((and (or (not prec) (and prec op-prec)) (setq match (cdr (assoc op sml-open-paren)))) (sml-find-match-forward (first match) (second match))) ;; don't forw over close-parens ((assoc op sml-close-paren) nil) ;; infix ops precedence ((and prec op-prec) (< prec op-prec)) ;; [ prec = nil ] a new operator, let's skip the sexps until the next (op-prec (while (sml-move-if (sml-forward-sexp op-prec))) t) ;; special symbols indicating we're getting out of a nesting level ((string-match sml-sexp-head-symbols-re op) nil) ;; if the op was not alphanum, then we still have to do the backward-sexp ;; this reproduces the usual backward-sexp, but it might be bogus ;; in this case since !@$% is a perfectly fine symbol (t t))))) ;(or (string-match "\\sw" op) (sml-backward-sexp prec)) (defun sml-in-word-p () (and (eq ?w (char-syntax (or (char-before) ? ))) (eq ?w (char-syntax (or (char-after) ? ))))) (defun sml-user-backward-sexp (&optional count) "Like `backward-sexp' but tailored to the SML syntax." (interactive "p") (unless count (setq count 1)) (sml-with-ist (let ((point (point))) (if (< count 0) (sml-user-forward-sexp (- count)) (when (sml-in-word-p) (forward-word 1)) (dotimes (i count) (unless (sml-backward-sexp nil) (goto-char point) (error "Containing expression ends prematurely"))))))) (defun sml-user-forward-sexp (&optional count) "Like `forward-sexp' but tailored to the SML syntax." (interactive "p") (unless count (setq count 1)) (sml-with-ist (let ((point (point))) (if (< count 0) (sml-user-backward-sexp (- count)) (when (sml-in-word-p) (backward-word 1)) (dotimes (i count) (unless (sml-forward-sexp nil) (goto-char point) (error "Containing expression ends prematurely"))))))) ;;(defun sml-forward-thing () ;; (if (= ?w (char-syntax (char-after))) (forward-word 1) (forward-char 1))) (defun sml-backward-arg () (sml-backward-sexp 1000)) (defun sml-forward-arg () (sml-forward-sexp 1000)) (provide 'sml-move) (defvar sml-rightalign-and) (defvar sml-indent-args) (defvar sml-indent-level) (defun sml-indent-line () "Indent current line of ML code." (interactive) (let ((savep (> (current-column) (current-indentation))) (indent (max (or (ignore-errors (sml-calculate-indentation)) 0) 0))) (if savep (save-excursion (indent-line-to indent)) (indent-line-to indent)))) (defun sml-find-comment-indent () (save-excursion (let ((depth 1)) (while (> depth 0) (if (re-search-backward "(\\*\\|\\*)" nil t) (cond ;; FIXME: That's just a stop-gap. ((eq (get-text-property (point) 'face) 'font-lock-string-face)) ((looking-at "*)") (incf depth)) ((looking-at comment-start-skip) (decf depth))) (setq depth -1))) (if (= depth 0) (1+ (current-column)) nil)))) (defun sml-calculate-indentation () (save-excursion (beginning-of-line) (skip-chars-forward "\t ") (sml-with-ist ;; Indentation for comments alone on a line, matches the ;; proper indentation of the next line. (when (looking-at "(\\*") (sml-forward-spaces)) (let (data (sym (save-excursion (sml-forward-sym)))) (or ;; Allow the user to override the indentation. (when (looking-at (concat ".*" (regexp-quote comment-start) "[ \t]*fixindent[ \t]*" (regexp-quote comment-end))) (current-indentation)) ;; Continued comment. (and (looking-at "\\*") (sml-find-comment-indent)) ;; Continued string ? (Added 890113 lbn) (and (looking-at "\\\\") (or (save-excursion (forward-line -1) (if (looking-at "[\t ]*\\\\") (current-indentation))) (save-excursion (if (re-search-backward "[^\\\\]\"" nil t) (1+ (current-column)) 0)))) ;; Closing parens. Could be handled below with `sml-indent-relative'? (and (looking-at "\\s)") (save-excursion (skip-syntax-forward ")") (backward-sexp 1) (if (sml-dangling-sym) (sml-indent-default 'noindent) (current-column)))) (and (setq data (assoc sym sml-close-paren)) (sml-indent-relative sym data)) (and (member sym sml-starters-syms) (sml-indent-starter sym)) (and (string= sym "|") (sml-indent-pipe)) (sml-indent-arg) (sml-indent-default)))))) (defsubst sml-bolp () (save-excursion (skip-chars-backward " \t|") (bolp))) (defun sml-first-starter-p () "Non-nil if starter at point is immediately preceded by let/local/in/..." (save-excursion (let ((sym (unless (save-excursion (sml-backward-arg)) (sml-backward-spaces) (sml-backward-sym)))) (if (member sym '(";" "d=")) (setq sym nil)) sym))) (defun sml-indent-starter (orig-sym) "Return the indentation to use for a symbol in `sml-starters-syms'. Point should be just before the symbol ORIG-SYM and is not preserved." (let ((sym (unless (save-excursion (sml-backward-arg)) (sml-backward-spaces) (sml-backward-sym)))) (if (member sym '(";" "d=")) (setq sym nil)) (if sym (sml-get-sym-indent sym) ;; FIXME: this can take a *long* time !! (setq sym (sml-old-find-matching-starter sml-starters-syms)) (if (or (sml-first-starter-p) ;; Don't align with `and' because it might be specially indented. (and (or (equal orig-sym "and") (not (equal sym "and"))) (sml-bolp))) (+ (current-column) (if (and sml-rightalign-and (equal orig-sym "and")) (- (length sym) 3) 0)) (sml-indent-starter orig-sym))))) (defun sml-indent-relative (sym data) (save-excursion (sml-forward-sym) (sml-backward-sexp nil) (unless (second data) (sml-backward-spaces) (sml-backward-sym)) (+ (or (cdr (assoc sym sml-symbol-indent)) 0) (sml-delegated-indent)))) (defun sml-indent-pipe () (let ((sym (sml-old-find-matching-starter sml-pipeheads (sml-op-prec "|" 'back)))) (when sym (if (string= sym "|") (if (sml-bolp) (current-column) (sml-indent-pipe)) (let ((pipe-indent (or (cdr (assoc "|" sml-symbol-indent)) -2))) (when (or (member sym '("datatype" "abstype")) (and (equal sym "and") (save-excursion (forward-word 1) (not (sml-funname-of-and))))) (re-search-forward "=")) (sml-forward-sym) (sml-forward-spaces) (+ pipe-indent (current-column))))))) (defun sml-indent-arg () (and (save-excursion (ignore-errors (sml-forward-arg))) ;;(not (looking-at sml-not-arg-re)) ;; looks like a function or an argument (sml-move-if (sml-backward-arg)) ;; an argument (if (save-excursion (not (sml-backward-arg))) ;; a first argument (+ (current-column) sml-indent-args) ;; not a first arg (while (and (/= (current-column) (current-indentation)) (sml-move-if (sml-backward-arg)))) (unless (save-excursion (sml-backward-arg)) ;; all earlier args are on the same line (sml-forward-arg) (sml-forward-spaces)) (current-column)))) (defun sml-get-indent (data sym) (let (d) (cond ((not (listp data)) data) ((setq d (member sym data)) (cadr d)) ((and (consp data) (not (stringp (car data)))) (car data)) (t sml-indent-level)))) (defun sml-dangling-sym () "Non-nil if the symbol after point is dangling. The symbol can be an SML symbol or an open-paren. \"Dangling\" means that it is not on its own line but is the last element on that line." (save-excursion (and (not (sml-bolp)) (< (sml-point-after (end-of-line)) (sml-point-after (or (sml-forward-sym) (skip-syntax-forward "(")) (sml-forward-spaces)))))) (defun sml-delegated-indent () (if (sml-dangling-sym) (sml-indent-default 'noindent) (sml-move-if (backward-word 1) (looking-at sml-agglomerate-re)) (current-column))) (defun sml-get-sym-indent (sym &optional style) "Find the indentation for the SYM we're `looking-at'. If indentation is delegated, point will move to the start of the parent. Optional argument STYLE is currently ignored." (assert (equal sym (save-excursion (sml-forward-sym)))) (save-excursion (let ((delegate (and (not (equal sym "end")) (assoc sym sml-close-paren))) (head-sym sym)) (when (and delegate (not (eval (third delegate)))) ;;(sml-find-match-backward sym delegate) (sml-forward-sym) (sml-backward-sexp nil) (setq head-sym (if (second delegate) (save-excursion (sml-forward-sym)) (sml-backward-spaces) (sml-backward-sym)))) (let ((idata (assoc head-sym sml-indent-rule))) (when idata ;;(if (or style (not delegate)) ;; normal indentation (let ((indent (sml-get-indent (cdr idata) sym))) (when indent (+ (sml-delegated-indent) indent))) ;; delgate indentation to the parent ;;(sml-forward-sym) (sml-backward-sexp nil) ;;(let* ((parent-sym (save-excursion (sml-forward-sym))) ;; (parent-indent (cdr (assoc parent-sym sml-indent-starters)))) ;; check the special rules ;;(+ (sml-delegated-indent) ;; (or (sml-get-indent (cdr indent-data) 1 'strict) ;; (sml-get-indent (cdr parent-indent) 1 'strict) ;; (sml-get-indent (cdr indent-data) 0) ;; (sml-get-indent (cdr parent-indent) 0)))))))) ))))) (defun sml-indent-default (&optional noindent) (let* ((sym-after (save-excursion (sml-forward-sym))) (_ (sml-backward-spaces)) (sym-before (sml-backward-sym)) (sym-indent (and sym-before (sml-get-sym-indent sym-before))) (indent-after (or (cdr (assoc sym-after sml-symbol-indent)) 0))) (when (equal sym-before "end") ;; I don't understand what's really happening here, but when ;; it's `end' clearly, we need to do something special. (forward-word 1) (setq sym-before nil sym-indent nil)) (cond (sym-indent ;; the previous sym is an indentation introducer: follow the rule (if noindent ;;(current-column) sym-indent (+ sym-indent indent-after))) ;; If we're just after a hanging open paren. ((and (eq (char-syntax (preceding-char)) ?\() (save-excursion (backward-char) (sml-dangling-sym))) (backward-char) (sml-indent-default)) (t ;; default-default (let* ((prec-after (sml-op-prec sym-after 'back)) (prec (or (sml-op-prec sym-before 'back) prec-after 100))) ;; go back until you hit a symbol that has a lower prec than the ;; "current one", or until you backed over a sym that has the same prec ;; but is at the beginning of a line. (while (and (not (sml-bolp)) (while (sml-move-if (sml-backward-sexp (1- prec)))) (not (sml-bolp))) (while (sml-move-if (sml-backward-sexp prec)))) (if noindent ;; the `noindent' case does back over an introductory symbol ;; such as `fun', ... (progn (sml-move-if (sml-backward-spaces) (member (sml-backward-sym) sml-starters-syms)) (current-column)) ;; Use `indent-after' for cases such as when , or ; should be ;; outdented so that their following terms are aligned. (+ (if (progn (if (equal sym-after ";") (sml-move-if (sml-backward-spaces) (member (sml-backward-sym) sml-starters-syms))) (and sym-after (not (looking-at sym-after)))) indent-after 0) (current-column)))))))) ;; maybe `|' should be set to word-syntax in our temp syntax table ? (defun sml-current-indentation () (save-excursion (beginning-of-line) (skip-chars-forward " \t|") (current-column))) (defun sml-old-find-matching-starter (syms &optional prec) (let (sym) (ignore-errors (while (progn (sml-backward-sexp prec) (setq sym (save-excursion (sml-forward-sym))) (not (or (member sym syms) (bobp))))) (if (member sym syms) sym)))) (defun sml-old-skip-siblings () (while (and (not (bobp)) (sml-backward-arg)) (sml-old-find-matching-starter sml-starters-syms)) (when (looking-at "in\\>\\|local\\>") ;; Skip over `local...in' and continue. (forward-word 1) (sml-backward-sexp nil) (sml-old-skip-siblings))) (provide 'sml-oldindent) ;;; sml-oldindent.el ends here sml-mode-5.0/NEWS0000644005056000007300000001232511741417724014510 0ustar monnierparallelismeChanges since 4.1: * New indentation code using SMIE when available. * `sml-back-to-outer-indent' is now on S-tab (aka `backtab') rather than M-tab. * Support for electric-layout-mode and electric-indent-mode. * `sml-mark-defun' tries to be more clever. * A single file (sml-mode.el) is needed unless you want to use an interactive process like SML/NJ, or if your Emacs does not provide SMIE. Changes since 4.0: * Switch to GPLv3+. * When possible (i.e. running under Emacs>=23), be case-sensitive when expanding abbreviations, and don't expand them in comments and strings. * When you `next-error' to a type error, highlight the actual parts of the types that differ. * Flush the recorded errors not only upon sml-compile and friends, but also when typing commands directly at the prompt. * New command sml-mlton-typecheck. * Simple support to parse errors and warnings in MLton's output. * Simple support for MLton's def-use files. Changes since 3.9.5: * No need to add the dir to your load-path any more. The sml-mode-startup.el file does it for you. * Symbols like -> can be displayed as real arrows. See sml-font-lock-symbols. * Fix some incompatibilities with the upcoming Emacs-21.4. * Indentation rules improved. New customizable variable `sml-rightalign-and'. Also `sml-symbol-indent' is now customizable. Changes since 3.9.3: * New add-log support (try C-x 4 a from within an SML function). * Imenu support * sml-bindings has disappeared. * The code skeletons are now abbrevs as well. * A new *sml* process is sent the content of sml-config-file (~/.sml-proc.sml) if it exists. * `sml-compile' works yet a bit differently. The command can begin with `cd "path";' and it will be replaced by OS.FileSys.chDir. * run-sml now pops up the new buffer. It can also run the command on another machine. And it always prompts for the command name. Use a prefix argument if you want to give args or to specify a host on which to run the command. * mouse-2 to yank in *sml* should work again (but won't work for next-error any more). * New major-modes sml-cm-mode, sml-lex-mode and sml-yacc-mode. * sml-load-hook has disappeared as has inferior-sml-load-hook. * sml-mode-startup.el is now automatically generated and you're supposed to `load' it from .emacs or site-start.el. * Minor bug fixes. Changes since 3.3: * the sml-drag-* commands have disappeared. * added a little bit of `customize' support. Many of the customization variables for indentation are still in flux, so they are not customize'd. * proformas have been replaced by skeletons. it's mostly the same as before (the layout has slightly changed, tho). The main difference is that the indentation relies on the major-mode indentation so it is implicitly customized, which makes more sense to me. Also I added an electric space M-SPC that will call the corresponding skeleton if any matches the immediately preceding symbol. Basically that allows you to type `l e t M-SPC' to call the `let' skeleton. * M-C-f and M-C-b try to be smart and jump around let..end and such blocks. It's probably either too smart or not smart enough, tho. * there is no more sml-.el since the code should work for "all" known compilers. If your favorite compiler doesn't seem to work right send me a sample session. * hilite support has disappeared and font-lock and menu support is now built-in. * the indentation algorithm is inherently much slower. I've tried to ensure the slowness never manifests itself in practice, but if you find a case where the indentation doesn't feel instantaneous, tell me. * function arguments get properly indented (yes, madam). * the indentation has been majorly reworked. The list of changes is too long. Many customizations have disappeared, some may reappear depending on the feedback I get. The indentation should now "always" work right, so tell me when it doesn't. * nested comments are only properly handled if you have a nested-comments aware Emacs (I don't know of any yet) or if you turn on font-lock. * provide `sml-compile' which does something similat to `compile' except it passes the command to an inferior-sml process. Also it has an additional hack to look for sml-make-file-name in parent directories and cd to it before sending the command (handy for CM.make() when the sources.cm file is not in the current directory). This hack is very ad-hoc and quite misleading for people who don't use CM. I.e. the default is not safe. * sml-send-region and friends now always use a temp file. The temp file management has been made a little more secure. * the overlay is now turned off by default. Instead the region is activated, so that transient-mark-mode will end up highlighting the error just like the overlay used to do. * sml-proc uses compile.el for error parsing. This mostly means that instead of C-c ` you want to use the standard C-x `. It also means that error formats for any compiler can be added more easily. * The special frame handling has been thrown out because it doesn't interact well with Emacs' own similar feature. I believe XEmacs still doesn't provide such a feature, so if you miss it, either switch to Emacs or (convince someone else to) add it to XEmacs. sml-mode-5.0/TODO0000644005056000007300000000220111741417724014471 0ustar monnierparallelisme* file-name completion in sml-cm-mode. * Don't always jump to the *sml* buffer when you send a snippet of code. * Fix inferior-sml-mode's TAB completion of filenames so it doesn't append a space. * Improve support for MLton's def-use info (see http://mlton.org/Emacs) * Add an sml-mlb-mode for ML Basis files (see http://mlton.org/Emacs) * make `M-x sml-compile' more generic. * allow specifying indentation of dependent keywords (how to indent `in' relative to `let', for example). * recognize irrefutable patterns (with "Capital"-heuristics, for example: a regexp like "\\([(),]\\|[_a-z][_a-z0-9]*\\)+"). This can then be used to allow indenting like (fn x => some expressions) * take advantage of text after-the-line (when available) for indentation. * obey fixity directives. * dangling `case e' in stuff like fun myfunction x = case x of bla => | bli => * deal with CPS kind of code ??? function1 (arg1, arg2, fn v1 => function2 (arg2, fn v2 => function3 (arg5, arg3, arg8, fn v3 => function4 (v1, v2, v3)))) or even just F.LET (v1, foo, F.LET (v2, bar, F.LET (v3, baz, F.RET [v1, v2, v3]))) sml-mode-5.0/makefile.pkg0000644005056000007300000000062711741417724016273 0ustar monnierparallelismePACKAGE = sml-mode ELFILES = sml-mode.el sml-proc.el sml-oldindent.el default: elcfiles TESTCASE = testcases.sml test: $(RM) $(TESTCASE).new $(EMACS) --batch \ --eval "(load \"$$(pwd)/sml-mode-startup\")" \ $(TESTCASE) \ --eval '(indent-region (point-min) (point-max) nil)' \ --eval '(write-region (point-min) (point-max) "$(TESTCASE).new")' diff -u -B $(TESTCASE) $(TESTCASE).new sml-mode-5.0/ChangeLog0000644005056000007300000005267011741417724015572 0ustar monnierparallelisme2012-04-11 Stefan Monnier Merge sml-defs.el into sml-mode.el. * sml-mode.el: Merge code from sml-defs.el. Remove ":group 'sml" since they're now redundant. * makefile.pkg (ELFILES): Adjust. 2012-04-11 Stefan Monnier * sml-mode.el (sml-mark-function): New implementation using SMIE. * sml-defs.el (sml-mode-map): Use backtab. Remove leftover unused sml-drag-region binding. 2012-04-11 Stefan Monnier Use SMIE by default and make sml-oldindent optional. * sml-mode.el: Only load sml-oldindent if necessary. (sml-use-smie): Default to t. (sml-smie-datatype-|-p): Better handle incomplete datatype branch. (sml-mode): Use prog-mode. Setup electric-layout and electric-indent. (sml-mode-variables): Always setup SMIE if possible. (sml-imenu-create-index, sml-funname-of-and, sml-electric-pipe) (sml-beginning-of-defun, sml-defuse-symdata-at-point) (sml-yacc-font-lock-keywords, sml-yacc-indentation): Avoid sml-oldindent functions. (sml-find-forward): Move from sml-oldindent and re-implement. (sml-electric-semi): Use self-insert-command so electric-layout and electric-indent can do their job. (sml-smie-find-matching-starter, sml-find-matching-starter) (sml-smie-skip-siblings, sml-skip-siblings): New functions. * sml-oldindent.el (sml-starters-indent-after, sml-exptrail-syms): Remove, unused. (sml-find-forward): Move back to sml-mode.el. (sml-old-find-matching-starter): Rename from sml-find-matching-starter. (sml-old-skip-siblings): Move&rename from sml-mode:sml-skip-siblings. 2012-04-11 Stefan Monnier Move non-SMIE indentation code to a separate file. * sml-oldindent.el: Rename from sml-move.el. * makefile.pkg (ELFILES): Adjust. * sml-mode.el (sml-indent-line, sml-find-comment-indent) (sml-calculate-indentation, sml-bolp, sml-first-starter-p) (sml-indent-starter, sml-indent-relative, sml-indent-pipe) (sml-find-forward, sml-indent-arg, sml-get-indent, sml-dangling-sym) (sml-delegated-indent, sml-get-sym-indent, sml-indent-default) (sml-current-indentation, sml-find-matching-starter): Move to sml-oldindent.el. (comment-quote-nested, compilation-error-regexp-alist): Declare. * sml-defs.el (sml-begin-syms, sml-begin-syms-re) (sml-sexp-head-symbols-re, sml-preproc-alist, sml-indent-rule) (sml-starters-indent-after, sml-delegate, sml-symbol-indent) (sml-open-paren, sml-close-paren, sml-agglomerate-re) (sml-exptrail-syms): Move to sml-oldindent.el. 2012-04-11 Stefan Monnier Get rid of ancient compatibility and small utility file. * sml-proc.el (inferior-sml-mode-map): Don't use defmap. * sml-move.el (sml-internal-syntax-table): Don't use defsyntax. * sml-mode.el (sml-syntax-prop-table): Don't use defsyntax. (sml-electric-space): `last-command-char' -> `last-command-event'. (sml-defuse-jump-to-def): Don't use goto-line from Elisp. * sml-defs.el (sml-mode-map): Don't use defmap. (sml-mode-syntax-table): Don't use defsyntax. (sml-preproc-alist, sml-builtin-nested-comments-flag): Move from sml-util.el. * sml-compat.el, sml-utils.el: Remove. * makefile.pkg (ELFILES): Update. 2012-04-11 Stefan Monnier Add SMIE support. * .bzrignore: New file. * makefile.pkg (test): Use sml-mode-startup. * sml-mode.el (sml-use-smie): New config var. (sml-smie-grammar, sml-indent-separator-outdent): New vars. (sml-smie-rules, sml-smie-definitional-equal-p) (sml-smie-non-nested-of-p, sml-smie-datatype-|-p) (sml-smie-forward-token-1, sml-smie-forward-token) (sml-smie-backward-token-1, sml-smie-backward-token): New functions. (sml-mode): Don't set forward-sexp-function. (sml-mode-variables): Set it here instead, and setup SMIE instead if applicable. 2010-03-04 Stefan Monnier * Release version 4.1. * sml-mode.el: Don't setup load-path here any more. * Makefile ($(PACKAGE)-startup.el): Recreate from scratch every time. Setup load-path. (dist): Try to update it to use Svn rather than CVS. 2007-11-08 Stefan Monnier * sml-proc.el (inferior-sml-next-error-hook): Make it match one more format. 2007-10-31 Stefan Monnier * testcases.sml: Add (old) buggy case from Christopher Dutchyn. * sml-proc.el (inferior-sml-font-lock-keywords): Don't add error-regexps if compile.el already highlights them anyway. * sml-mode.el (sml-font-lock-symbols-alist): Fix char for "not". 2007-10-31 Stefan Monnier * sml-mode.el (sml-def-skeleton): If possible, only expand for lower-case abbrevs and not inside strings or comments. (sml-mlton-typecheck): Typo. * sml-proc.el (sml-proc-comint-input-filter-function): New function. (inferior-sml-mode): Use it. (sml-update-cursor): Don't forget errors here any more. 2007-10-31 Stefan Monnier * sml-util.el (defmap, defsyntax): Avoid defconst. (flatten): Remove. * sml-mode.el (sml-calculate-indentation): Avoid previous-line. (sml-keywords-regexp): Avoid the need for `flatten'. (sml-defuse-jump-to-def): Fix typo. * sml-defs.el (sml-syms-re): Don't use `flatten'. (sml-sexp-head-symbols-re, sml-starters-indent-after) (sml-non-nested-of-starter-re): Avoid the need for `flatten'. * sml-proc.el (inferior-sml-next-error-hook): New fun. (inferior-sml-mode): Use it. 2007-06-29 Stefan Monnier * sml-mode.el (sml-mlton-error-regexp-alist): New var. (sml-mlton-typecheck): Use it. (compilation-error-regexp-alist): Add entries after loading "compile" so that M-x compile works. * sml-proc.el (inferior-sml-mode): Make TAB add a " rather than a space at the end of a file name. 2007-06-14 Stefan Monnier * sml-mode.el (sml-mode-variables): Set comment-quote-nested instead of comment-nested. Set comment-end-skip. (sml-first-starter-p): New function. (sml-indent-starter): Use it to fix an indentation bug. (sml-mlton-command, sml-mlton-mainfile): New vars. (sml-mlton-typecheck): New command. (sml-defuse-file): New var. (sml-defuse-def-regexp, sml-defuse-use-regexp-format): New consts. (sml-defuse-file, sml-defuse-symdata-at-point): New functions. (sml-defuse-set-file, sml-defuse-jump-to-def): New commands. 2005-11-20 Stefan Monnier * sml-move.el (sml-find-match-forward): Avoid infinite looping if the construct is not properly closed (yet). 2005-11-16 Stefan Monnier * sml-defs.el (sml-mode-menu): Remove left over obsolete entries. * sml-proc.el (inferior-sml-mode): Preserve the menu-bar of the compilation minor mode, if any. 2004-11-24 Stefan Monnier * Release version 4.0. 2004-11-23 Stefan Monnier * sml-mode.el (sml-font-lock-symbols-alist): Add yet more silly entries. 2004-11-15 Stefan Monnier * sml-mode.el (sml-font-lock-symbols-alist): Add entries for >= and <=. (sml-font-lock-symbols-alist): Add entries for simple type variables. (sml-font-lock-compose-symbol): Add support for non-punctuation syms. 2004-11-14 Stefan Monnier * sml-move.el (sml-backward-sexp, sml-forward-sexp): Remove unused var. * sml-mode.el (sml-calculate-indentation): Remove unused var. (sml-get-indent): Change first arg's meaning. (sml-get-sym-indent): Adjust call. (sml-forms-menu): Simplify. (sml-font-lock-symbols, sml-font-lock-symbols-alist): New vars. (sml-font-lock-compose-symbol, sml-font-lock-symbols-keywords): New funs. (sml-font-lock-keywords): Use them. * sml-compat.el (temp-directory): Get rid of warning. * Makefile (install_startup): Don't add to load-path any more. * sml-proc.el (sml-make-error): Use match-string. (sml-error-regexp-alist): Merge regexps. (sml-update-cursor): Check sml-temp-file is non-nil. 2004-04-21 Stefan Monnier * sml-proc.el (sml-error-regexp-alist): Use new compile.el features if available. (sml-send-region): Remove unused var `file'. (sml-drag-region): Remove unused function. (sml-update-cursor): Use compilation-fake-loc if available. (compilation-goto-locus): Only advise if necessary. * sml-mode.el: Add self to load-path in sml-mode-startup.el. (sml-def-skeleton): Try to use the new `system' abbrev flag. 2004-04-04 Stefan Monnier * testcases.sml: Add a nasty case that is still wrong. * sml-proc.el (sml-error-regexp-alist): Tune the regexp for sml/nj. * sml-mode.el (sml-mode): Better handle paragraphs in comments. (sml-mode-variables): Clean up paragraph settings. (sml-electric-pipe): Fix a boundary bug. (sml-indent-starter, sml-get-sym-indent, sml-find-matching-starter): Fix indentation algorithm. Can't remember what it was about. This code is nasty, I just can't understand what's doing what. (completion-ignored-extensions): Add the new .cm directory. 2003-12-09 Stefan Monnier * sml-defs.el (sml-mode-menu): Fix typo. Use std names. (sml-begin-syms-re): Reduce redundancy. 2001-09-18 Stefan Monnier * sml-mode.el (sml-tyvarseq-re): Fix typo. 2001-07-20 Stefan Monnier * sml-mode.el (sml-rightalign-and): New defcustom. (sml-tyvarseq-re): New var. (sml-font-lock-keywords): Use it. (sml-imenu-create-index): Don't get confused by tyvarseq's. (sml-mode-variables): Don't set `comment-column'. (sml-funname-of-and): New function. (sml-electric-pipe): Use it. (sml-find-comment-indent): Try to ignore comment-markers in strings. (sml-calculate-indentation): Handle closing parens specially. (sml-indent-pipe): Recognize the case where `and' defines a datatype. (sml-dangling-sym): Make it work if the symbol is an open-paren. (sml-indent-default): Change the behavior when preceded by `end', although I'm not quite sure why. Understand dangling open-parens. Properly skip *all* subexpressions of lower precedence. Allow use of sml-symbol-indent to outdent lines starting with , or ;. (sml-insert-form): Use preceding-char to avoid bug at bobp. 2001-07-19 Stefan Monnier * sml-proc.el (sml-proc-buffer): Save excursion when calling run-sml. * sml-move.el (sml-syntax-prec): Split ; and , from `in' and `with'. * sml-mode.texi: Put the entry in `Emacs' rather than `Editors'. * sml-mode.spec (BuildArch): Simplify call to `install-info'. * sml-defs.el (sml-mode-menu): Add an explicit t for always-active. (sml-symbol-indent): Add entries for , and ; and turn into defcustom. * sml-compat.el: Add more stuff. It might help for Emacs-19.34. * makefile.pkg (test): Use elisp files in current dir. 2000-12-24 Stefan Monnier * Release version 3.9.5. * Makefile (install): Also install .el files. (dist): Don't rely on $CVSROOT. * sml-mode.el: Require `skeleton'. (sml-mode): Add the menu for XEmacs. Make sure @ is a valid skeleton. (sml-comment-indent): Remove. (sml-mode-variables): Don't set comment-indent-function. (sml-def-skeleton): Nop if skeletons aren't available. (skeletons): Use `> _' and `@'. (sml-forms-menu): Don't bother with easy-menu-filter-return crap. (sml-cm-mode-syntax-table, sml-cm-font-lock-keywords): New vars. (sml-cm-mode): Use define-derived-mode rather than define-generic-mode. (sml-lex-font-lock-keywords, sml-lex-font-lock-defaults): New vars. (sml-yacc-font-lock-keywords): Use sml-lex-font-lock-keywords. Refine pattern to recognize the %prec keyword. (sml-yacc-font-lock-defaults): Fix typo. * sml-proc.el (inferior-sml-mode): Disable next-error for XEmacs. * sml-util.el (defsyntax): Don't forget to eval `doc'. * sml-mode.spec: Simplify. * sml-defs.el (sml-mode-menu): Remove bogus entry for sml-mode-version. 2000-10-06 Stefan Monnier * sml-mode.el: Make the toplevel closer to usual practice. (sml-imenu-regexp, sml-imenu-create-index): New var and fun. (sml-mode): Use them. (sml-beginning-of-defun): Add `and' as function-leader. (sml-lex-mode): New trivial mode. (sml-yacc-bnf-face, sml-yacc-indent-action, sml-yacc-indent-pipe) (sml-yacc-indent-term, sml-yacc-font-lock-keywords) (sml-yacc-font-lock-defaults): New vars. (sml-yacc-indent-line, sml-yacc-indentation, sml-yacc-mode): New funs. * sml-mode.texi: Added yours truly to the list of authors. * sml-mode.spec: New file. * sml-defs.el (sml-outline-regexp): Slightly improved regexp. 2000-08-24 Stefan Monnier * sml-proc.el (inferior-sml-mode-map): Don't inherit from sml-bindings. Add the binding for C-c C-l explicitly instead. (sml-run): Look in cwd (but only if the command has slashes). * sml-mode.el (sml-mode-abbrev-table): Remove (created by define-derived-mode). (sml-mode): Setup add-log's current-defun-function. (sml-indent-line): Never indent to a negative level. (sml-skip-siblings, sml-beginning-of-defun, sml-max-name-components) (sml-current-fun-name): New funs and vars for add-log support. (sml-comment-indent): Simplify. (sml-def-skeleton): Also create the skeleton as an abbrev. (skeletons): New for "struct", "sig", "val", "fn" and "fun". (sml-electric-space): Rewrite to use abbrev's machinery. * sml-defs.el (sml-mode-map): Merge with sml-bindings. (sml-bindings): Remove. 2000-02-22 Stefan Monnier * sml-mode.el (sml-find-matching-starter): Use syms instead of a RE. (sml-indent-default): Use symbol membership rather than a regexp. Also, use `sym-indent' instead of (current-column). This fixes a problem with a hanging `structure Foo = (struct|let)' (due to `structure' having a sml-indent-rule, as opposed to `fun'). Hopefully it won't introduce other problems. (sml-font-lock-keywords): Match vars `val x : int' also. (sml-electric-pipe): Update to the new `sml-find-matching-starter' and return a sensible default instead of raising an error in case of unexpected situations. (sml-indent-line): Ignore errors and keep the cursor where it is. (sml-calculate-indentation, sml-indent-pipe): Use syms instead of REs. * sml-defs.el (sml-starters-re, sml-pipehead-re): Remove. * testcases.sml: New file. * makefile.pkg (test): New target to run the test suite. 2000-02-18 Stefan Monnier * *.el: Pass through checkdoc and use `eval-when-compile' whenever possible for (require 'cl). 2000-02-18 Stefan Monnier * sml-util.el (make-temp-dir, make-temp-file, temp-file-dir) (delete-temp-dirs): Replace by the make-temp-file from Emacs-21. (custom-create-map): Add :group arg and allow key to be a list. (define-major-mode): Remove (use define-derived-mode instead). (sml-builtin-nested-comments-flag): New var. (concatq): Remove. * sml-proc.el (sml-host-name): New var. (sml-make-file-name): Replace by `sml-compile-commands'. (sml-config-file): New var. (sml-compile-commands-alist): New var. (inferior-sml-load-hook): Remove. (sml-buffer): Query if the current buffer is not a *sml*. (inferior-sml-mode): Use minor-mode-overriding-map-alist to disable compilation-minor-mode's keybindings. (run-sml): Turn into an alias for sml-run. (sml-run): Query the user for the command. If prefix is set (or if default value is not null) query for args and host. Use `split-string' rather than our own function. Run cmd on another host if requested and pass it an init file. Pop to the buffer at the end. (sml-args-to-list): Remove. (sml-compile): Look for special files (sml-compile-command-alist) in the current dir (and its parents) to choose a default command. Remember the command for next time in the same buffer. Make the `cd' explicit in the command so the user can change it. (sml-make-error): Fix for when `endline' is absent. * sml-mode.el: Pass it through checkdoc. (sml-mode-version): Remove. (sml-load-hook): Remove. (sml-mode-info): Use `info' rather than `Info-goto-node'. (sml-keywords-regexp): Add "o". (sml-syntax-prop-table): Use `defsyntax'. (sml-font-lock-syntactic-keywords): Only use nested comments if supported. (sml-mode): Use `define-derived-mode'. (sml-electric-pipe): `sml-indent-line' -> `indent-according-to-mode'. (sml-indent-line): Use `indent-line-to'. (sml-cm-mode): New mode for CM files. * Makefile: Update. * sml-mode-startup.el: Remove since it's now auto-generated. * sml-defs.el (sml-bindings): Remove left over C-c` binding. (sml-mode-map): Add binding for sml-drag-region (was in sml-proc.el). (sml-mode-syntax-table): Only use nested comments if supported. (sml-mode-menu): Use next-error rather than the old sml-next-error. (sml-pipehead-re): Remove "of". * sml-compat.el (set-keymap-parents): Make sure it also works when called with a single keymap rather than a list. (temporary-file-directory): Add a default definition for XEmacs. (make-temp-file): New function. 1999-08-11 Stefan Monnier * Release version 3.9.3. * sml-mode.texi: Somewhat update the doc. 1999-08-09 Stefan Monnier * Makefile: Update to the version of pcl-cvs. * sml-proc.el: Eliminate some old unused code. * sml-defs.el,sml-mode.el,sml-proc.el: Add simple customize support. 1999-07-07 Stefan Monnier * sml-proc.el (sml-update-cursor): Make sure it also works if compile.el is fixed to uses a marker. * sml-mode.el (sml-indent): Fix the `fixindent'. 1999-06-22 Stefan Monnier * sml-mode-startup.el: Fix to fulfill autoload.el assumptions. 1999-06-21 Stefan Monnier * sml-defs.el (sml-bindings): Remove bindings for TAB and M-C-\. 1999-06-19 Stefan Monnier * sml-mode.el (sml-font-lock-keywords): Skip type vars in "fun 'a myfn" (sml-calculate-indentation): Add a hack to allow the user to manually override the indentation algorithm with a magic comment. * sml-mode-startup.el: Update the autoloads automatically. 1999-06-19 Stefan Monnier * Release version 3.9.2 * sml-proc.el (sml-error-regexp-alist): Fix the pathological font-locking on long lines. * sml-move.el (sml-forward-sexp): Slightly improved. 1999-06-17 Stefan Monnier * sml-mode.el (sml-insert-form): Only add a space if needed. (sml-electric-space): New command bound to M-SPC. * sml-defs.el (sml-close-paren): Add a second field that specifies when not to delegate. Only used for `else'. 1999-06-16 Stefan Monnier * sml-move.el (sml-(for|back)ward-sym): Distinguish between operator "=" and syntax for definitions "d=". * sml-defs.el (sml-indent-starters, sml-delegate): Simplify. (sml-symbol-indent): Add outdentation for `fn' and generalize it to also work for `of' and `in' and `end'. * sml-mode.el (sml-nested-if-indent): Reintroduce as well as the special casing code for it. (sml-indent-relative): Generalize treatment of `of', `in', `end', ... (sml-electric-pipe): Remove the slow behavior and add smarts for the never-used type-variable arguments for function definitions. 1999-06-15 Stefan Monnier * sml-defs.el (sml-mode-menu), sml-mode.el (sml-forms-menu): Make the menu dynamically. * sml-mode.el (sml-form-): Use skeletons. (sml-calculate-indentation): Add `with' indentation. 1999-06-14 Stefan Monnier * sml-move.el (sml-(for|back)ward-sym): Now also return the string if any and take care of the "op" special keyword. (sml-op-prec): Setup an alist for the infix operators. * version 3.9.1: sent to Roland McGrath. 1999-06-13 Stefan Monnier * sml-smlnj.el, sml-mosml.el, sml-poly-ml.el: Remove. * sml-proc.el (...): Get rid of sml-next-error by spicing up the interface with compile.el so that intervals can be displayed. `sml-overlay' is kept (and moved from sml-mode to sml-proc where it belongs) but is made redundant in the case of transient-mark-mode. 1999-06-12 Stefan Monnier * sml-proc.el (sml-prompt-regexp): More general regexp to catch mosml, smlnj as well as polyml prompts. (sml-update-cursor, sml-send-command, inferior-sml-mode): Make it work with compile.el's `next-error'. (sml-temp-threshold): Drop: always use a temp file. 1999-06-10 Stefan Monnier * sml-move.el (sml-op-prec): Update the list of default infix ops based on sml/nj's source files. 1999-06-08 Stefan Monnier * sml-proc.el (sml-run): Remove dubious code to take care of a supposedly special case in order not to send "" when args=nil. 1999-06-07 Stefan Monnier * sml-mode.el (sml-font-lock-syntactic-keywords): Add syntactic fontification for the ' \"' case (exhibited by lexgen.sml). 1999-06-07 Stefan Monnier * ALL: The new indentation begins to work. v3_9_0 1999-05-29 Stefan Monnier * sml-defs.el (sml-mode-syntax-table): Add ~ of prefix-syntax. * sml-mode.el (sml-find-match-indent): (nilp sml-type-of-indent) is only applied if the `let' is alone at the end of the line. (sml-type-of-indent): Default changed to `nil'. 1999-05-28 Stefan Monnier * sml-mode.el (sml-font-lock-keywords): Change _ and ' back to word syntax for font-locking. 1999-05-27 Stefan Monnier * sml-mode.el (sml-font-lock-syntactic-keywords): Finally got the matching of let...end working. (sml-electric-pipe): Take a fun sexp (symbol) rather than a fun word. 1998-10-26 Stefan Monnier * sml-mode.el (sml-font-lock-syntactic-keywords): Add syntactic-keywords to support nested comments.