From 26f8c60d721bcd3fe6a0183b9f5ea076f9018293 Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Fri, 21 Jul 2017 22:00:53 +0000 Subject: pythontex (21jul17) git-svn-id: svn://tug.org/texlive/trunk@44860 c570f23f-e606-0410-a88d-b1316a301751 --- Master/texmf-dist/scripts/pythontex/depythontex.py | 2 +- .../texmf-dist/scripts/pythontex/depythontex2.py | 4 +- .../texmf-dist/scripts/pythontex/depythontex3.py | 4 +- Master/texmf-dist/scripts/pythontex/pythontex.py | 2 +- Master/texmf-dist/scripts/pythontex/pythontex2.py | 54 ++++- Master/texmf-dist/scripts/pythontex/pythontex3.py | 54 ++++- .../texmf-dist/scripts/pythontex/pythontex_2to3.py | 38 +-- .../scripts/pythontex/pythontex_engines.py | 259 +++++++++++++++------ 8 files changed, 305 insertions(+), 112 deletions(-) (limited to 'Master/texmf-dist/scripts/pythontex') diff --git a/Master/texmf-dist/scripts/pythontex/depythontex.py b/Master/texmf-dist/scripts/pythontex/depythontex.py index 30256c6d1c3..3d48e3b1daa 100755 --- a/Master/texmf-dist/scripts/pythontex/depythontex.py +++ b/Master/texmf-dist/scripts/pythontex/depythontex.py @@ -9,7 +9,7 @@ on your system. If you wish to use a different version of Python, you could launch depythontex2.py or depythontex3.py directly. The version of Python does not matter for depythontex, since no code is executed. -Copyright (c) 2013-2016, Geoffrey M. Poore +Copyright (c) 2013-2017, Geoffrey M. Poore All rights reserved. Licensed under the BSD 3-Clause License: http://www.opensource.org/licenses/BSD-3-Clause diff --git a/Master/texmf-dist/scripts/pythontex/depythontex2.py b/Master/texmf-dist/scripts/pythontex/depythontex2.py index 0975b9b8ca4..7687e0e7244 100755 --- a/Master/texmf-dist/scripts/pythontex/depythontex2.py +++ b/Master/texmf-dist/scripts/pythontex/depythontex2.py @@ -47,7 +47,7 @@ example, typeset code may have a different appearance or layout when it is typeset with a different package. -Copyright (c) 2013-2016, Geoffrey M. Poore +Copyright (c) 2013-2017, Geoffrey M. Poore All rights reserved. Licensed under the BSD 3-Clause License: http://www.opensource.org/licenses/BSD-3-Clause @@ -86,7 +86,7 @@ import codecs # Script parameters # Version -__version__ = '0.15' +__version__ = '0.16' # Functions and parameters for customizing the script output diff --git a/Master/texmf-dist/scripts/pythontex/depythontex3.py b/Master/texmf-dist/scripts/pythontex/depythontex3.py index 567363eeaa0..bdcea2185e0 100755 --- a/Master/texmf-dist/scripts/pythontex/depythontex3.py +++ b/Master/texmf-dist/scripts/pythontex/depythontex3.py @@ -47,7 +47,7 @@ example, typeset code may have a different appearance or layout when it is typeset with a different package. -Copyright (c) 2013-2016, Geoffrey M. Poore +Copyright (c) 2013-2017, Geoffrey M. Poore All rights reserved. Licensed under the BSD 3-Clause License: http://www.opensource.org/licenses/BSD-3-Clause @@ -86,7 +86,7 @@ import codecs # Script parameters # Version -__version__ = '0.15' +__version__ = '0.16' # Functions and parameters for customizing the script output diff --git a/Master/texmf-dist/scripts/pythontex/pythontex.py b/Master/texmf-dist/scripts/pythontex/pythontex.py index 0c6e5c70bb1..2fe6de902a2 100755 --- a/Master/texmf-dist/scripts/pythontex/pythontex.py +++ b/Master/texmf-dist/scripts/pythontex/pythontex.py @@ -16,7 +16,7 @@ under which your code is actually executed. Licensed under the BSD 3-Clause License: -Copyright (c) 2012-2016, Geoffrey M. Poore +Copyright (c) 2012-2017, Geoffrey M. Poore All rights reserved. diff --git a/Master/texmf-dist/scripts/pythontex/pythontex2.py b/Master/texmf-dist/scripts/pythontex/pythontex2.py index 519b36f3066..871d394e375 100755 --- a/Master/texmf-dist/scripts/pythontex/pythontex2.py +++ b/Master/texmf-dist/scripts/pythontex/pythontex2.py @@ -13,7 +13,7 @@ should be in the same directory. Licensed under the BSD 3-Clause License: -Copyright (c) 2012-2016, Geoffrey M. Poore +Copyright (c) 2012-2017, Geoffrey M. Poore All rights reserved. @@ -77,7 +77,7 @@ else: # Script parameters # Version -__version__ = '0.15' +__version__ = '0.16' @@ -1286,7 +1286,8 @@ def do_multiprocessing(data, temp_data, old_data, engine_dict): engine_dict[family].warnings, engine_dict[family].linenumbers, engine_dict[family].lookbehind, - keeptemps, hashdependencies)''' + keeptemps, hashdependencies, + pygments_settings)''' tasks.append(pool.apply_async(run_code, [encoding, outputdir, workingdir, code_dict[key], engine_dict[family].language, @@ -1299,7 +1300,8 @@ def do_multiprocessing(data, temp_data, old_data, engine_dict): engine_dict[family].warnings, engine_dict[family].linenumbers, engine_dict[family].lookbehind, - keeptemps, hashdependencies])) + keeptemps, hashdependencies, + pygments_settings])) if verbose: print(' - Code process ' + key.replace('#', ':')) @@ -1475,7 +1477,7 @@ def do_multiprocessing(data, temp_data, old_data, engine_dict): def run_code(encoding, outputdir, workingdir, code_list, language, commands, command_created, extension, makestderr, stderrfilename, code_index, errorsig, warningsig, linesig, stderrlookbehind, - keeptemps, hashdependencies): + keeptemps, hashdependencies, pygments_settings): ''' Function for multiprocessing code files ''' @@ -1526,7 +1528,10 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands, exec_cmd = shlex.split(bytes(command.format(file=script.replace('\\', '\\\\'), File=script_full.replace('\\', '\\\\'), workingdir=workingdir.replace('\\', '\\\\')))) exec_cmd = [unicode(elem) for elem in exec_cmd] else: - exec_cmd = shlex.split(command.format(file=script.replace('\\', '\\\\'), File=script_full.replace('\\', '\\\\'), workingdir=workingdir.replace('\\', '\\\\'))) + if family != 'juliacon': + exec_cmd = shlex.split(command.format(file=script.replace('\\', '\\\\'), File=script_full.replace('\\', '\\\\'), workingdir=workingdir.replace('\\', '\\\\'))) + else: + exec_cmd = shlex.split(command.format(file=script.replace('\\', '/'), File=script_full.replace('\\', '/'), workingdir=workingdir.replace('\\', '/'))) # Add any created files due to the command # This needs to be done before attempts to execute, to prevent orphans try: @@ -1563,6 +1568,22 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands, messages.append(' Missing output file for ' + key_run.replace('#', ':')) errors += 1 else: + if family == 'juliacon': + with open(out_file_name.rsplit('.', 1)[0] + '.tex', 'r', encoding=encoding) as f: + tex_data_lines = f.readlines() + inst = 0 + for n, line in enumerate(tex_data_lines): + if line.rstrip() == '\\begin{juliaterm}': + tex_data_lines[n] = '=>PYTHONTEX:STDOUT#{0}#code#\n'.format(inst) + inst += 1 + if n != 0: + tex_data_lines[n-1] = '' + if line.rstrip() == '\\end{juliaterm}': + tex_data_lines[n] = '' + tex_data_lines.append('=>PYTHONTEX:DEPENDENCIES#\n=>PYTHONTEX:CREATED#\n') + with open(out_file_name, 'w', encoding=encoding) as f: + f.write(''.join(tex_data_lines)) + f = open(out_file_name, 'r', encoding=encoding) out = f.read() f.close() @@ -1619,6 +1640,18 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands, else: dependencies[dep] = (os.path.getmtime(dep_file), '') + if family == 'juliacon': + from pygments import highlight + from pygments.lexers import get_lexer_by_name + from pygments.formatters import LatexFormatter + formatter = dict() + lexer = dict() + for codetype in pygments_settings: + if codetype != ':GLOBAL': + p = pygments_settings[codetype]['formatter_options'].copy() + p['commandprefix'] = 'PYG' + formatter[codetype] = LatexFormatter(**p) + lexer[codetype] = get_lexer_by_name(pygments_settings[codetype]['lexer'], **p) for block in out.split('=>PYTHONTEX:STDOUT#')[1:]: if block: delims, content = block.split('#\n', 1) @@ -1649,6 +1682,8 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands, # Remove newline added by printing, prevent # LaTeX from adding a space after content content = content.rsplit('\n', 1)[0] + '\\endinput\n' + if family == 'juliacon': + content = highlight(content, lexer[family], formatter[family]) f.write(content) f.close() files.append(fname) @@ -1658,6 +1693,8 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands, messages.append('* PythonTeX error') messages.append(' Missing stderr file for ' + key_run.replace('#', ':')) errors += 1 + elif family == 'juliacon': + pass else: # Open error and code files. f = open(err_file_name, encoding=encoding) @@ -2408,7 +2445,10 @@ def python_console(jobname, encoding, outputdir, workingdir, fvextfile, sys.excepthook = sys.__excepthook__ old_stdout = sys.stdout sys.stdout = self.iostdout - self.interact(self.banner) + if sys.version_info.major == 3 and sys.version_info.minor >= 6: + self.interact(self.banner, exitmsg='') + else: + self.interact(self.banner) sys.stdout = old_stdout self.session_log = self.iostdout.getvalue() diff --git a/Master/texmf-dist/scripts/pythontex/pythontex3.py b/Master/texmf-dist/scripts/pythontex/pythontex3.py index b872dbaa2c4..cb96f97d336 100755 --- a/Master/texmf-dist/scripts/pythontex/pythontex3.py +++ b/Master/texmf-dist/scripts/pythontex/pythontex3.py @@ -13,7 +13,7 @@ should be in the same directory. Licensed under the BSD 3-Clause License: -Copyright (c) 2012-2016, Geoffrey M. Poore +Copyright (c) 2012-2017, Geoffrey M. Poore All rights reserved. @@ -77,7 +77,7 @@ else: # Script parameters # Version -__version__ = '0.15' +__version__ = '0.16' @@ -1286,7 +1286,8 @@ def do_multiprocessing(data, temp_data, old_data, engine_dict): engine_dict[family].warnings, engine_dict[family].linenumbers, engine_dict[family].lookbehind, - keeptemps, hashdependencies)''' + keeptemps, hashdependencies, + pygments_settings)''' tasks.append(pool.apply_async(run_code, [encoding, outputdir, workingdir, code_dict[key], engine_dict[family].language, @@ -1299,7 +1300,8 @@ def do_multiprocessing(data, temp_data, old_data, engine_dict): engine_dict[family].warnings, engine_dict[family].linenumbers, engine_dict[family].lookbehind, - keeptemps, hashdependencies])) + keeptemps, hashdependencies, + pygments_settings])) if verbose: print(' - Code process ' + key.replace('#', ':')) @@ -1475,7 +1477,7 @@ def do_multiprocessing(data, temp_data, old_data, engine_dict): def run_code(encoding, outputdir, workingdir, code_list, language, commands, command_created, extension, makestderr, stderrfilename, code_index, errorsig, warningsig, linesig, stderrlookbehind, - keeptemps, hashdependencies): + keeptemps, hashdependencies, pygments_settings): ''' Function for multiprocessing code files ''' @@ -1526,7 +1528,10 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands, exec_cmd = shlex.split(bytes(command.format(file=script.replace('\\', '\\\\'), File=script_full.replace('\\', '\\\\'), workingdir=workingdir.replace('\\', '\\\\')))) exec_cmd = [unicode(elem) for elem in exec_cmd] else: - exec_cmd = shlex.split(command.format(file=script.replace('\\', '\\\\'), File=script_full.replace('\\', '\\\\'), workingdir=workingdir.replace('\\', '\\\\'))) + if family != 'juliacon': + exec_cmd = shlex.split(command.format(file=script.replace('\\', '\\\\'), File=script_full.replace('\\', '\\\\'), workingdir=workingdir.replace('\\', '\\\\'))) + else: + exec_cmd = shlex.split(command.format(file=script.replace('\\', '/'), File=script_full.replace('\\', '/'), workingdir=workingdir.replace('\\', '/'))) # Add any created files due to the command # This needs to be done before attempts to execute, to prevent orphans try: @@ -1563,6 +1568,22 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands, messages.append(' Missing output file for ' + key_run.replace('#', ':')) errors += 1 else: + if family == 'juliacon': + with open(out_file_name.rsplit('.', 1)[0] + '.tex', 'r', encoding=encoding) as f: + tex_data_lines = f.readlines() + inst = 0 + for n, line in enumerate(tex_data_lines): + if line.rstrip() == '\\begin{juliaterm}': + tex_data_lines[n] = '=>PYTHONTEX:STDOUT#{0}#code#\n'.format(inst) + inst += 1 + if n != 0: + tex_data_lines[n-1] = '' + if line.rstrip() == '\\end{juliaterm}': + tex_data_lines[n] = '' + tex_data_lines.append('=>PYTHONTEX:DEPENDENCIES#\n=>PYTHONTEX:CREATED#\n') + with open(out_file_name, 'w', encoding=encoding) as f: + f.write(''.join(tex_data_lines)) + f = open(out_file_name, 'r', encoding=encoding) out = f.read() f.close() @@ -1619,6 +1640,18 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands, else: dependencies[dep] = (os.path.getmtime(dep_file), '') + if family == 'juliacon': + from pygments import highlight + from pygments.lexers import get_lexer_by_name + from pygments.formatters import LatexFormatter + formatter = dict() + lexer = dict() + for codetype in pygments_settings: + if codetype != ':GLOBAL': + p = pygments_settings[codetype]['formatter_options'].copy() + p['commandprefix'] = 'PYG' + formatter[codetype] = LatexFormatter(**p) + lexer[codetype] = get_lexer_by_name(pygments_settings[codetype]['lexer'], **p) for block in out.split('=>PYTHONTEX:STDOUT#')[1:]: if block: delims, content = block.split('#\n', 1) @@ -1649,6 +1682,8 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands, # Remove newline added by printing, prevent # LaTeX from adding a space after content content = content.rsplit('\n', 1)[0] + '\\endinput\n' + if family == 'juliacon': + content = highlight(content, lexer[family], formatter[family]) f.write(content) f.close() files.append(fname) @@ -1658,6 +1693,8 @@ def run_code(encoding, outputdir, workingdir, code_list, language, commands, messages.append('* PythonTeX error') messages.append(' Missing stderr file for ' + key_run.replace('#', ':')) errors += 1 + elif family == 'juliacon': + pass else: # Open error and code files. f = open(err_file_name, encoding=encoding) @@ -2408,7 +2445,10 @@ def python_console(jobname, encoding, outputdir, workingdir, fvextfile, sys.excepthook = sys.__excepthook__ old_stdout = sys.stdout sys.stdout = self.iostdout - self.interact(self.banner) + if sys.version_info.major == 3 and sys.version_info.minor >= 6: + self.interact(self.banner, exitmsg='') + else: + self.interact(self.banner) sys.stdout = old_stdout self.session_log = self.iostdout.getvalue() diff --git a/Master/texmf-dist/scripts/pythontex/pythontex_2to3.py b/Master/texmf-dist/scripts/pythontex/pythontex_2to3.py index 166e6784676..442309c86b1 100755 --- a/Master/texmf-dist/scripts/pythontex/pythontex_2to3.py +++ b/Master/texmf-dist/scripts/pythontex/pythontex_2to3.py @@ -4,30 +4,30 @@ ''' Convert PythonTeX scripts from Python 2 to Python 3 -It isn't possible to have a single PythonTeX code base, since unicode text -needs to be supported. Under Python 2, this means importing unicode_literals +It isn't possible to have a single PythonTeX code base, since unicode text +needs to be supported. Under Python 2, this means importing unicode_literals from __future__, or using the unicode function or "u" prefix. Under Python 3, all strings are automatically unicode. -At the same time, the differences between the Python 2 and 3 versions are +At the same time, the differences between the Python 2 and 3 versions are usually very small, involving only a few lines of code. To keep the code base -unified, while simultaneously fully supporting both Python 2 and 3, the -following scheme was devised. The code is written for Python 2. Whenever -code is not compatible with Python 3, it is enclosed with the tags -"#// Python 2" and "#\\ End Python 2" (each on its own line, by itself). If -a Python 3 version of the code is needed, it is included between analogous -tags "#// Python 3" and "#\\ End Python 2". The Python 3 code is commented +unified, while simultaneously fully supporting both Python 2 and 3, the +following scheme was devised. The code is written for Python 2. Whenever +code is not compatible with Python 3, it is enclosed with the tags +"#// Python 2" and "#\\ End Python 2" (each on its own line, by itself). If +a Python 3 version of the code is needed, it is included between analogous +tags "#// Python 3" and "#\\ End Python 2". The Python 3 code is commented out with "#", at the same indentation level as the Python 3 tags. -This script creates Python 3 scripts from the original Python 2 scripts -by commenting out everything between the Python 2 tags, and uncommenting -everything between the Python 3 tags. In this way, full compatibility is -maintained with both Python 2 and 3 while keeping the code base essentially -unified. This approach also allows greater customization of version-specific -code than would be possible if automatic translation with a tool like 2to3 +This script creates Python 3 scripts from the original Python 2 scripts +by commenting out everything between the Python 2 tags, and uncommenting +everything between the Python 3 tags. In this way, full compatibility is +maintained with both Python 2 and 3 while keeping the code base essentially +unified. This approach also allows greater customization of version-specific +code than would be possible if automatic translation with a tool like 2to3 was required. -Copyright (c) 2012-2014, Geoffrey M. Poore +Copyright (c) 2012-2017, Geoffrey M. Poore All rights reserved. Licensed under the BSD 3-Clause License: http://www.opensource.org/licenses/BSD-3-Clause @@ -50,7 +50,7 @@ def from2to3(list_of_code): in_2 = False in_3 = False indent = '' - + for line in list_of_code: if r'#// Python 2' in line: in_2 = True @@ -70,8 +70,8 @@ def from2to3(list_of_code): if fixed[0].startswith('#!/usr/bin/env python2'): fixed[0] = fixed[0].replace('python2', 'python3') return fixed - - + + for file in files_to_process: f = open(file, 'r', encoding=encoding) converted_code = from2to3(f.readlines()) diff --git a/Master/texmf-dist/scripts/pythontex/pythontex_engines.py b/Master/texmf-dist/scripts/pythontex/pythontex_engines.py index 6bcbd1690db..38fea5efc43 100755 --- a/Master/texmf-dist/scripts/pythontex/pythontex_engines.py +++ b/Master/texmf-dist/scripts/pythontex/pythontex_engines.py @@ -17,7 +17,7 @@ document (script for execution). -Copyright (c) 2012-2016, Geoffrey M. Poore +Copyright (c) 2012-2017, Geoffrey M. Poore All rights reserved. Licensed under the BSD 3-Clause License: http://www.opensource.org/licenses/BSD-3-Clause @@ -1113,6 +1113,12 @@ CodeEngine('julia', 'julia', '.jl', '{julia} "{file}.jl"', julia_template, SubCodeEngine('julia', 'jl') +CodeEngine('juliacon', 'julia', '.jl', '{julia} -e "using Weave; weave(\\"{File}.jl\\", \\"tex\\")"', '{body}\n', + '#+ term=true\n{code}\n', '', '', + 'ERROR:', 'WARNING:', ':{number}', True, created='{File}.tex') + + + octave_template = ''' @@ -1285,42 +1291,133 @@ CodeEngine('bash', 'bash', '.sh', rust_template = ''' // -*- coding: utf-8 -*- - #![allow(dead_code)] - #![allow(unused_imports)] - - + #![allow(dead_code, unused_imports)] + #[warn(unused_imports)] mod rust_tex_utils {{ - use std::fmt; - use std::collections; - use std::io::prelude::*; - - pub struct RustTeXUtils {{ - _formatter: Box String>, - _before: Box, - _after: Box, - pub family: &'static str, - pub session: &'static str, - pub restart: &'static str, - pub dependencies: Vec, - pub created: Vec, - pub command: &'static str, - pub context: collections::HashMap<&'static str, &'static str>, - pub args: collections::HashMap<&'static str, &'static str>, - pub instance: &'static str, - pub line: &'static str, + use std::{{borrow, collections, fmt, fs, io, iter, ops, path}}; + use self::OpenMode::{{ReadMode, WriteMode, AppendMode, TruncateMode, CreateMode, CreateNewMode}}; + pub struct UserAction<'u> {{ + _act: Box }} - - impl RustTeXUtils {{ + impl<'u> UserAction<'u> {{ + pub fn new() -> Self {{ + Self::from(|| {{}}) + }} + pub fn act(&mut self) {{ + (self._act)(); + }} + pub fn set(&mut self, f: F) {{ + self._act = Box::new(f); + }} + }} + impl<'u> Default for UserAction<'u> {{ + fn default() -> Self {{ + Self::new() + }} + }} + impl<'u, F: FnMut() + 'u> From for UserAction<'u> {{ + fn from(f: F) -> Self {{ + UserAction {{ _act: Box::new(f) }} + }} + }} + impl<'u, U: Into> + 'u> ops::Add for UserAction<'u> {{ + type Output = UserAction<'u>; + fn add(self, f: U) -> Self::Output {{ + let mut self_act: Box = self._act; + let mut other_act: Box = f.into()._act; + Self::from(move || {{ self_act.as_mut()(); other_act.as_mut()(); }}) + }} + }} + impl<'u, F: Into> + 'u> iter::FromIterator for UserAction<'u> {{ + fn from_iter(iter: T) -> Self where T: IntoIterator {{ + let mut others: Vec = iter.into_iter().map(F::into).collect(); + Self::from(move || {{ for other in others.iter_mut() {{ other.act(); }} }}) + }} + }} + impl<'u> ops::Deref for UserAction<'u> {{ + type Target = FnMut() + 'u; + fn deref(&self) -> &Self::Target {{ + &*self._act + }} + }} + impl<'u> ops::DerefMut for UserAction<'u> {{ + fn deref_mut(&mut self) -> &mut Self::Target {{ + &mut *self._act + }} + }} + pub struct RustTeXUtils<'u> {{ + _formatter: Box String + 'u>, + pub before: UserAction<'u>, + pub after: UserAction<'u>, + pub family: &'u str, + pub session: &'u str, + pub restart: &'u str, + pub dependencies: collections::HashSet>, + pub created: collections::HashSet>, + pub command: &'u str, + pub context: collections::HashMap<&'u str, borrow::Cow<'u, str>>, + pub args: collections::HashMap<&'u str, borrow::Cow<'u, str>>, + pub instance: &'u str, + pub line: &'u str, + }} + #[derive(Clone,Copy,Debug,Hash,PartialEq,Eq)] + pub enum OpenMode {{ + /// Open the file for reading + ReadMode, + /// Open the file for writing + WriteMode, + /// Open the file for appending + AppendMode, + /// Truncate the file before opening + TruncateMode, + /// Create the file before opening if necessary + CreateMode, + /// Always create the file before opening + CreateNewMode, + }} + pub mod open_mode {{ + pub use super::OpenMode::{{self, ReadMode, WriteMode, AppendMode, TruncateMode, CreateMode, CreateNewMode}}; + pub const R: &'static [OpenMode] = &[ReadMode]; + pub const W: &'static [OpenMode] = &[WriteMode]; + pub const A: &'static [OpenMode] = &[AppendMode]; + pub const WC: &'static [OpenMode] = &[WriteMode, CreateMode]; + pub const CW: &'static [OpenMode] = WC; + pub const AC: &'static [OpenMode] = &[AppendMode, CreateMode]; + pub const CA: &'static [OpenMode] = AC; + pub const WT: &'static [OpenMode] = &[WriteMode, TruncateMode]; + pub const TW: &'static [OpenMode] = WT; + pub const WCT: &'static [OpenMode] = &[WriteMode, CreateMode, TruncateMode]; + pub const WTC: &'static [OpenMode] = WCT; + pub const CWT: &'static [OpenMode] = WCT; + pub const CTW: &'static [OpenMode] = WCT; + pub const TWC: &'static [OpenMode] = WCT; + pub const TCW: &'static [OpenMode] = WCT; + pub const WN: &'static [OpenMode] = &[WriteMode, CreateNewMode]; + pub const NW: &'static [OpenMode] = WN; + pub const AN: &'static [OpenMode] = &[AppendMode, CreateNewMode]; + pub const NA: &'static [OpenMode] = AN; + }} + impl OpenMode {{ + /// The same options as `fs::File::open`. + pub fn open() -> &'static [OpenMode] {{ + open_mode::R + }} + /// The same options as `fs::File::create`. + pub fn create() -> &'static [OpenMode] {{ + open_mode::WCT + }} + }} + impl<'u> RustTeXUtils<'u> {{ pub fn new() -> Self {{ RustTeXUtils {{ _formatter: Box::new(|x: &fmt::Display| format!("{{}}", x)), - _before: Box::new(|| {{}}), - _after: Box::new(|| {{}}), + before: UserAction::new(), + after: UserAction::new(), family: "{family}", session: "{session}", restart: "{restart}", - dependencies: Vec::new(), - created: Vec::new(), + dependencies: collections::HashSet::new(), + created: collections::HashSet::new(), command: "", context: collections::HashMap::new(), args: collections::HashMap::new(), @@ -1328,53 +1425,69 @@ rust_template = ''' line: "", }} }} - - pub fn formatter(&mut self, x: A) -> String {{ - (*self._formatter)(&x) + (self._formatter)(&x) }} - pub fn set_formatter String + 'static>(&mut self, f: F) {{ + pub fn set_formatter String + 'u>(&mut self, f: F) {{ self._formatter = Box::new(f); }} - - pub fn before(&mut self) {{ - (*self._before)(); - }} - pub fn set_before(&mut self, f: F) {{ - self._before = Box::new(f); + pub fn add_dependencies(&mut self, deps: SS) + where SS::Item: Into> + {{ + self.dependencies.extend(deps.into_iter().map(SS::Item::into)); }} - - pub fn after(&mut self) {{ - (*self._after)(); - }} - pub fn set_after(&mut self, f: F) {{ - self._after = Box::new(f); - }} - - pub fn add_dependencies(&mut self, deps: SS) where SS::Item: Into {{ - self.dependencies.append(&mut deps.into_iter().map(|x| x.into()).collect()); + pub fn add_created(&mut self, crts: SS) + where SS::Item: Into> + {{ + self.created.extend(crts.into_iter().map(SS::Item::into)); }} - - pub fn add_created(&mut self, crts: SS) where SS::Item: Into {{ - self.created.append(&mut crts.into_iter().map(|x| x.into()).collect()); + pub fn open(&mut self, name: P, options: O) -> io::Result + where P: AsRef, + O: IntoIterator, + O::Item: borrow::Borrow + {{ + let opts = options.into_iter() + .map(|x| *>::borrow(&x)) + .collect::>(); + let mut options = fs::OpenOptions::new(); + if opts.contains(&ReadMode) {{ + options.read(true); + self.add_dependencies(iter::once(name.as_ref().to_owned())); + }} + if opts.contains(&WriteMode) {{ + options.write(true); + }} + if opts.contains(&AppendMode) {{ + options.append(true); + }} + if opts.contains(&TruncateMode) {{ + options.truncate(true); + }} + if opts.contains(&CreateMode) {{ + options.create(true); + self.add_created(iter::once(name.as_ref().to_owned())); + }} + if opts.contains(&CreateNewMode) {{ + options.create_new(true); + self.add_created(iter::once(name.as_ref().to_owned())); + }} + options.open(name) }} - pub fn cleanup(self) {{ println!("{{}}", "{dependencies_delim}"); for x in self.dependencies {{ - println!("{{}}", x); + println!("{{}}", x.to_str().expect(&format!("could not properly display path ({{:?}})", x))); }} println!("{{}}", "{created_delim}"); for x in self.created {{ - println!("{{}}", x); + println!("{{}}", x.to_str().expect(&format!("could not properly display path ({{:?}})", x))); }} }} - - pub fn setup_wrapper(&mut self, cmd: &'static str, cxt: &'static str, ags: &'static str, ist: &'static str, lne: &'static str) {{ - fn parse_map(kvs: &'static str) -> collections::HashMap<&'static str, &'static str> {{ + pub fn setup_wrapper(&mut self, cmd: &'u str, cxt: &'u str, ags: &'u str, ist: &'u str, lne: &'u str) {{ + fn parse_map<'w>(kvs: &'w str) -> collections::HashMap<&'w str, borrow::Cow<'w, str>> {{ kvs.split(',').filter(|s| !s.is_empty()).map(|kv| {{ let (k, v) = kv.split_at(kv.find('=').expect(&format!("Error parsing supposed key-value pair ({{}})", kv))); - (k.trim(), v[1..].trim()) + (k.trim(), v[1..].trim().into()) }}).collect() }} self.command = cmd; @@ -1384,24 +1497,23 @@ rust_template = ''' self.line = lne; }} }} + impl<'u> Default for RustTeXUtils<'u> {{ + fn default() -> Self {{ + Self::new() + }} + }} }} - - - use std::{{io, fmt, env, path, ffi, collections}}; + use std::{{borrow, collections, env, ffi, fmt, fs, hash, io, iter, ops, path}}; use std::io::prelude::*; - - + use rust_tex_utils::open_mode; #[allow(unused_mut)] fn main() {{ let mut rstex = rust_tex_utils::RustTeXUtils::new(); if env::set_current_dir(ffi::OsString::from("{workingdir}".to_string())).is_err() && env::args().all(|x| x != "--manual") {{ panic!("Could not change to the specified working directory ({workingdir})"); }} - {extend} - {body} - rstex.cleanup(); }} ''' @@ -1410,20 +1522,21 @@ rust_wrapper = ''' rstex.setup_wrapper("{command}", "{context}", "{args}", "{instance}", "{line}"); println!("{stdoutdelim}"); writeln!(io::stderr(), "{stderrdelim}").unwrap(); - rstex.before(); - + rstex.before.act(); {code} - - rstex.after(); + rstex.after.act(); ''' -rust_sub = '''println!("{field_delim}");\nprintln!("{{}}", {field});\n''' +rust_sub = ''' + println!("{field_delim}"); + println!("{{}}", {field}); + ''' CodeEngine('rust', 'rust', '.rs', # The full script name has to be used in order to make Windows and Unix behave nicely # together when naming executables. Despite appearances, using `.exe` works on Unix too. ['{rustc} --crate-type bin -o {File}.exe -L {workingdir} {file}.rs', '{File}.exe'], - rust_template, rust_wrapper, 'println!("{{}}", rstex.formatter({code}));', rust_sub, + rust_template, rust_wrapper, '{{ let val = {{ {code} }}; println!("{{}}", rstex.formatter(val)); }}', rust_sub, errors='error:', warnings='warning:', linenumbers='.rs:{number}', created='{File}.exe') -- cgit v1.2.3