ADR: User Script Debugging Support Architecture

Status

Accepted - Implemented across multiple PRs and ongoing enhancements

Date

2025-09-27

Context

OmegaT supports user scripts written in Groovy and JavaScript that are executed via javax.script.ScriptEngine. These scripts have access to OmegaT’s core APIs through exposed bindings (project, editor, glossary, mainWindow, Core). Currently, script developers face significant challenges in debugging their scripts:

  1. No integrated debugging capabilities: Scripts must be debugged through print statements or external logging

  2. Limited development environment support: While OmegaT provides a basic integrated editor with syntax highlighting, there is no code completion for OmegaT-specific APIs, no variable inspection, and no breakpoint debugging

  3. No variable inspection: No way to inspect script variables or OmegaT object states during execution

  4. Difficult error diagnosis: Stack traces from ScriptEngine don’t provide clear mapping to original source lines

  5. Limited IDE integration: Developers who prefer external IDEs lack debugging support and OmegaT API intellisense

The community has expressed strong interest in modern debugging capabilities to improve script development productivity and attract more developers to the OmegaT scripting ecosystem.

Decision

We will implement script debugging support through the following architectural approach:

1. Refactor ScriptRunner Architecture

  • Extract AbstractScriptRunner: Create an abstract base class containing common functionality and template methods

  • Implement StandardScriptRunner: Maintain existing behavior with zero performance overhead

  • Implement DebugScriptRunner: Add debugging capabilities through script instrumentation and debug server

  • Maintain ScriptRunner as Facade: Ensure backward compatibility by delegating to AbstractScriptRunner

2. Debug Server Integration

  • HTTP-based Debug Server: Implement a lightweight HTTP server using Java’s built-in HttpServer

  • Debug Adapter Protocol Compatibility: Provide endpoints compatible with Debug Adapter Protocol(DAP)

  • Runtime Selection: Choose runner implementation based on system property omegat.debug.scripts

3. Script Instrumentation Approach

  • Non-invasive Instrumentation: Inject debug tracking code without modifying the original script logic

  • Line Tracking: Add line number tracking for breakpoint support

  • Variable Watching: Provide debug helpers accessible to scripts via bindings

  • Breakpoint Support: Allow manual breakpoints through breakpoint() function calls

4. External IDE/Editor Integration

  • Debug Configuration: Provide launch configurations for attaching to OmegaT debug sessions

  • Developer Documentation: Provide detailed documentation for script debugging

Implementation Details

Core Architecture

// Template method pattern
public abstract class AbstractScriptRunner {
    public static Object executeScript(String script, ScriptEngine engine, Map<String, Object> additionalBindings) {
        return getActiveRunner().doExecuteScript(script, engine, additionalBindings);
    }
    
    protected abstract Object doExecuteScript(String script, ScriptEngine engine, Map<String, Object> additionalBindings);
}

// Runtime selection
public static AbstractScriptRunner getActiveRunner() {
    if (Boolean.getBoolean("omegat.debug.scripts")) {
        return new DebugScriptRunner();
    } else {
        return new StandardScriptRunner();
    }
}

Debug Server

  • Port: Default 8000, configurable via omegat.debug.scripts.port

  • Endpoints: /setBreakpoints, /getVariables, /continue, /stepOver

  • Communication: JSON over HTTP, compatible with Debug Adapter Protocol

  • Security: Localhost-only binding for security

Script Instrumentation

  • Line Tracking: Inject __debug__.trackLine(lineNumber) before executable statements

  • Variable Access: Provide __debug__.watch(name, value) for variable inspection

  • Manual Breakpoints: Global breakpoint() function for explicit pause points

  • Conditional: Only active when the debug server is running

Consequences

Positive

  1. Improved Developer Experience: Modern debugging capabilities comparable to professional IDEs

  2. Backward Compatibility: Existing scripts and OmegaT installations remain unaffected

  3. Performance: Zero overhead in standard mode; debug overhead only when explicitly enabled

  4. Extensibility: Architecture supports future debugging enhancements

  5. Community Growth: Lower barrier to entry for script development

  6. External Tool Support: Enables integration with popular development environments

Negative

  1. Increased Complexity: Additional code paths and components to maintain

  2. Debugging Overhead: Performance impact when debug mode is enabled

  3. Network Dependency: Debug server requires localhost network access

  4. Maintenance Burden: Additional documentation, testing, and support requirements

Neutral

  1. Optional Feature: Debug capabilities are opt-in and don’t affect standard usage

  2. Standard Technologies: Uses well-established protocols (HTTP, Debug Adapter Protocol)

  3. Incremental Implementation: Can be developed and deployed in phases

Alternatives Considered

1. Direct JVM Debugging

Approach: Use standard JVM debugging (-agentlib:jdwp) to debug scripts Rejected: ScriptEngine execution context makes this impractical; scripts run in a separate execution context

2. Custom Language Server

Approach: Implement Language Server Protocol for OmegaT scripts Rejected: Unnecessary complexity; existing Groovy/JavaScript language servers provide sufficient functionality

3. Plugin-based Debugging

Approach: Implement debugging entirely as an external plugin without core changes Rejected: Limited access to script execution context; would require reflection hacks

4. IDE-specific Integration

Approach: Integrate directly with specific IDEs (IntelliJ, Eclipse, VSCode) Rejected: Would limit accessibility. DAP’s universal protocol support can work across VSCode, Emacs, Vim/Neovim, Sublime Text, and numerous other editors without requiring separate integrations.

Implementation Plan

Phase 1: Core Refactoring

  • Extract AbstractScriptRunner with template methods

  • Implement StandardScriptRunner maintaining existing behavior

  • Update ScriptRunner facade for backward compatibility

  • Comprehensive testing to ensure no regressions

Phase 2: Debug Infrastructure

  • Implement DebugScriptRunner with instrumentation

  • Develop HTTP debug server with basic protocol support

  • Add debug helper bindings for scripts

  • Integration testing with sample scripts

Phase 3: IDE Integration

  • Implement OmegaT API type generation from Maven artifacts

  • Create debugging documentation and tutorials

  • Beta testing with community developers

Phase 4: Polish and Documentation

  • Performance optimization and stability improvements

  • Comprehensive documentation for developers and users

  • Community feedback integration

  • Release preparation

References

Phase 1: Refactoring

Phase 2: Debug Infrastructure

Appendix: Technical Specifications

System Properties

  • omegat.debug.scripts: Enable debug mode (boolean, default: false)

  • omegat.debug.scripts.port: Debug server port (integer, default: 8000)

Script Debug API

// Available in debug mode
breakpoint()                           // Pause execution
__debug__.watch("variable", value)     // Watch variable
__debug__.log("message")               // Debug logging
__debug__.trackLine(lineNumber)        // Internal line tracking

This ADR establishes the foundation for professional script debugging capabilities in OmegaT while maintaining the project’s commitment to backward compatibility and architectural integrity.