% foo.sl: How to write a Jed mode
% ===============================
% 
% A mode to place "foo" at the beginning of each line and highlight it.\ [#]_
% 
% Copyright (c) 2007 Günter Milde  (milde users.sf.net)
% Released under the terms of the GNU General Public License (ver. 2 or later)
% 
% .. contents:: 
% 
% .. [#] Actually, this is a manual and template for the creation of Jed
%        extension scripts ("modes"). It assumes you to have basic knowledge
%        of S-Lang_ and Jed_ and know how to use its help system (Using the
%        `help browser`_ from Jedmodes is recommended.)
% 
% Support loading this mode with ``require("foo");``::

provide("foo");
 
% Usage
% -----
% To find out what the foo mode does, try::

M-x evalbuffer 
M-x foo-mode

% and have a look at the **Mode** menu.
% 
% Customisation
% -------------
% You can use the ``foo_mode_hook`` to define your own shortcuts, e.g. ::
 
define foo_mode_hook(mode)
{
  local_setkey("start_line_with_foo(\"bar \")", "^A");
}

% Also, have a look at the `Custom variables`_.
% 
% Versions
% --------
% 
% | 1.0            first draft
% | 1.2 2006-01-10 adapted to SLang 2
% | 1.3 2007-10-21 literate version
% 
% 
% Requirements
% ------------
% 
% from Jed's standard library::

require("comments");  
require("keydefs"); % symbolic names for keys

% from http://jedmodes.sf.net/::

autoload("close_buffer", "bufutils");

% Recommendations
% ---------------
% (from http://jedmodes.sf.net/)
% Browse documentation::

#if (expand_jedlib_file("browse_url.sl") != "")
autoload("browse_url", "browse_url");
#endif

% Custom variables
% ----------------
% 
% Use ``custom_variable()`` to provide user-configurable options.
% 
% These variables can be defined and initialized by users but will be set 
% to the default value if they do not exist at evaluation time. ::

%!%+
%\variable{Foo_Insertion}
%\synopsis{String to insert with \var{start_line_with_foo}}
%\usage{String_Type Foo_Insertion = "foo "}
%\description
%  The string that will be inserted by the function 
%  \sfun{start_line_with_foo}.
%\seealso{foo_mode, start_line_with_foo}
%!%-
custom_variable("Foo_Insertion", "foo ");           

% Namespace
% ---------
%   
% The ``implements()`` function may be used to name the local namespace. Doing
% so will enable access to the members of the namespace from outside the unit::

implements("foo");

% .. note:: 
%   The default scope of definitions changes:
% 
%   ==================  ======================  ===================
%   Definition          without ``implements``  with ``implements``
%   ==================  ======================  ===================
%   private define fun    private               private
%   static define fun     static                static
%   define fun            public                static
%   public define fun     public                public
%   ==================  ======================  ===================
% 
%   make_ini_ can generate autoloads for all functions that 
%   are defined with ``define public``. To put a function in the "Global"
%   namespace but prevent generation of an autoload, use e.g. 
%   ``define  public`` (with 2 spaces).
% 
% 
%   
% Static variables
% ----------------
% 
% Define quasi-constants or variables for inter-function communication as
% ``private`` if they are only used in this compilation unit (file) and as
% ``static`` if they should be accessible with the ``namespace->name``
% notation. (Do this *after* the ``implements``, as otherwise the variables
% become inaccessible.) ::

% the name of the mode, keymap, and syntax table
private variable mode = "foo";

static variable foo_word_chars = get_word_chars()+ "_";

% Functions
% ---------
% 
% It helps the end-user a lot, if you provide on-line help text with the
% public functions. The tmtools_ mode at Jedmodes_ helps with formatting, the
% tm_ and make_ini_ modes makes it aviable to the Jed help system. ::

%!%+
%\function{start_line_with_foo}
%\synopsis{Prepend line with the sting in\var{Foo_Insertion}}
%\usage{ start_line_with_foo(insertion=Foo_Insertion)}
%\description
%  Prepend the current line with the string given in the custom
%  variable \var{Foo_Insertion} or in the optional argument \var{insertion}.
%\example
% While 
%#v+
%  start_line_with_foo()
%#v-
% inserts by default "foo " at the beginning of the line, you can override 
% this by setting \var{Foo_Insertion} or by e.g.
%#v+
%  start_line_with_foo("bar ")
%#v-
%\notes
%  Please document all public functions to the user can get online-help
%  via the Help menu.
%  \var{tm_make_doc} is a nice help in creating in-source documentation
%  in the "tm" format used by Jed
%\seealso{foo_mode, Foo_Insertion}
%!%-
public define start_line_with_foo() % (insertion=Foo_Insertion)
{ 
   variable insertion;
   if (_NARGS)                  % optional argument present
     insertion = ();
   else
     insertion = read_mini("start every line with:", Foo_Insertion, "");
   push_spot();
   bol;
   insert(insertion);
   pop_spot();
}

% Internal functions are best commented with a short abstract line::
  
% give a message() with help usage.
static define small_help()
{
   message("<RET>:Insert Foo q:Quit foo mode");
}


% Static functions that are intended for use from other modules, should
% have an on-line help text too::  
  
%!%+
%\function{foo->normalize_modename}
%\synopsis{find the normalized form of a mode}
%\usage{foo->normalize_modename(mode)}
%\description
% take a mode name and do some guesses for the associated slang-function
%\example
% To do this for the current mode, use e.g. 
%#v+
%  normalize_modename(what_mode, pop)
%#v-
% (the `pop' will pop the second return value of `what_mode').
%\notes
%  A related function is in bufutils.sl.
%\seealso{normalized_modename}
%!%-
static define normalize_modename(mode)
{
   variable modstr = extract_element (mode, 0, ' ');
   if (modstr == "") 
     modstr = "no";
   if (is_defined (modstr))
     return modstr;
   modstr += "_mode";
   if (is_defined (modstr))
     return modstr;
   modstr = strlow (modstr);
   if (is_defined (modstr))
     return modstr;
   error ("Mode " + modstr + " is not defined.");
}

% return to the mode that was used before
static define quit_foo()
{
   runhooks(normalize_modename(get_blocal_var("previous_mode")));
   update(0);
}

% Commenting
% ----------
% 
% Instead of defining your own (un)commenting functions, provide an interface
% to comments.sl::

set_comment_info (mode, "#foo: ", " :foo#", 7);

% Syntax highlight
% ----------------
% 
% Syntax table (non-DFA)::

create_syntax_table (mode);
define_syntax ("#foo:", ":foo#", '%', mode); % Comments
define_syntax ("([{", ")]}", '(', mode);     % Delimiters
define_syntax ("0-9a-zA-Z", 'w', mode);      % Words
define_syntax ("-+0-9.", '0', mode);         % Numbers
define_syntax (",", ',', mode);              % Delimiters
define_syntax (";", ',', mode);              % Delimiters
define_syntax ("-+/&*=<>|!~^", '+', mode);   % Operators

set_syntax_flags (mode, 0);

% Keywords::

() = define_keywords_n(mode, "foo", 3, 0);

% DFA syntax highlight
% --------------------
% 
% More versatile but also more complicated. See also **Help>Browse_Docs>dfa**.
% 
% DFA is not available for all versions of Jed, therefore we put the code in
% a preprocessor section.
% 
% Simple setup
% ~~~~~~~~~~~~
% ::

#ifdef HAS_DFA_SYNTAX
create_syntax_table (mode);  % clear the non-DFA entries
private define setup_dfa_callback (mode)
{
   dfa_define_highlight_rule("[0-9]*", "number", mode);
   dfa_define_highlight_rule (sprintf("[%s]*foo[%s]*", 
                                      foo_word_chars, foo_word_chars),
                              "keyword", mode);
   dfa_define_highlight_rule ("#foo:.*:foo#", "comment", mode);
   dfa_build_highlight_table(mode);
}
dfa_set_init_callback (&setup_dfa_callback, mode);
enable_dfa_syntax_for_mode(mode);
#endif

% Setup with cache file
% ~~~~~~~~~~~~~~~~~~~~~
% 
% Building a DFA highlight table can take considerable time. For complex
% highlight schemes, it makes sense to cache the table. The cache file will be
% created 
% 
% * at first use of the mode, or 
% * in a preprocessing step (``preparse.sl`` or ``update_dfa_cache_files()``
%   from make_ini_.
% 
% In the second case, the section between the lines 
% ``%%% DFA_CACHE_BEGIN %%%`` and ``%%% DFA_CACHE_END %%%`` will be evaluated
% in a temporary buffer. Therefore, no references to non-public variables or
% functions are allowed here. (Notice that ``mode`` inside the
% ``setup_dfa_callback()`` refers to the argument of the function.)::
  
#ifdef HAS_DFA_SYNTAX
%%% DFA_CACHE_BEGIN %%%
private define setup_dfa_callback(mode)
{
   variable foo_word_chars = get_word_chars()+ "_";
   dfa_enable_highlight_cache("foo.dfa", mode);
   dfa_define_highlight_rule("[0-9]*", "number", mode);
   dfa_define_highlight_rule ("[%s]*foo[%s]*", 
                                      foo_word_chars, foo_word_chars,
                              "keyword", mode);
   dfa_define_highlight_rule ("#foo:.*:foo#", "comment", mode);
   dfa_build_highlight_table(mode);
}
dfa_set_init_callback (&setup_dfa_callback, "foo");
%%% DFA_CACHE_END %%%
enable_dfa_syntax_for_mode(mode);
#endif


% Keybindings
% -----------
% 
% * Be carefull not to overwrite existing keybindings (if no good reason exists
%   for this). 
% * Remember that not all users use the emacs (or cua or ide ...) emulation.
% * You can bind to static functions if the mode sets up a named namespace_.
% * ``definekey_reserved()`` uses the (user-configurable)
%   ``_Reserved_Key_Prefix`` to avoid clashes.
% 
% ::

!if (keymap_p(mode)) 
  make_keymap(mode);
definekey_reserved(mode+"->small_help",  "?",  mode);
definekey_reserved(mode+"->quit_foo",     "q", mode);
definekey_reserved("start_line_with_foo","^M", mode);  % Return

% Mode menu
% ---------
% 
% The **Mode** menu is Jed's version of a context sensitive menu. It allows
% easy access to mode functions and also displays the key-binding (so there
% is normally no need for a separate help file with keybindings).
% ::
  
private define foo_menu (menu)
{
   menu_append_item (menu, "&Foo Line", "start_line_with_foo");
   menu_append_item (menu, "&Quit Foo", mode + "->quit_foo");
   menu_append_item (menu, "Foo &Help", mode + "->small_help");
}

% Mode function
% -------------
%   
% The mode function is called to set a Jed buffer in a special editing mode.
% It should:
% 
% * call ``set_mode(mode, flag)`` to actually set the mode,
% * activate the syntax highlight table,
% * activate the keymap,
% * register the mode menu
% * optionally set additional "mode info" fields and do some housekeeping
%   actions,
% * provide a mode_hook, so users can add their favourite shortcuts.
% 
% ::

%!%+
%\function{foo_mode}
%\synopsis{An example mode}
%\usage{Void foo_mode()}
%\description
%  A mode to place "foo" at the beginning of a line.
%  Also highlights all foos.
%\example
%#v+
%   foo_mode();
%   () = what_mode(); % returns two items
%   message(());
%#v-
%\notes
%  Actually, foo.sl is a commented template for writing jed modes.
%\seealso{mode_set_mode_info, slang_mode, set_mode, what_mode}
%!%-
public define foo_mode()
{
   ($1, ) = what_mode();
   define_blocal_var("previous_mode", $1);
   set_mode(mode, 4);
   use_syntax_table(mode);
   use_keymap(mode);
   mode_set_mode_info(mode, "init_mode_menu", &foo_menu);
   mode_set_mode_info(mode, "fold_info", "#{{{\r#}}}\r\r");
   mode_set_mode_info(mode, "word_chars", foo_word_chars);
   run_mode_hooks(mode + "_mode_hook");
}


% .. References
%    ----------
% 
% .. _Jed:          http://www.jedsoft.org/jed/
% .. _S-Lang:       http://www.jedsoft.org/slang/
% .. _Jedmodes:     http://jedmodes.sf.net/
% .. _tmtools:      http://jedmodes.sf.net/mode/tmtools/
% .. _make_ini:     http://jedmodes.sf.net/mode/make_ini/
% .. _tm:           http://jedmodes.sf.net/mode/tm/
% .. _help browser: http://jedmodes.sf.net/mode/hyperhelp/
% 
