% File: latex.sl -*- mode: SLang; mode: fold -*-
%
% Copyright (c)
% 2007 Jörg Sommer <
[email protected]>
% $Id: latex_typo.sl 206 2007-09-08 13:38:39Z joerg $
%
% -*- This file is part of Jörg's LaTeX Mode (JLM) -*-
%
% Description: This file includes all functions related to typography.
%
% License: 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 2 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.
if (current_namespace() != "latex")
throw UsageError, "You must load this file into the namespace \"latex\"";
private define is_active()
{
return andelse {LaTeX_Typo_Active} {not is_verbatim()};
}
private variable b_n_c_del;
private variable b_n_c_insert;
private variable b_n_c_fun;
private define before_next_char_hook(fun);
private define before_next_char_hook(fun)
{
debug_msg(sprintf("%s: %c, %S, %s, %s, %S", _function_name(), LAST_CHAR,
fun, b_n_c_del, b_n_c_insert, b_n_c_fun) );
EXIT_BLOCK
{
remove_from_hook("_jed_before_key_hooks", &before_next_char_hook());
__uninitialize(&b_n_c_del);
}
if (andelse {typeof(fun) == String_Type}
{orelse {fun == "self_insert_cmd"} {strncmp(fun, "latex->", 7) == 0}}
{@b_n_c_fun(LAST_CHAR)} )
{
variable len = strlen(b_n_c_del);
() = left(len);
if ( looking_at(b_n_c_del) )
() = replace_chars(len, b_n_c_insert);
else
() = right(len);
}
}
private define enable_before_next_char_hook(del, ins, fun)
{
if ( __is_initialized(&b_n_c_del) )
throw UsageError, "before_next_char_hook already active";
b_n_c_del = del;
b_n_c_insert = ins;
b_n_c_fun = fun;
add_to_hook("_jed_before_key_hooks", &before_next_char_hook());
}
private define is_alpha_or_hyphen(char)
{
return orelse {char == '-'} {isalpha(char)};
}
%!%+
%\function{typo_slash()}
%\synopsis{Replaces a / after a word by \\slash{} or adds ""}
%\usage{typo_slash()}
%\description
% LaTeX does not break words combined with / after the slash, e.g. the
% text "input/output" is one block and is not wrapped before output.
%
% If the number of characters before the / is at least \var{LaTeX_Typo_Word_Size},
% the / is replaced by \\slash{}, if the package babel is not loaded, or a
% "" is appended, if the package babel is loaded. \\slash{} is a / with an
% hyphenpoint after it. Unfortunely, it removes all other hyphenpoints
% from word after the slash. This does not happen with "" from babel.
%
%\notes
% You can disable this function by setting the variable \var{LaTeX_Typo_Active}
% to 0. This still inserts the / character as \sfun{self_insert_cmd} would do.
%
%\seealso{LaTeX_Typo_Active, LaTeX_Typo_Word_Size}
%!%-
static define typo_slash()
{
if (LAST_CHAR != '/')
throw UsageError, "typo_slash called for anything else than /";
% Insert the / and do abbrev expansion
call("self_insert_cmd");
!if ( is_active() )
return;
push_spot();
EXIT_BLOCK
{
pop_spot();
}
() = left(1);
if ( blooking_at("-") or blooking_at("\"~") or blooking_at("\"\"") )
{
EXIT_BLOCK {};
pop_spot();
if ( pkg_loaded("babel") )
insert("\"\"");
else
() = replace_chars(1, "\\slash{}");
return;
}
_get_point();
bskip_word_chars();
if ( () - _get_point() < LaTeX_Typo_Word_Size )
return;
if ( pkg_loaded("babel") )
enable_before_next_char_hook("", "\"\"", &is_alpha_or_hyphen());
else
enable_before_next_char_hook("/", "\\slash{}", &isalpha());
}
%!%+
%\function{typo_percent()}
%\synopsis{Inserts a \\, before \\%}
%\usage{typo_percent()}
%\description
% In german typography, a spatium (\\,) should be before a percent sign.
% This function removes all whitespaces before \\% and insert an \\,.
%
%\notes
% You can disable this function by setting the variable \var{LaTeX_Typo_Active}
% to 0. This still inserts the % character as \sfun{self_insert_cmd} would do.
%
%\seealso{LaTeX_Typo_Active}
%!%-
static define typo_percent()
{
if (LAST_CHAR != '%')
throw UsageError, "typo_percent called for anything else than %";
if (andelse {is_active()} {is_escaped()})
{
() = left(1);
trim();
!if ( blooking_at("\\,") )
insert("\\,");
() = right(1);
}
call("self_insert_cmd");
}
%!%+
%\function{typo_abbrev()}
%\synopsis{Inserts a \\, between abbreviations}
%\usage{typo_abbrev()}
%\description
% In german typography, a spatium (\\,) should be after the dot inside of
% an abbreviation, e.g. z.\\,B. or i.\\,d.\\,R. This functions checks if the
% number of characters before the first dot and before the current point is
% less than LaTeX_Typo_Word_Size and inserts a \\, after the previous dot,
% if it is so.
%
%\notes
% You can disable this function by setting the variable \var{LaTeX_Typo_Active}
% to 0. This still inserts the % character as \sfun{self_insert_cmd} would do.
%
%\seealso{LaTeX_Typo_Active, LaTeX_Typo_Word_Size}
%!%-
static define typo_abbrev()
{
if (LAST_CHAR != '.')
throw UsageError, "typo_abbrev called for anything else than .";
call("self_insert_cmd");
if ( is_active() )
{
push_spot();
EXIT_BLOCK
{
pop_spot();
}
() = left(1);
_get_point();
bskip_word_chars();
variable len = () - _get_point();
if (len == 0 or len >= LaTeX_Typo_Word_Size)
return;
bskip_white();
() = left(1);
!if ( looking_at_char('.') )
return;
push_spot();
EXIT_BLOCK
{
pop_spot();
pop_spot();
}
_get_point();
bskip_word_chars();
if ( () - _get_point() >= LaTeX_Typo_Word_Size )
return;
pop_spot();
() = right(1);
trim();
insert("\\,");
}
}
%!%+
%\function{typo_german_decimal_point()}
%\synopsis{Puts a comma between digits into \\mathord{}}
%\usage{typo_german_decimal_point()}
%\description
% In german, the comma is the separator of the fractional part of
% numbers. In LaTeX, the default meaning of , in math mode is as a list
% separator. Due to this it's necessary to tell LaTeX that the , is the
% decimal point.
%
% This functions checks, if the characters before the comma are digits
% with a leading space and if the characters behind the comma are digits.
% Then it replaces the comma by \\mathord{,}. In text mode, this functions
% does nothing special.
%
%\notes
% You can disable this function by setting the variable \var{LaTeX_Typo_Active}
% to 0. This still inserts the , character as \sfun{self_insert_cmd} would do.
%
%\seealso{LaTeX_Typo_Active}
%!%-
static define typo_german_decimal_point()
{
if (LAST_CHAR != ',')
throw UsageError, "typo_german_decimal_point called for anything else than ,";
call("self_insert_cmd");
!if (andelse {is_active()} {is_math()})
return;
push_spot();
EXIT_BLOCK
{
pop_spot();
}
() = left(1);
_get_point();
bskip_chars("0-9");
if ( andelse {() - _get_point() > 0}
{orelse {bolp()} {left(1) and is_substr(" \t$", char(what_char()))}} )
enable_before_next_char_hook(",", "\\mathord{,}", &isdigit());
}
private variable word_char_count;
private define hyphen_hook(fun);
private define hyphen_hook(fun)
{
debug_msg(_function_name() + ": " + char(LAST_CHAR) + ", $fun"$);
EXIT_BLOCK
{
remove_from_hook("_jed_before_key_hooks", &hyphen_hook());
__uninitialize(&word_char_count);
}
variable replace_str, back = 1;
if (andelse {typeof(fun) == String_Type} {fun == "self_insert_cmd"}
{isalpha(LAST_CHAR)})
{
if ( __is_initialized(&word_char_count) )
% 8.
{
debug_msg("hyphen_hook: $word_char_count"$);
++word_char_count;
if (word_char_count < LaTeX_Typo_Word_Size)
{
EXIT_BLOCK;
return;
}
back = word_char_count;
replace_str = "\"=";
}
else
% 7.
replace_str = "\"~";
}
else if (LAST_CHAR == ')')
% 6.
{
replace_str = "\"~";
enable_before_next_char_hook("", "\"\"", &isalpha());
}
else if (LAST_CHAR == '/')
% 5.
replace_str = "\"~";
else
return;
() = left(back);
() = replace_chars(1, replace_str);
() = right(back - 1);
}
%!%+
%\function{typo_hyphen()}
%\synopsis{Replace a hyphen by a special hyphen}
%\usage{typo_hyphen()}
%\description
% A simple - is not everywhere what you want. Babel defines some special
% hypens:
%#v+
% texdoc gerdoc section 2.2.4 or texdoc babel
% "" an unvisible hyphen point
% "~ an hyphen without an hyphen point
% "= an hyphen that doesn't prevents hyphenation in the
% rest of the word lik - does
%#v-
%
% \sfun{typo_hyphen} makes the following replacements:
%#v+
% w = word character s = space ( \t)
% W = at least LaTeX_Typo_Word_Size word chars
%
% rule | example
% --------------------+-------------------
% 1. $-w -> $"~w | $i$-mal, $\alpha$-Teilchen
% 2. }-w -> }"~w | \LaTeX{}-Aufruf
% 3. \w-w -> \w"~w | \LaTeX-Aufruf
% 4. s-w -> s"~w | bergauf und -ab
% 5. w-/ -> w"~/ | Ein-/Ausgabe
% 6. w-) -> w"~) | (Haupt-)Aufgabe
% 7. w-w -> w"~w | I-Punkt, HI-Virus
% 8. W-W -> W"=W | primitiv-rekursiv
% 9. /-w -> /"~w | Vorlesungsgliederung/-struktur
% 10. /""-w -> /"""~w | Vorlesungsgliederung/-struktur
%#v-
%\notes
% You can disable this function by setting the variable \var{LaTeX_Typo_Active}
% to 0. This still inserts the - character as \sfun{self_insert_cmd} would do.
%
%\seealso{LaTeX_Typo_Active}
%!%-
static define typo_hyphen()
{
if (LAST_CHAR != '-')
throw UsageError, "typo_hyphen called for anything else than -";
call("self_insert_cmd");
!if (andelse {is_active()} {pkg_loaded("babel")})
return;
push_spot();
EXIT_BLOCK
{
pop_spot();
}
if ( orelse {left(2) and is_substr("$}/ \t", char(what_char()) )}
{left(2) and looking_at("/\"\"")} )
% 1. + 2. + 4. + 9. + 10.
{
enable_before_next_char_hook("-", "\"~", &isalpha());
return;
}
() = right(3);
_get_point();
bskip_word_chars();
variable char_cnt = () - _get_point();
if (char_cnt == 0)
return;
if ( is_escaped() )
% 3.
enable_before_next_char_hook("-", "\"~", &isalpha());
else
% 5.--8.
{
if (char_cnt >= LaTeX_Typo_Word_Size)
% 8.
word_char_count = 0;
add_to_hook("_jed_before_key_hooks", &hyphen_hook());
}
}
static define typo_dots()
{
if ( orelse {is_verbatim()} {not blooking_at("..")} )
{
typo_abbrev();
return;
}
() = left(2);
push_spot();
variable text;
!if ( is_math() )
{
push_mark();
bskip_chars(" ");
() = left(1);
!if ( looking_at_char(',') )
() = right(1);
text = bufsubstr();
pop_spot();
deln(2);
try
cmd_insert("dots", 0);
catch ApplicationError:
cmd_insert("ldots");
insert(text);
return;
}
bskip_chars(" ");
() = left(1);
variable ch = what_char();
switch (ch)
{ case ',':
if ( pkg_loaded("amsmath") )
text = "\\dotsc,";
else
text = "\\ldots,";
}
{ case '+' or case '-' or case '<' or case '>' or case '=':
if ( pkg_loaded("amsmath") )
text = "\\dotsb" + char(what_char());
else
text = "\\cdots" + char( what_char() );
}
{ case '}':
if ( pkg_loaded("amsmath") )
text = "\\dotso";
else
text = "\\ldots";
}
{
variable cmd = is_command();
switch(cmd)
{ case "\\leq" or case "\\geq" or case "\\pm" or case "\\mp" or
case "\\cup" or case "\\cap" or case "\\vee" or case "\\wedge":
if ( pkg_loaded("amsmath") )
text = "\\dotsb" + cmd;
else
text = "\\cdots" + cmd;
}
{ case "\\int" or case "\\sum" or case "\\prod":
if ( pkg_loaded("amsmath") )
text = "\\dotsi";
else
text = "\\cdots";
pop_spot();
() = replace_chars(2, text+cmd);
return;
}
{
if ( pkg_loaded("amsmath") )
text = "\\dotso ";
else
text = "\\ldots ";
pop_spot();
() = replace_chars(2, text);
return;
}
}
bskip_chars(" ");
() = left(1);
variable looping = 0;
while ( looking_at_char('}') )
{
variable mark = create_user_mark();
if (find_matching_delimiter('}') != 1)
{
goto_user_mark(mark);
break;
}
() = left(1);
switch ( what_char() )
{ case '_' or case '^':
if (looping == 0)
{
() = right(2);
push_mark();
() = left(3);
}
else
() = left(1);
++looping;
}
{
goto_user_mark(mark);
break;
}
}
if (looping == 0)
{
pop_spot();
() = replace_chars(2, text);
return;
}
if ( re_looking_at("[0-9]") )
bskip_chars("0-9");
else
{
variable found_brakets = 0;
if ( andelse {looking_at_char('}')} {find_matching_delimiter('}') == 1} )
{
found_brakets = 1;
() = left(1);
}
cmd = is_command();
switch (cmd)
{ case "\\int" or case "\\sum" or case "\\prod":
if ( pkg_loaded("amsmath") )
text = "\\dotsi";
else
text = "\\cdots";
pop_mark(0);
pop_spot();
() = replace_chars(2, text+cmd);
return;
}
{ case NULL:
if (found_brakets)
() = right(1);
else
{
if ( re_looking_at("[A-Za-z]") )
bskip_chars("A-Za-z");
text += " ";
}
}
}
text += bufsubstr();
pop_spot();
() = replace_chars(2, text+"}");
() = left(1);
}