<commit_before>=begin
= VimMate: Vim graphical add-on
Copyright (c) 2006 <NAME>

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
=end

require 'set'
require 'vimmatelib/icons'

module VimMate

  # A file within the tree
  class ListedFile
    @@all_by_path = Hash.new
    attr_reader :name, :path, :parent
    attr_accessor :row

    # Create a ListedFile from a path and an optional parent ListedDirectory.
    def initialize(path, parent = nil)
      @path = File.expand_path path
      @name = File.basename(path)
      # FIXME or find parent by parent path
      @parent = parent
      if @parent && !@parent.is_a?(ListedDirectory)
        raise ArgumentError, "parent is no ListedDirectory" 
      end
      self.class.register self
      @last_status = nil
    end

    # Refresh the file. Doesn't do anything since it's the directory
    # that does the job.
    def refresh
      self
    end

    # The type of icon to use
    def icon_type
      :file
    end

    # Returns the icon for this file
    def icon
      Icons.file_icon
    end

    # Returns the status text for this file
    def status_text
      ""
    end

    def exists?
      File.exists? path
    end

    def self.register(listed_file)
      @@all_by_path[listed_file.path] = listed_file
    end
    def self.unregister(listed_file)
      @@all_by_path.delete listed_file
    end
    def self.all
      @@all_by_path.values
    end
    def self.all_paths
      @@all_by_path.keys
    end
    def self.find_by_path(path)
      @@all[path]
    end
  end

  # A directory within the tree. Can contain files and other directories.
  class ListedDirectory < ListedFile
    # Create a ListedDirectory from a path and an optional parent.
    def initialize(path, parent = nil)
      super(path, parent)
      @files = Set.new
      refresh
    end

    # The type of icon to use
    def icon_type
      :folder
    end    

    # Yield each files and directory within this directory
    def each_file(&block)
      @files.each(&block)
      self
    end

    def files_count
      @files.length
    end

    # Refresh the files from this directory. If it doesn't exist, the
    # file is removed. If it didn't exist before, the file is added.
    def refresh
      super
      remove_not_existing_files

      self
    end

    # Find files to add
    def add_new_files
      begin
        Dir.foreach(path) do |file|
          # Skip hidden files
          next if file =~ /^\./
          path = File.join(path, file)

          # Skip files that we already have
          next if all_paths.include? path
          next if ListedTree.should_exclude? path

          add_new_file_or_directory
        end
      rescue Errno::ENOENT
      end
    end

    def add_new_file_or_directory(path)
      @files << if File.directory? path
                  ListedDirectory.new(path, self)
                else
                  ListedFile.new(path, self)
                end
    end

    # Find files that do not exist (anymore) and remove them
    def remove_not_existing_files
      files_to_remove = Set.new
      all_paths = Set.new
      each_file do |file|
        file.refresh
        if file.exists?
          all_paths << file.path
        else
          files_to_remove << file
          # @tree_signal.call(:remove, file)
        end
      end
      @files -= files_to_remove
    end
  end

  # A tree of files and directory. Can signal added and removed files.
  class ListedTree
    include Enumerable<commit_msg>adding all dirs first (brait)<commit_after>=begin
= VimMate: Vim graphical add-on
Copyright (c) 2006 <NAME>

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
=end

require 'set'
require 'vimmatelib/icons'

module VimMate

  # A file within the tree
  class ListedFile
    @@all_by_path = Hash.new
    attr_reader :name, :path, :parent
    attr_accessor :row

    # Create a ListedFile from a path and an optional parent ListedDirectory.
    def initialize(new_path, parent = nil)
      @path = File.expand_path new_path
      @name = File.basename(path)
      # FIXME or find parent by parent path
      @parent = parent
      if @parent && !@parent.is_a?(ListedDirectory)
        raise ArgumentError, "parent is no ListedDirectory" 
      end
      self.class.register self
      @last_status = nil
    end

    # Refresh the file. Doesn't do anything since it's the directory
    # that does the job.
    def refresh
      self
    end

    # The type of icon to use
    def icon_type
      :file
    end

    # Returns the icon for this file
    def icon
      Icons.file_icon
    end

    # Returns the status text for this file
    def status_text
      ""
    end

    def exists?
      File.exists? path
    end

    def self.register(listed_file)
      @@all_by_path[listed_file.path] = listed_file
    end
    def self.unregister(listed_file)
      @@all_by_path.delete listed_file
    end
    def self.all
      @@all_by_path.values
    end
    def self.all_paths
      @@all_by_path.keys
    end
    def self.find_by_path(path)
      @@all[path]
    end
  end

  # A directory within the tree. Can contain files and other directories.
  class ListedDirectory < ListedFile
    # Create a ListedDirectory from a path and an optional parent.
    def initialize(path, parent = nil)
      super(path, parent)
      @files = Set.new
    end

    # The type of icon to use
    def icon_type
      :folder
    end    

    # Yield each files and directory within this directory
    def each_file(&block)
      @files.each(&block)
      self
    end

    def each_directory(&block)
      @files.select {|f| f.is_a? ListedDirectory }.each(&block)
    end

    def files_count
      @files.length
    end

    # Refresh the files from this directory. If it doesn't exist, the
    # file is removed. If it didn't exist before, the file is added.
    def refresh
      super
      remove_not_existing_files
      add_new_files
      # refresh subdirs. files must not be refreshed this way
      each_directory do |dir|
        dir.refresh
      end

      self
    end

    # Find files to add
    def add_new_files
      begin
        Dir.foreach(path) do |file|
          # Skip hidden files
          next if file =~ /^\./
          file_path = File.join(path, file)

          # Skip files that we already have
          next if self.class.all_paths.include? file_path
          next if ListedTree.should_exclude? file_path

          add_new_file_or_directory file_path
        end
      rescue Errno::ENOENT
      end
    end

    def add_new_file_or_directory(file_path)
      @files << if File.directory? file_path
                  ListedDirectory.new(file_path, self)
                else
                  ListedFile.new(file_path, self)
                end
    end

    # Find files that do not exist (anymore) and remove them
    def remove_not_existing_files
      files_to_remove = Set.new
      all_paths = Set.new
      each_file do |file|
        file.refresh
        if file.exists?
          all_paths << file.path
        else
          files_to_remove << file
          # @tree_signal.call(:remove, file)
        end
      end
      @files -= files_to_remove
    end
  end

  # A tree of files and directory. Can signal added and removed files.
  class ListedTree
    include Enumerable
{'fraction_non_alphanumeric': 0.05644463667820069, 'fraction_numerical': 0.0008650519031141869, 'mean_word_length': 2.419223659889094, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 5, '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': '7355810', 'n_tokens_mistral': 2699, 'n_tokens_neox': 2584, 'n_words': 1038}