% File: latex.sl -*- mode: SLang; mode: fold -*- % % Copyright (c) % 2007 Jörg Sommer % $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); }