run method

Future<void> run()

Implementation

Future<void> run() async {
  // Disable flushing in the Lua print function to avoid stream conflicts
  LuaLikeConfig().flushAfterPrint = false;

  final history = ReplHistory();

  // Load history from file first
  try {
    history.loadFromFile();
    if (debugMode) {
      print('Loaded ${history.length} commands from history');
    }
  } catch (e) {
    if (debugMode) {
      print('Error loading history: $e');
    }
  }

  // Create a console for terminal control with scrolling support
  final console = Console.scrolling(recordBlanks: false);

  // Set up virtual devices for REPL I/O
  final stdinDevice = VirtualIODevice();
  final stdoutDevice = ConsoleOutputDevice(console);
  IOLib.defaultInput = createLuaFile(stdinDevice);
  IOLib.defaultOutput = createLuaFile(stdoutDevice);

  // Custom print function that writes to our buffer instead of stdout
  void customPrint(String message) {
    stdoutDevice.write('$message\n');
  }

  // Print welcome message
  customPrint(
    'LuaLike $lualikeVersion (Lua $luaCompatVersion compatible)  AST mode',
  );
  customPrint('Type "exit" to quit');

  // REPL loop
  bool running = true;
  String? multilineBuffer;

  while (running) {
    try {
      // Clear the output buffer before each command
      multilineBuffer = null;

      // Get custom prompt from _PROMPT global variable
      final promptValue = bridge.getGlobal('_PROMPT');
      String basePrompt = '> ';
      if (promptValue != null && promptValue is String) {
        basePrompt = promptValue;
      }

      // Determine prompt based on whether we're in multiline mode
      String prompt = multilineBuffer != null ? '>> ' : basePrompt;

      // Read input line using console.readLine which supports history navigation
      console.write(prompt);
      final line = console.readLine(
        cancelOnBreak: true,
        callback: (text, lastPressed) {
          if (lastPressed.isControl) {
            if (lastPressed.controlChar == ControlCharacter.ctrlC) {
              running = false;
            }
          }
        },
      );

      // Check for exit or EOF
      if (line == null ||
          (multilineBuffer == null && line.toLowerCase() == 'exit')) {
        running = false;
        continue;
      }

      // Write input to the virtual stdin device
      stdinDevice.write('$line\n');

      // Add to history if not empty
      if (line.isNotEmpty) {
        history.add(line);
      }

      // Handle multiline input
      String codeToExecute;
      if (multilineBuffer != null) {
        // We're in multiline mode
        if (line.trim().isEmpty) {
          // Empty line ends multiline input
          codeToExecute = multilineBuffer;
          multilineBuffer = null;
        } else {
          // Add to multiline buffer
          multilineBuffer += '\n$line';
          continue;
        }
      } else {
        // Check if this might be the start of multiline input
        if (line.trim().endsWith('{') ||
            line.trim().endsWith('(') ||
            line.trim().endsWith('do') ||
            line.trim().endsWith('then') ||
            line.trim().endsWith('else') ||
            line.trim().endsWith('function') ||
            line.trim().endsWith('=')) {
          multilineBuffer = line;
          continue;
        }
        codeToExecute = line;
      }

      // Execute the code
      Object? result;
      try {
        result = await bridge.execute(codeToExecute);
      } on ReturnException catch (e) {
        result = e.value;
      } catch (e) {
        if (e.toString().contains('unexpected symbol near') ||
            e.toString().contains('syntax error')) {
          // This might be an incomplete statement, try multiline mode
          if (multilineBuffer == null) {
            multilineBuffer = codeToExecute;
            continue;
          } else {
            // If we're already in multiline mode and still have syntax errors,
            // show the error and reset
            String errorMsg = e.toString();
            if (errorMsg.startsWith('Exception: ')) {
              errorMsg = errorMsg.substring('Exception: '.length);
            }
            customPrint('stdin:1: $errorMsg');
            multilineBuffer = null;
            continue;
          }
        } else {
          // Other errors
          String errorMsg = e.toString();
          if (errorMsg.startsWith('Exception: ')) {
            errorMsg = errorMsg.substring('Exception: '.length);
          }
          customPrint('stdin:1: $errorMsg');
          continue;
        }
      }

      // Print the result
      customPrint('= ${_formatValue(result)}');
    } catch (e, stack) {
      customPrint('Error: $e');
      if (debugMode) {
        customPrint('Stack trace: $stack');
      }
      // Reset multiline buffer on error
      multilineBuffer = null;
    }
  }

  // Save history to file before exiting
  try {
    history.saveToFile();
  } catch (e) {
    if (debugMode) {
      customPrint('Error saving history: $e');
    }
  }

  customPrint('\nGoodbye!');
}