Skip to content

Latest commit

 

History

History
165 lines (135 loc) · 5.37 KB

File metadata and controls

165 lines (135 loc) · 5.37 KB

GHCi REPL Manager Implementation Summary

Overview

Successfully implemented a robust GHCi REPL manager for the Haskell-Run VS Code extension that replaces the old terminal-based approach with a prompt-driven, queue-based system.

Files Created

1. /src/ghci/GhciReplManager.ts (NEW)

Purpose: Core REPL management with deterministic prompt-based output handling

Key Features:

  • Prompt-based Detection: Uses regex />\s*$/ to detect GHCi prompt instead of hard-coded delays
  • Command Queue System: Serializes commands to prevent race conditions
  • Automatic Error Detection: Regex patterns detect compilation errors, missing modules, parse errors
  • State Safety: Module state updated ONLY on successful operations
  • Defensive Timeout: 10-second failsafe timeout (not for synchronization)
  • Event-Based Sync: No setTimeout() for command synchronization - purely event-driven

Core Components:

  1. Interface: GhciCommand

    interface GhciCommand {
      command: string;
      resolve: (output: string) => void;
      reject: (error: string) => void;
    }
  2. Class Methods:

    • constructor(outputChannel) - Initialize with output logging
    • runCommand(command) - Execute command, return Promise
    • loadModule(filePath, moduleName, loadedModules) - Load .hs file with state safety
    • reloadModule(moduleName, loadedModules) - Reload with state safety
    • evaluate(expression) - Evaluate Haskell expression
    • dispose() - Clean termination of GHCi process
    • isAlive() - Check if REPL is running

Files Modified

1. /src/extension.ts

Changes:

a) Import new manager (Line 7)

import { GhciReplManager } from './ghci/GhciReplManager';

b) Initialize in activate() (Lines 24-28)

const ghciRepl = new GhciReplManager(outputChannel);
const loadedModules = new Set<string>();
context.subscriptions.push({
    dispose: () => ghciRepl.dispose()
});

c) Update runSelectedFunction command (Lines 291-310)

  • Removed terminal visibility requirement
  • Removed setTimeout() delays
  • Replaced old replManager.loadModule() calls with ghciRepl.loadModule()
  • Replaced replManager.evaluateInRepl() with ghciRepl.evaluate()
  • Added proper result output to debug channel

d) Update REPL management commands (Lines 320-330)

  • restartRepl - Disposes and reinitializes GHCi
  • clearRepl - Clears module tracking and reinits REPL

What This Fixes (Issue #5)

Problem Fixed By
Hard-coded 500ms/1000ms delays Prompt-based detection regex
Silent failures without error context ERROR_REGEX pattern matching
Wrong internal state on failure State updates only after success
Race conditions in command execution Serialized command queue
Terminal visibility dependency Event-based output handling
Cross-platform timing issues Deterministic prompt detection
No error information in UI Detailed error messages to user

Architecture

activate()
├─ Create GhciReplManager instance
├─ Initialize command handlers
│  ├─ runSelectedFunction
│  │  ├─ Load module via ghciRepl.loadModule()
│  │  └─ Evaluate via ghciRepl.evaluate()
│  ├─ restartRepl
│  └─ clearRepl
└─ Dispose cleanup on deactivate()

GhciReplManager
├─ spawn('ghci') → ChildProcess
├─ Event listeners
│  ├─ stdout.on('data') → onData()
│  ├─ stderr.on('data') → onData()
│  ├─ on('exit') → error handling
│  └─ on('error') → error handling
├─ Command Queue
│  ├─ FIFO serialization
│  └─ Promise-based API
└─ Output Parsing
   ├─ PROMPT_REGEX: />\s*$/
   └─ ERROR_REGEX: (error:|Failed,|Could not find module|parse error|Variable not in scope)

Integration Points

1. Command Execution Flow

User selects function
  ↓
runSelectedFunction handler
  ↓
ghciRepl.loadModule() → Load file
  ↓
ghciRepl.evaluate() → Run expression
  ↓
Promise resolves with output
  ↓
Show result in output channel + notification

2. Error Handling

  • Automatic error detection via regex
  • Promise rejection on error match
  • User notification + output channel logging
  • State unchanged on failure

3. Resource Management

  • Single GHCi process per session
  • Automatic cleanup on dispose
  • Subscription-based disposal

Testing Checklist

  • ✅ Build succeeds (npm run watch:tsc)
  • ✅ No TypeScript errors
  • ✅ Extension activation works
  • ✅ GhciReplManager initializes on startup
  • ✅ Output channel logs all operations
  • ⏳ Manual testing: Load and evaluate Haskell functions
  • ⏳ Manual testing: Error detection and recovery
  • ⏳ Manual testing: Restart/clear commands

Next Steps (Optional)

  1. Add unit tests for prompt detection and error parsing
  2. Support Windows line endings if needed (\r\n variant for PROMPT_REGEX)
  3. Add timeout configuration to package.json settings
  4. Monitor for GHCi version compatibility (prompt format may vary)

Backward Compatibility

  • Old ReplManager still imported but not used by new code
  • Can be removed in future cleanup PR
  • Existing CodeLens and TreeView providers unaffected
  • All existing commands still functional

Implementation Date: January 9, 2026
Status: ✅ Complete and buildable