<commit_before>require 'fileutils'
require_relative 'rfd/commands'
require_relative 'rfd/item'

module Rfd
  VERSION = Gem.loaded_specs['rfd'].version.to_s

  def self.start(dir = '.')
    [Curses::COLOR_WHITE, Curses::COLOR_CYAN, Curses::COLOR_MAGENTA, Curses::COLOR_GREEN, Curses::COLOR_RED].each do |c|
      Curses.init_pair c, c, Curses::COLOR_BLACK
    end

    Rfd::MainWindow.new dir
  end

  def self.maxx
    Curses.getmaxx Curses.stdscr
  end

  def self.maxy
    Curses.getmaxy Curses.stdscr
  end

  class Window
    attr_reader :window

    def wmove(y, x = 0)
      Curses.wmove window, y, x
    end

    def waddstr(str, clear_to_eol_before_add: false)
      wclrtoeol if clear_to_eol_before_add
      Curses.waddstr window, str
    end

    def mvwaddstr(y, x, str)
      Curses.mvwaddstr window, y, x, str
    end

    def wclear
      Curses.wclear window
    end

    def wrefresh
      Curses.wrefresh window
    end

    def maxx
      Curses.getmaxx window
    end

    def maxy
      Curses.getmaxy window
    end

    def begx
      Curses.getbegx window
    end

    def begy
      Curses.getbegy window
    end

    def subwin(height, width, top, left)
      Curses.derwin Curses.stdscr, height, width, top, left
    end

    def wclrtoeol
      Curses.wclrtoeol window
    end
  end

  # bordered Window
  class SubWindow < Window
    def initialize(*)
      border_window = subwin maxy + 2, maxx + 2, begy - 1, begx - 1
      Curses.box border_window, 0, 0
    end
  end

  class HeaderLeftWindow < SubWindow
    def initialize
      @window = subwin 3, Rfd.maxx - 32, 1, 1
      super
    end

    def draw_path_and_page_number(path: nil, current: 1, total: nil)
      @path_and_page_number = %Q[Page: #{"#{current}/ #{total}".ljust(11)}  Path: #{path}]
      wmove 0
      waddstr @path_and_page_number, clear_to_eol_before_add: true
    end

    def draw_current_file_info(current_file)
      draw_current_filename current_file.full_display_name
      draw_stat current_file
    end

    def draw_current_filename(current_file_name)
      @current_file_name = "File: #{current_file_name}"
      wmove 1
      waddstr @current_file_name, clear_to_eol_before_add: true
    end

    def draw_stat(item)
      @stat = "      #{item.size_or_dir.ljust(13)}#{item.mtime} #{item.mode}"
      wmove 2
      waddstr @stat, clear_to_eol_before_add: true
    end
  end

  class HeaderRightWindow < SubWindow
    def initialize
      @window = subwin 3, 29, 1, Rfd.maxx - 30
      super
    end

    def draw_marked_items(count: 0, size: 0)
      wmove 1
      waddstr %Q[#{"#{count}Marked".rjust(11)} #{size.to_s.reverse.gsub( /(\d{3})(?=\d)/, '\1,').reverse.rjust(16)}], clear_to_eol_before_add: true
    end

    def draw_total_items(count: 0, size: 0)
      wmove 2
      waddstr %Q[#{"#{count}Files".rjust(10)} #{size.to_s.reverse.gsub( /(\d{3})(?=\d)/, '\1,').reverse.rjust(17)}], clear_to_eol_before_add: true
    end

    def debug(s)
      wmove 0, 0
      wclrtoeol
      waddstr s.to_s
    end
  end

  class MainWindow < Window
    include Rfd::Commands

    class Panes
      attr_reader :current_index

      def initialize(panes)
        @panes, @current_index = panes, 0
      end

      def active
        @panes[@current_index]
      end

      def switch(index)
        @current_index = index if index < @panes.size
      end

      def size
        @panes.size
      end

      def clear_all
        @panes.each do |p|
          Curses.wclear p
          Curses.wrefresh p
        end
      end
    end

    attr_reader :header_l, :header_r, :command_line

    def initialize(dir = '.')
      @header_l = HeaderLeftWindow.new
      @header_r = HeaderRightWindow.new
      @command_line = CommandLineWindow.new

      spawn_panes 2
      border_window = subwin Rfd.maxy - 5, Rfd.maxx, 4, 0
      Curses.box border_window, 0, 0
      @row = 0

      cd dir
      ls
    end

    def run
      loop do
        case (c = Curses.getch)
        when 10  # enter
          enter
        when 27  # esc
          q
        when 32  # space
          space
        when 127  # DEL
          del
        when 258  # down
          j
        when 259  # up
          k
        when 260  # left
          h
        when 261  # right
          l
        when Curses::KEY_CTRL_A..Curses::KEY_CTRL_Z
          chr = ((c - 1 + 65) ^ 0b0100000).chr
          public_send "ctrl_#{chr}" if respond_to?("ctrl_#{chr}")
        else
          if respond_to? c.chr
            public_send c.chr
          else
            debug "key: #{c}"
          end
        end
      end
    end

    def spawn_panes(num)
      width = (Rfd.maxx - 2) / num<commit_msg>Use defined key code constants rather than magic numbers<commit_after>require 'fileutils'
require_relative 'rfd/commands'
require_relative 'rfd/item'

module Rfd
  VERSION = Gem.loaded_specs['rfd'].version.to_s

  def self.start(dir = '.')
    [Curses::COLOR_WHITE, Curses::COLOR_CYAN, Curses::COLOR_MAGENTA, Curses::COLOR_GREEN, Curses::COLOR_RED].each do |c|
      Curses.init_pair c, c, Curses::COLOR_BLACK
    end

    Rfd::MainWindow.new dir
  end

  def self.maxx
    Curses.getmaxx Curses.stdscr
  end

  def self.maxy
    Curses.getmaxy Curses.stdscr
  end

  class Window
    attr_reader :window

    def wmove(y, x = 0)
      Curses.wmove window, y, x
    end

    def waddstr(str, clear_to_eol_before_add: false)
      wclrtoeol if clear_to_eol_before_add
      Curses.waddstr window, str
    end

    def mvwaddstr(y, x, str)
      Curses.mvwaddstr window, y, x, str
    end

    def wclear
      Curses.wclear window
    end

    def wrefresh
      Curses.wrefresh window
    end

    def maxx
      Curses.getmaxx window
    end

    def maxy
      Curses.getmaxy window
    end

    def begx
      Curses.getbegx window
    end

    def begy
      Curses.getbegy window
    end

    def subwin(height, width, top, left)
      Curses.derwin Curses.stdscr, height, width, top, left
    end

    def wclrtoeol
      Curses.wclrtoeol window
    end
  end

  # bordered Window
  class SubWindow < Window
    def initialize(*)
      border_window = subwin maxy + 2, maxx + 2, begy - 1, begx - 1
      Curses.box border_window, 0, 0
    end
  end

  class HeaderLeftWindow < SubWindow
    def initialize
      @window = subwin 3, Rfd.maxx - 32, 1, 1
      super
    end

    def draw_path_and_page_number(path: nil, current: 1, total: nil)
      @path_and_page_number = %Q[Page: #{"#{current}/ #{total}".ljust(11)}  Path: #{path}]
      wmove 0
      waddstr @path_and_page_number, clear_to_eol_before_add: true
    end

    def draw_current_file_info(current_file)
      draw_current_filename current_file.full_display_name
      draw_stat current_file
    end

    def draw_current_filename(current_file_name)
      @current_file_name = "File: #{current_file_name}"
      wmove 1
      waddstr @current_file_name, clear_to_eol_before_add: true
    end

    def draw_stat(item)
      @stat = "      #{item.size_or_dir.ljust(13)}#{item.mtime} #{item.mode}"
      wmove 2
      waddstr @stat, clear_to_eol_before_add: true
    end
  end

  class HeaderRightWindow < SubWindow
    def initialize
      @window = subwin 3, 29, 1, Rfd.maxx - 30
      super
    end

    def draw_marked_items(count: 0, size: 0)
      wmove 1
      waddstr %Q[#{"#{count}Marked".rjust(11)} #{size.to_s.reverse.gsub( /(\d{3})(?=\d)/, '\1,').reverse.rjust(16)}], clear_to_eol_before_add: true
    end

    def draw_total_items(count: 0, size: 0)
      wmove 2
      waddstr %Q[#{"#{count}Files".rjust(10)} #{size.to_s.reverse.gsub( /(\d{3})(?=\d)/, '\1,').reverse.rjust(17)}], clear_to_eol_before_add: true
    end

    def debug(s)
      wmove 0, 0
      wclrtoeol
      waddstr s.to_s
    end
  end

  class MainWindow < Window
    include Rfd::Commands

    class Panes
      attr_reader :current_index

      def initialize(panes)
        @panes, @current_index = panes, 0
      end

      def active
        @panes[@current_index]
      end

      def switch(index)
        @current_index = index if index < @panes.size
      end

      def size
        @panes.size
      end

      def clear_all
        @panes.each do |p|
          Curses.wclear p
          Curses.wrefresh p
        end
      end
    end

    attr_reader :header_l, :header_r, :command_line

    def initialize(dir = '.')
      @header_l = HeaderLeftWindow.new
      @header_r = HeaderRightWindow.new
      @command_line = CommandLineWindow.new

      spawn_panes 2
      border_window = subwin Rfd.maxy - 5, Rfd.maxx, 4, 0
      Curses.box border_window, 0, 0
      @row = 0

      cd dir
      ls
    end

    def run
      loop do
        case (c = Curses.getch)
        when Curses::KEY_RETURN
          enter
        when Curses::KEY_ESCAPE
          q
        when 32  # space
          space
        when 127  # DEL
          del
        when Curses::KEY_DOWN
          j
        when Curses::KEY_UP
          k
        when Curses::KEY_LEFT
          h
        when Curses::KEY_RIGHT
          l
        when Curses::KEY_CTRL_A..Curses::KEY_CTRL_Z
          chr = ((c - 1 + 65) ^ 0b0100000).chr
          public_send "ctrl_#{chr}" if respond_to?("ctrl_#{chr}")
        else
          if respond_to? c.chr
            public_send c.chr
          else
            debug "key: #{c}"
          end
        end
      end
    end

    def spawn_panes(num)
      width = (Rfd.maxx - 2) / num
{'fraction_non_alphanumeric': 0.09169054441260745, 'fraction_numerical': 0.016767483816194417, 'mean_word_length': 2.074714518760196, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 3, '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': '590136', 'n_tokens_mistral': 3589, 'n_tokens_neox': 3386, 'n_words': 815}