<commit_before>#!/usr/bin/env ruby

#
#  editor.rb
#
# Created by <NAME> on 2008-03-01.
# Copyright (c) 2008 <NAME>. All rights reserved.
#
# This is Free Software.  See LICENSE for details.
#

require 'forwardable'
require 'terminal_layout'
require 'ansi_string'
require 'term/ansicolor'

module RawLine

  #
  # The Editor class defines methods to:
  #
  # * Read characters from STDIN or any type of input
  # * Write characters to STDOUT or any type of output
  # * Bind keys to specific actions
  # * Perform line-related operations like moving, navigating through history, etc.
  #
  # Note that the following default key bindings are provided:
  #
  # * TAB: word completion defined via completion_proc
  # * LEFT/RIGHT ARROWS: cursor movement (left/right)
  # * UP/DOWN ARROWS: history navigation
  # * DEL: Delete character under cursor
  # * BACKSPACE: Delete character before cursor
  # * INSERT: Toggle insert/replace mode (default: insert)
  # * CTRL+K: Clear the whole line
  # * CTRL+Z: undo (unless already registered by the OS)
  # * CTRL+Y: redo (unless already registered by the OS)
  #
  class Editor
    extend Forwardable
    include HighLine::SystemExtensions

    attr_accessor :char, :history_size, :line_history_size, :highlight_history_matching_text
    attr_accessor :terminal, :keys, :mode
    attr_accessor :completion_class, :completion_proc, :line, :history, :completion_append_string
    attr_accessor :match_hidden_files
    attr_accessor :word_break_characters
    attr_reader :output
    attr_accessor :dom

    # TODO: dom traversal for lookup rather than assignment
    attr_accessor :prompt_box, :input_box, :content_box

    def puts(*args)
      # @output.puts *args
    end

    def print(*args)
      # @output.print *args
    end

    def preserve_cursor(&blk)
      # @output.synchronize do
        begin
          # @output.print @terminal.term_info.control_string("sc")
          blk.call
        ensure
          # @output.print @terminal.term_info.control_string("rc")
        end
      # end
    end

    #
    # Create an instance of RawLine::Editor which can be used
    # to read from input and perform line-editing operations.
    # This method takes an optional block used to override the
    # following instance attributes:
    # * <tt>@history_size</tt> - the size of the editor history buffer (30).
    # * <tt>@line_history_size</tt> - the size of the editor line history buffer (50).
    # * <tt>@keys</tt> - the keys (arrays of character codes) bound to specific actions.
    # * <tt>@word_break_characters</tt> - a regex used for word separation, default inclues: " \t\n\"\\'`@$><=;|&{("
    # * <tt>@mode</tt> - The editor's character insertion mode (:insert).
    # * <tt>@completion_proc</tt> - a Proc object used to perform word completion.
    # * <tt>@completion_append_string</tt> - a string to append to completed words ('').
    # * <tt>@terminal</tt> -  a RawLine::Terminal containing character key codes.
    #
    def initialize(input=STDIN, output=STDOUT)
      @input = input
      # @output = output

      case RUBY_PLATFORM
      when /mswin/i then
        @terminal = WindowsTerminal.new
        if RawLine.win32console? then
          @win32_io = Win32::Console::ANSI::IO.new
        end
      else
        @terminal = VT220Terminal.new
      end
      @history_size = 30
      @line_history_size = 50
      @keys = {}
      @word_break_characters = " \t\n\"'@\$><=;|&{("
      @mode = :insert
      @completion_class = Completer
      @completion_proc = filename_completion_proc
      @completion_append_string = ''
      @match_hidden_files = false
      set_default_keys
      @add_history = false
      @highlight_history_matching_text = true
      @history = HistoryBuffer.new(@history_size) do |h|
        h.duplicates = false;
        h.exclude = lambda { |item| item.strip == "" }
      end
      @keyboard_input_processors = [self]
      yield self if block_given?
      update_word_separator
      @char = nil

      @event_registry = Rawline::EventRegistry.new do |registry|
        registry.subscribe :default, -> (_) { self.check_for_keyboard_input }
        registry.subscribe :dom_tree_change, -> (_) { self.render }
      end
      @event_loop = Rawline::EventLoop.new(registry: @event_registry)

      @dom ||= build_dom_tree
      @renderer ||= build_renderer

      initialize_line
    end

    attr_reader :dom

    def events
      @event_loop
    end

    #
    # Return the current RawLine version
    #
    def library_version
      "RawLine v#{RawLine.rawline_version}"
    end

    def prompt
      @line.prompt if @line
    end

    def prompt=(text)
      return if !@allow_prompt_updates || @line.nil? || @line.prompt == text
      @prompt_box.content = Prompt.new(text)
    end

    def initialize_line
      @input_box.content = ""
      update_word_separator
      @add_history = true #add_history
      @line = Line.new(@line_history_size) do |l|
        l.prompt = @prompt_box.content
        l.word_separator = @word_separator
      end
      add_to_line_history
      @allow_prompt_updates = true
    end

    def reset_line
      initialize_line
      render(reset: true)
    end

    def check_for_keyboard_input
      bytes = []
      begin
        loop do
          bytes << @input.read_nonblock(1)
        end
      rescue IO::WaitReadable
        @keyboard_input_processors.last.read_bytes(bytes)

        IO.select([@input], [], [], 0.01)
        @event_loop.add_event name: 'check_for_keyboard_input', source: self
      end
    end<commit_msg>Restore file descriptor flags after calling read_nonblock on STDIN.<commit_after>#!/usr/bin/env ruby

#
#  editor.rb
#
# Created by <NAME> on 2008-03-01.
# Copyright (c) 2008 <NAME>. All rights reserved.
#
# This is Free Software.  See LICENSE for details.
#

