Skip to content

Conversation

@prudhomm
Copy link
Member

@prudhomm prudhomm commented Dec 8, 2025

Summary

Add support for XeLaTeX and LuaLaTeX compilation engines alongside the existing pdflatex and latexmk options.

Changes

  • Add xelatex and lualatex as valid engine options in CLI
  • Implement _run_xelatex() and _run_lualatex() methods for direct compilation
  • Add _build_xelatex_command() and _build_lualatex_command() helpers
  • Update _build_latexmk_command() with pdf_mode parameter for -xelatex/-lualatex flags
  • Add xelatex and lualatex to dependency checks
  • Update sample config documentation with new engine options

Usage

# Compile with XeLaTeX (for custom fonts)
article-cli compile --engine xelatex presentation.tex

# Compile with LuaLaTeX
article-cli compile --engine lualatex document.tex

# Use latexmk with XeLaTeX backend
article-cli compile --engine latexmk document.tex  # Now supports -xelatex flag internally

Closes #1

- Add xelatex and lualatex as valid engine options in CLI
- Implement _run_xelatex() and _run_lualatex() methods for direct compilation
- Add _build_xelatex_command() and _build_lualatex_command() helpers
- Update _build_latexmk_command() with pdf_mode parameter for -xelatex/-lualatex flags
- Add xelatex and lualatex to dependency checks
- Update sample config documentation with new engine options"
Refs #1

- Add test_latex_compiler.py with 29 tests for Issue #1
- Test _build_latexmk_command with pdf_mode parameter
- Test _build_xelatex_command and _build_lualatex_command
- Test _compile_once routing for all engines
- Test _compile_watch rejection for direct engines
- Test check_dependencies includes xelatex and lualatex
- Test execution success, failure, and timeout scenarios
@prudhomm prudhomm force-pushed the 1-add-xelatex-and-lualatex-engine-support branch from 4a050df to d146c40 Compare December 8, 2025 03:44
@prudhomm prudhomm requested a review from Copilot December 8, 2025 03:45
@prudhomm prudhomm merged commit b8e9847 into main Dec 8, 2025
12 checks passed
@prudhomm prudhomm deleted the 1-add-xelatex-and-lualatex-engine-support branch December 8, 2025 03:49
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for XeLaTeX and LuaLaTeX compilation engines to the article-cli tool, expanding beyond the existing pdflatex and latexmk options. These additional engines enable users to compile LaTeX documents that require advanced font support and Unicode handling, which are common in presentations and documents with custom fonts.

Key Changes

  • Added xelatex and lualatex as valid engine choices in CLI arguments
  • Implemented direct compilation methods _run_xelatex() and _run_lualatex() with multi-pass support
  • Extended _build_latexmk_command() with pdf_mode parameter to support different backend engines
  • Updated dependency checking to include the new engines
  • Added comprehensive test coverage for all new functionality

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.

File Description
src/article_cli/cli.py Updated CLI argument parser to accept xelatex and lualatex as engine options with helpful description
src/article_cli/config.py Updated sample config documentation to list all four engine options with usage guidance
src/article_cli/latex_compiler.py Implemented core functionality: routing logic, command builders, and execution methods for XeLaTeX and LuaLaTeX engines; updated latexmk command builder with pdf_mode parameter; added new engines to dependency checks
tests/test_latex_compiler.py Added comprehensive test suite covering command building, routing, watch mode restrictions, execution success/failure, timeouts, and dependency checking for new engines

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +242 to +250
"""Build latexmk command based on LaTeX Workshop configuration
Args:
tex_path: Path to .tex file
shell_escape: Enable shell escape
output_dir: Output directory
continuous: Enable preview continuous mode
pdf_mode: PDF generation mode ("pdf", "xelatex", "lualatex")
"""
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring mentions LaTeX Workshop configuration, but the function now has additional functionality (pdf_mode parameter) that isn't specifically related to LaTeX Workshop. Consider updating the main description to be more generic or explain that the pdf_mode extension allows for different LaTeX engines.

Copilot uses AI. Check for mistakes.
Comment on lines +307 to +360
def _run_xelatex(
self, tex_path: Path, shell_escape: bool, output_dir: Optional[str]
) -> bool:
"""Run xelatex compilation (multiple passes for cross-references)"""
cmd = self._build_xelatex_command(tex_path, shell_escape, output_dir)

try:
# Run multiple passes for cross-references, bibliography, etc.
passes = ["First pass", "Second pass", "Third pass"]

for i, pass_name in enumerate(passes):
print_info(f"{pass_name} (xelatex)...")
result = subprocess.run(
cmd,
cwd=tex_path.parent,
capture_output=True,
text=True,
timeout=120, # 2 minute timeout per pass
)

