Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 109 #113

Merged
merged 2 commits into from
Nov 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
Work in progress
----------------
- **Changed interface** to extension modules for packages and document classes
(issue [#110](../../issues/110)).
Entry point is now function init\_module() with two arguments: parser and
list of packet options.
See for instance file yalafi/packages/amsmath.py.
- yalafi.shell
- added support for multi-language documents (issue [#98](../../issues/98))
- new options --multi-language, --ml-continue-threshold, --ml-rule-threshold,
--ml-disable, --ml-disablecategories
- yalafi core
- **Changed interface** to extension modules for packages and document
classes (issue [#110](../../issues/110)).
Entry point is now function init\_module() with two arguments: parser and
list of package options.
See for instance yalafi/packages/amsmath.py and yalafi/packages/babel.py.
- added support for multi-language documents (issue [#98](../../issues/98));
including fixed issues [#104](../../issues/104), [#108](../../issues/108)
including fixed issues [#104](../../issues/104), [#108](../../issues/108),
[#109](../../issues/109)
- README.md: updated, added section for multi-language support

Version 1.1.7 (2020/11/04)
Expand Down
22 changes: 10 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1183,12 +1183,11 @@ This feature is experimental, any comments are welcome.
Operation may be slow, unless a LanguageTool server is used, for instance,
via option '--server my'.

As an example, assume option '--multi-language' for yalafi.shell and input:
As an example, assume option '--multi-language' for yalafi.shell and the LaTeX
text:
```
\documentclass{article}
\usepackage[german,english]{babel}

\selectlanguage{english}
\newcommand{\german}[1]{\textit{\foreignlanguage{german}{#1}}}

\begin{document}
Expand All @@ -1199,17 +1198,16 @@ Then, the Vim example from section [“Plain Vim”](#plain-vim)
with setting `let g:ltyc_showsuggestions = 1` will produce this quickfix
window:
```
t.tex|8 col 9 info| Possible spelling mistake found. Suggestion: the; then; they; them; thee; Theo; hex; THX; TeX; Tex; The; t hex; the x; Théo
t.tex|8 col 34 info| Möglicher Tippfehler gefunden. Suggestion: exzellent; exzellente; exzellenten; exzellenter; Exzellenz; exzellentes; erzählend; exzellentem; erhellend; erkältend; exzelliert
t.tex|8 col 44 info| Two consecutive dots Suggestion: .; …
t.tex|6 col 9 info| Possible spelling mistake found. Suggestion: the; then; they; them; thee; Theo; hex; THX; TeX; Tex; The; t hex; the x; Théo
t.tex|6 col 34 info| Möglicher Tippfehler gefunden. Suggestion: exzellent; exzellente; exzellenten; exzellenter; Exzellenz; exzellentes; erzählend; exzellentem; erhellend; erkältend; exzelliert
t.tex|6 col 44 info| Two consecutive dots Suggestion: .; …
```
The initial language is specified by option --language.
Commands like `\selectlanguage{...}` are effective in files loaded via option
--define or with `\LTinput{...}`.
Language names in 'babel' commands are mapped to xx-XX codes by dictionary
The initial language is specified by option --language, it is overwritten
upon `\usepackage[...]{babel}`.
Commands like `\selectlanguage{...}` are also effective in files loaded via
option --define or with `\LTinput{...}`.
Language names in babel commands are mapped to xx-XX codes by dictionary
'language\_map' in file [yalafi/packages/babel.py](yalafi/packages/babel.py).
Please note:
- Currently, `\usepackage[...]{babel}` does not set the active language.

**Further options.**
In the above example, LanguageTool is invoked for
Expand Down
28 changes: 28 additions & 0 deletions tests/test_multi_lang.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,3 +236,31 @@ def test_10():
plain = get_ml_txt(latex_10, lang='en-GB')
assert plain == plain_10

# issue #109
#
latex_11 = r"""
"a
\usepackage{babel}
"a
\usepackage[english,german]{babel}
"a
\selectlanguage{english}
"a
"""
plain_11 = {
'de-DE': [
"""ä
"""
],
'en-GB': [
"""
"a
"a
""", """"a
"""
],
}
def test_11():
plain = get_ml_txt(latex_11, lang='en-GB')
assert plain == plain_11

2 changes: 1 addition & 1 deletion tests/test_shell/test_shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def test_shell():
# start "LT server"
subprocess.Popen(server_cmd.split(), stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
time.sleep(10)
time.sleep(5)

# run shell.py
out = subprocess.run(shell_cmd.split(), stdout=subprocess.PIPE,
Expand Down
2 changes: 1 addition & 1 deletion tests/test_shell_cmd/run_shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def run_as_server(options, latex, json):
+ ' ' + options)
server = subprocess.Popen(cmd.split(), stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
time.sleep(10)
time.sleep(5)

cmd = 'python -m yalafi.shell --plain-input --server my ' + shell_in
out = subprocess.run(cmd.split(), stdout=subprocess.PIPE)
Expand Down
4 changes: 3 additions & 1 deletion yalafi/defs.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@
from . import utils

class InitModule:
def __init__(self, macros_latex='', macros_python=[], environments=[]):
def __init__(self, macros_latex='', macros_python=[], environments=[],
inject_tokens=[]):
self.macros_latex = macros_latex
self.macros_python = macros_python
self.environs = environments
self.inject_tokens = inject_tokens

class Printable:
def __repr__(self):
Expand Down
9 changes: 5 additions & 4 deletions yalafi/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,16 +126,17 @@ def h_load_defs(parser, buf, mac, args, pos):
return utils.latex_error('could not read file ' + repr(file),
pos, parser.latex, parser.parms)
toks = parser.parser_work(latex)
return utils.filter_lang_toks(toks, pos)
return utils.filter_set_toks(toks, pos, defs.LanguageToken)

# read definitions for a LaTeX package
#
def h_load_module(prefix):
def f(parser, buf, mac, args, pos):
options = []
options = parser.parse_keyvals_list(args[0])
options = parser.expand_keyvals(options)
pack = parser.get_text_expanded(args[1])
f = utils.get_module_handler(pack, prefix)
parser.init_package(pack, f, options)
return []
out = parser.init_package(pack, f, options)
return utils.filter_set_toks(out, pos, None)
return f

10 changes: 5 additions & 5 deletions yalafi/mathparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def expand_display_math(self, buf, tok, env):
tokens = self.detect_math_parts(tokens)
sec, next_repl = self.replace_section(False, tokens,
first_section, next_repl,
self.parser.parms.lang_context().math_repl_display)
self.parser.parms.lang_context.math_repl_display)
out += sec
if end and end.txt == '&':
out.append(defs.SpaceToken(out[-1].pos, ' ', pos_fix=True))
Expand All @@ -98,7 +98,7 @@ def expand_display_math(self, buf, tok, env):
out = [defs.ActionToken(start_simple),
defs.SpaceToken(start_simple, ' ', pos_fix=True),
defs.TextToken(start_simple, self.parser.parms.
lang_context().math_repl_display[0],
lang_context.math_repl_display[0],
pos_fix=True)]
if txt and txt[-1] in self.parser.parms.math_punctuation:
out.append(defs.TextToken(out[-1].pos, txt[-1],
Expand All @@ -114,7 +114,7 @@ def expand_inline_math(self, buf, tok):
tokens = self.detect_math_parts(tokens)
out = [defs.ActionToken(tok.pos)]
t, x = self.replace_section(True, tokens, True, True,
self.parser.parms.lang_context().math_repl_inline)
self.parser.parms.lang_context.math_repl_inline)
out += t
out.append(defs.ActionToken(out[-1].pos))
return out
Expand Down Expand Up @@ -241,8 +241,8 @@ def replace_section(self, inline, tokens, first_section,
op = tok.leading_op()
elem = tok.has_elem(parms)
if not inline and first_part and op:
s = parms.lang_context().math_op_text.get(
op.txt, parms.lang_context().math_op_text[None])
s = parms.lang_context.math_op_text.get(
op.txt, parms.lang_context.math_op_text[None])
out.append(defs.SpaceToken(tok.pos, ' ', pos_fix=True))
out.append(defs.TextToken(op.pos, s, pos_fix=True))
out.append(defs.SpaceToken(op.pos, ' ', pos_fix=True))
Expand Down
2 changes: 1 addition & 1 deletion yalafi/packages/amsthm.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def h_proof(parser, buf, mac, args, pos):
if args[0]:
ret = args[0]
else:
ret = [TextToken(pos, parser.parms.lang_context().proof_name,
ret = [TextToken(pos, parser.parms.lang_context.proof_name,
pos_fix=True)]
return ret + [TextToken(ret[-1].pos, '.', pos_fix=True),
SpaceToken(ret[-1].pos, '\n', pos_fix=True)]
Expand Down
8 changes: 7 additions & 1 deletion yalafi/packages/babel.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,14 @@ def init_module(parser, options):

environments = []

inject_tokens = []
if options:
# set current language to the last in option list
inject_tokens = [LanguageToken(0, lang=translate_lang(options[-1][0]),
hard=True)]

return InitModule(macros_latex=macros_latex, macros_python=macros_python,
environments=environments)
environments=environments, inject_tokens=inject_tokens)

def modify_language_map(babel, lt):
language_map[babel] = lt
Expand Down
4 changes: 2 additions & 2 deletions yalafi/packages/glossaries.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def h_newacronym(parser, buf, mac, args, pos):
return modify_description(parser, args[2])

def h_newglossaryentry(parser, buf, mac, args, pos):
descr = parser.parse_keyval_list(args[1]).get('description', [])
descr = parser.parse_keyvals_dict(args[1]).get('description', [])
return modify_description(parser, descr)

the_glossary = {}
Expand All @@ -127,7 +127,7 @@ def h_newglossaryentry(parser, buf, mac, args, pos):
#
def h_parse_glsdefs(parser, buf, mac, args, pos):
label = parser.get_text_expanded(args[0])
the_glossary[label] = parser.parse_keyval_list(args[1])
the_glossary[label] = parser.parse_keyvals_dict(args[1])
return []

# get token list for a key, database label given as token list
Expand Down
5 changes: 2 additions & 3 deletions yalafi/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ def init_parser_languages(self, lang):

self.parser_lang_stack = [(settings[self.check_parser_lang(lang)],
lang)]
self.lang_context = self.parser_lang_stack[-1][0]

# set misc collections
#
Expand Down Expand Up @@ -433,9 +434,7 @@ def change_parser_lang(self, tok):
self.parser_lang_stack.append(
(self.parser_lang_settings[self.check_parser_lang(tok.lang)],
tok.lang))

def lang_context(self):
return self.parser_lang_stack[-1][0]
self.lang_context = self.parser_lang_stack[-1][0]

def lang_context_lang(self):
return self.parser_lang_stack[-1][1]
Expand Down
48 changes: 33 additions & 15 deletions yalafi/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class Parser:
def __init__(self, parms, packages=[], read_macros=None):
self.parms = parms
self.read_macros = read_macros
self.packages = []
self.packages = {}
self.the_macros = {}
self.the_environments = {}
self.mathparser = mathparser.MathParser(self)
Expand Down Expand Up @@ -65,20 +65,24 @@ def labs_default(level):
# - load other required packages
# - let package handler update parameter object
# - update local dictionaries for macros and environments
# --> only do all that if package not yet loaded, or if options have
# changed
#
def init_package(self, name, actions, options):
if name and name in self.packages:
return
out = []
if name and name in self.packages and self.packages[name] == options:
return out
try:
for requ in actions[0]:
if requ not in self.packages:
self.init_package(requ, utils.get_module_handler(
if requ not in self.packages or self.packages[requ] == options:
out += self.init_package(requ, utils.get_module_handler(
requ, self.parms.package_modules), options)
if name:
self.packages.append(name)
self.modify_parameters(actions[1], options)
self.packages[name] = options
out += self.modify_parameters(actions[1], options)
except:
utils.fatal('error loading module ' + repr(name))
return out

# modify local parameter object
# - used by package extension mechanism
Expand All @@ -92,6 +96,7 @@ def modify_parameters(self, f, options):
self.the_environments[e.name] = e
if mods.macros_latex:
self.parser_work(mods.macros_latex)
return mods.inject_tokens

# scan and parse (expand) LaTeX string to tokens
# - here also skip LaTeX text enclosed in special comments
Expand Down Expand Up @@ -141,7 +146,7 @@ def parse(self, latex, define='', extract=None):
main = []
if define:
toks = self.parser_work(define)
main = utils.filter_lang_toks(toks, 0)
main = utils.filter_set_toks(toks, 0, defs.LanguageToken)
main += self.parser_work(latex)

if extract:
Expand Down Expand Up @@ -249,7 +254,7 @@ def expand_sequence(self, buf, env_stop=None):
if self.parms.multi_language:
self.parms.change_parser_lang(tok)
out.append(tok)
elif tok.txt in self.parms.lang_context().active_chars:
elif tok.txt in self.parms.lang_context.active_chars:
out.append(self.expand_short_macro(buf, tok))
continue
elif type(tok) is defs.CommentToken:
Expand Down Expand Up @@ -588,21 +593,25 @@ def Space(pos):
def expand_short_macro(self, buf, tok):
cur = buf.next()
if (not cur or tok.txt + cur.txt
not in self.parms.lang_context().short_macros):
not in self.parms.lang_context.short_macros):
return tok
buf.next()
return defs.TextToken(tok.pos,
self.parms.lang_context().short_macros[tok.txt + cur.txt],
self.parms.lang_context.short_macros[tok.txt + cur.txt],
pos_fix=True)

# parse a key-value list of the form 'a=b,a4paper,x={ hoho }'
# - return a dictionary
# - values are token lists, except if no '=' given: value then is None
# XXX: parsing of key name is not really robust
#
def parse_keyval_list(self, tokens):
def parse_keyvals_dict(self, tokens):
as_list = self.parse_keyvals_list(tokens)
return {k: v for (k, v) in as_list}

def parse_keyvals_list(self, tokens):
buf = scanner.Buffer(tokens)
values = {}
values = []
while True:
tok = buf.skip_space()
if not tok:
Expand All @@ -616,7 +625,7 @@ def parse_keyval_list(self, tokens):
if not tok or tok.txt == ',':
# no value given with = ...
tok = buf.next()
values[key] = None
values.append((key, None))
continue
buf.next() # skip '='
tok = buf.skip_space() # skip leading space of value
Expand All @@ -640,7 +649,16 @@ def parse_keyval_list(self, tokens):
if val and type(val[-1]) is defs.SpaceToken:
# skip trailing space of value
val.pop()
values[key] = val
values.append((key, val))
buf.next() # skip ','
return values

# expand keyval list to text form (None if no '=' given)
#
def expand_keyvals(self, keyvals):
def f(kv):
if kv[1] is None:
return kv
return (kv[0], self.get_text_expanded(kv[1]))
return [f(kv) for kv in keyvals]

6 changes: 3 additions & 3 deletions yalafi/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,14 @@ def get_module_handler(name, prefix):
warning('could not load module ' + repr(mod))
return [], lambda p, o: defs.InitModule()

# filter out LanguageTokens and set their position numbers
# filter out tokens and set their position numbers
#
def filter_lang_toks(toks, pos):
def filter_set_toks(toks, pos, tok_typ):
def f(t):
t = copy.copy(t)
t.pos = pos
return t
return [f(t) for t in toks if type(t) is defs.LanguageToken]
return [f(t) for t in toks if tok_typ is None or type(t) is tok_typ]


class LanguageSection:
Expand Down