require 'forwardable'
require 'terminal_layout'
require 'ansi_string'
require 'term/ansicolor'
require 'fcntl'

module RawLine

  #
  # The Editor class defines methods to:
  #
  # * Read characters from STDIN or any type of input
  # * Write characters to STDOUT or any type of output
  # * Bind keys to specific actions
  # * Perform line-related operations like moving, navigating through history, etc.
  #
  # Note that the following default key bindings are provided:
  #
  # * TAB: word completion defined via completion_proc
  # * LEFT/RIGHT ARROWS: cursor movement (left/right)
  # * UP/DOWN ARROWS: history navigation
  # * DEL: Delete character under cursor
  # * BACKSPACE: Delete character before cursor
  # * INSERT: Toggle insert/replace mode (default: insert)
  # * CTRL+K: Clear the whole line
  # * CTRL+Z: undo (unless already registered by the OS)
  # * CTRL+Y: redo (unless already registered by the OS)
  #
  class Editor
    extend Forwardable
    include HighLine::SystemExtensions

    attr_accessor :char, :history_size, :line_history_size, :highlight_history_matching_text
    attr_accessor :terminal, :keys, :mode
    attr_accessor :completion_class, :completion_proc, :line, :history, :completion_append_string
    attr_accessor :match_hidden_files
    attr_accessor :word_break_characters
    attr_reader :output
    attr_accessor :dom

    # TODO: dom traversal for lookup rather than assignment
    attr_accessor :prompt_box, :input_box, :content_box

    def puts(*args)
      # @output.puts *args
    end

    def print(*args)
      # @output.print *args
    end

    def preserve_cursor(&blk)
      # @output.synchronize do
        begin
          # @output.print @terminal.term_info.control_string("sc")
          blk.call
        ensure
          # @output.print @terminal.term_info.control_string("rc")
        end
      # end
    end

    #
    # Create an instance of RawLine::Editor which can be used
    # to read from input and perform line-editing operations.
    # This method takes an optional block used to override the
    # following instance attributes:
    # * <tt>@history_size</tt> - the size of the editor history buffer (30).
    # * <tt>@line_history_size</tt> - the size of the editor line history buffer (50).
    # * <tt>@keys</tt> - the keys (arrays of character codes) bound to specific actions.
    # * <tt>@word_break_characters</tt> - a regex used for word separation, default inclues: " \t\n\"\\'`@$><=;|&{("
    # * <tt>@mode</tt> - The editor's character insertion mode (:insert).
    # * <tt>@completion_proc</tt> - a Proc object used to perform word completion.
    # * <tt>@completion_append_string</tt> - a string to append to completed words ('').
    # * <tt>@terminal</tt> -  a RawLine::Terminal containing character key codes.
    #
    def initialize(input=STDIN, output=STDOUT)
      @input = input
      # @output = output

      case RUBY_PLATFORM
      when /mswin/i then
        @terminal = WindowsTerminal.new
        if RawLine.win32console? then
          @win32_io = Win32::Console::ANSI::IO.new
        end
      else
        @terminal = VT220Terminal.new
      end
      @history_size = 30
      @line_history_size = 50
      @keys = {}
      @word_break_characters = " \t\n\"'@\$><=;|&{("
      @mode = :insert
      @completion_class = Completer
      @completion_proc = filename_completion_proc
      @completion_append_string = ''
      @match_hidden_files = false
      set_default_keys
      @add_history = false
      @highlight_history_matching_text = true
      @history = HistoryBuffer.new(@history_size) do |h|
        h.duplicates = false;
        h.exclude = lambda { |item| item.strip == "" }
      end
      @keyboard_input_processors = [self]
      yield self if block_given?
      update_word_separator
      @char = nil

      @event_registry = Rawline::EventRegistry.new do |registry|
        registry.subscribe :default, -> (_) { self.check_for_keyboard_input }
        registry.subscribe :dom_tree_change, -> (_) { self.render }
      end
      @event_loop = Rawline::EventLoop.new(registry: @event_registry)

      @dom ||= build_dom_tree
      @renderer ||= build_renderer

      initialize_line
    end

    attr_reader :dom

    def events
      @event_loop
    end

    #
    # Return the current RawLine version
    #
    def library_version
      "RawLine v#{RawLine.rawline_version}"
    end

    def prompt
      @line.prompt if @line
    end

    def prompt=(text)
      return if !@allow_prompt_updates || @line.nil? || @line.prompt == text
      @prompt_box.content = Prompt.new(text)
    end

    def initialize_line
      @input_box.content = ""
      update_word_separator
      @add_history = true #add_history
      @line = Line.new(@line_history_size) do |l|
        l.prompt = @prompt_box.content
        l.word_separator = @word_separator
      end
      add_to_line_history
      @allow_prompt_updates = true
    end

    def reset_line
      initialize_line
      render(reset: true)
    end

    def check_for_keyboard_input
      bytes = []
      begin
        file_descriptor_flags = @input.fcntl(Fcntl::F_GETFL, 0)
        loop do
          bytes << @input.read_nonblock(1)
        end
      rescue IO::WaitReadable
        # reset flags so O_NONBLOCK is turned off on the file descriptor
        # if it was turned on during the read_nonblock above
        @input.fcntl(Fcntl::F_SETFL, file_descriptor_flags)
        @keyboard_input_processors.last.read_bytes(bytes)

        IO.select([@input], [], [], 0.01)
        @event_loop.add_event name: 'check_for_keyboard_input', source: self
      end
    end
{'fraction_non_alphanumeric': 0.098535054063481, 'fraction_numerical': 0.00584234391349843, 'mean_word_length': 2.787648612945839, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 47, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '196901', 'n_tokens_mistral': 3679, 'n_tokens_neox': 3491, 'n_words': 960}