if result.returncode != 0:
print_error(f"❌ {pass_name} failed")
if result.stdout:
print("STDOUT:")
print(result.stdout)
if result.stderr:
print("STDERR:")
print(result.stderr)
return False

# Check if we need to run bibtex/biber
if i == 0: # After first pass
self._run_bibliography_if_needed(tex_path, result.stdout)

pdf_name = tex_path.with_suffix(".pdf").name
if output_dir:
pdf_path = Path(output_dir) / pdf_name
else:
pdf_path = tex_path.with_suffix(".pdf")

if pdf_path.exists():
print_success(f"✅ Compilation successful: {pdf_path}")
self._show_pdf_info(pdf_path)
return True
else:
print_error("Compilation reported success but PDF not found")
return False

except subprocess.TimeoutExpired:
print_error("Compilation timed out")
return False
except Exception as e:
print_error(f"Compilation error: {e}")
return False
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The _run_xelatex method is almost identical to _run_pdflatex (lines 179-232) with only the engine name and command builder method differing. Consider extracting the common logic into a shared helper method like _run_engine_multi_pass(engine_name, build_command_func, tex_path, shell_escape, output_dir) to reduce code duplication and improve maintainability.

Copilot uses AI. Check for mistakes.
Comment on lines +386 to +439
def _run_lualatex(
self, tex_path: Path, shell_escape: bool, output_dir: Optional[str]
) -> bool:
"""Run lualatex compilation (multiple passes for cross-references)"""
cmd = self._build_lualatex_command(tex_path, shell_escape, output_dir)

try:
# Run multiple passes for cross-references, bibliography, etc.
passes = ["First pass", "Second pass", "Third pass"]

for i, pass_name in enumerate(passes):
print_info(f"{pass_name} (lualatex)...")
result = subprocess.run(
cmd,
cwd=tex_path.parent,
capture_output=True,
text=True,
timeout=120, # 2 minute timeout per pass
)

if result.returncode != 0:
print_error(f"❌ {pass_name} failed")
if result.stdout:
print("STDOUT:")
print(result.stdout)
if result.stderr:
print("STDERR:")
print(result.stderr)
return False

# Check if we need to run bibtex/biber
if i == 0: # After first pass
self._run_bibliography_if_needed(tex_path, result.stdout)

pdf_name = tex_path.with_suffix(".pdf").name
if output_dir:
pdf_path = Path(output_dir) / pdf_name
else:
pdf_path = tex_path.with_suffix(".pdf")

if pdf_path.exists():
print_success(f"✅ Compilation successful: {pdf_path}")
self._show_pdf_info(pdf_path)
return True
else:
print_error("Compilation reported success but PDF not found")
return False

except subprocess.TimeoutExpired:
print_error("Compilation timed out")
return False
except Exception as e:
print_error(f"Compilation error: {e}")
return False
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The _run_lualatex method is almost identical to _run_xelatex and _run_pdflatex, with only the engine name and command builder method differing. This is a third instance of duplicated logic. Consider extracting the common logic into a shared helper method to reduce code duplication and improve maintainability.

Copilot uses AI. Check for mistakes.
Comment on lines +362 to +384
def _build_xelatex_command(
self, tex_path: Path, shell_escape: bool, output_dir: Optional[str]
) -> List[str]:
"""Build xelatex command"""
cmd = ["xelatex"]

if shell_escape:
cmd.append("--shell-escape")

cmd.extend(
[
"-synctex=1",
"-interaction=nonstopmode",
"-file-line-error",
]
)

if output_dir:
cmd.extend(["-output-directory", output_dir])

cmd.append(str(tex_path))

return cmd
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The _build_xelatex_command method is almost identical to _build_pdflatex_command with only the command name differing. Consider extracting the common logic into a shared helper method like _build_engine_command(engine_name, tex_path, shell_escape, output_dir) to reduce code duplication.

Copilot uses AI. Check for mistakes.
Comment on lines +441 to +463
def _build_lualatex_command(
self, tex_path: Path, shell_escape: bool, output_dir: Optional[str]
) -> List[str]:
"""Build lualatex command"""
cmd = ["lualatex"]

if shell_escape:
cmd.append("--shell-escape")

cmd.extend(
[
"-synctex=1",
"-interaction=nonstopmode",
"-file-line-error",
]
)

if output_dir:
cmd.extend(["-output-directory", output_dir])

cmd.append(str(tex_path))

return cmd
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The _build_lualatex_command method is almost identical to _build_xelatex_command and _build_pdflatex_command with only the command name differing. This is a third instance of the same duplicated logic. Consider extracting the common logic into a shared helper method to reduce code duplication.

Copilot uses AI. Check for mistakes.
"""

import subprocess
from pathlib import Path
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import of 'Path' is not used.

Suggested change
from pathlib import Path

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add XeLaTeX and LuaLaTeX engine support

2 participants