# frozen_string_literal: true require "dnote/core_ext" require "dnote/notes" require "dnote/format" require "dnote/options" module DNote # User session which is used by commandline interface. # # By making this a class it makes it easy for external # libraries to use this library just as if they were # calling the commandline, but without the need to shellout. # class Session # Directory relative to this script. This is used # to lookup the available format templates. DIR = File.dirname(__FILE__) # Default format. DEFAULT_FORMAT = "text" # Default title. DEFAULT_TITLE = "Developer's Notes" # Paths to include. attr_accessor :paths # Paths to exclude (match by pathname). attr_reader :exclude # Paths to ignore (match by basename). attr_reader :ignore # Labels to lookup. # By default these are TODO, FIXME and OPTIMIZE. attr_accessor :labels # Selected labels can optionally do without the colon. attr_accessor :colon # Alternate remark marker. Useful to other languages besides Ruby. attr_accessor :marker # Output format. attr_accessor :format # If custom format, specify template file. attr_accessor :template # Some format put a title at the top of the output. # The default is "Developer's Notes". attr_accessor :title # Output to a file instead of STDOUT. attr_accessor :output # If output path given, don't actually write to disk. attr_accessor :dryrun # String template for line URLs (mainly for HTML format). For example, # DNote uses GitHub so we could use a link template: # # "https://github.com/rubyworks/dnote/blob/master/%s#L%s" # attr_accessor :url # Number of lines of context to display. The default is zero. attr_accessor :context private # New Session. def initialize(options = {}) options ||= {} initialize_defaults options.each { |k, v| __send__("#{k}=", v) } yield(self) if block_given? end # Set default values for attributes. def initialize_defaults @paths = [] @labels = [] @exclude = [] @ignore = [] @format = DEFAULT_FORMAT @title = DEFAULT_TITLE @dryrun = false @marker = nil @url = nil @context = 0 end public # Set exclude list ensuring that the value is an array. def exclude=(list) @exclude = [list].compact.flatten.compact end # Set ignore list ensuring that the value is an array. def ignore=(list) @ignore = [list].compact.flatten.compact end # Run session. def run notes = Notes.new(files, labels: labels, colon: colon, marker: marker, url: url, context: context) collection = notes.notes_collection formatter = Format.new(collection, format: format, template: template, title: title, output: output) formatter.render end # Collect path globs and remove exclusions. # This method uses #paths, #exclude and #ignore to # compile the list of files. def files list = [paths].flatten.compact list = ["**/*.rb"] if list.empty? list = glob(list) list -= glob(exclude) list.reject do |path| path.split("/").any? { |part| ignore.any? { |ig| File.fnmatch?(ig, part) } } end end # Collect the file glob of each path given. If # a path is a directory, inclue all content. def glob(paths) paths.map do |path| if File.directory?(path) Dir.glob(File.join(path, "**/*")) else Dir.glob(path) end end.flatten.uniq end # List availble format templates def list_templates tdir = File.join(DIR, "templates") tfiles = Dir[File.join(tdir, "**/*.erb")] tnames = tfiles.map { |tname| tname.sub("#{tdir}/", "").chomp(".erb") } groups = tnames.group_by { |tname| tname.split("/").first } groups.sort.each do |(_type, names)| puts("%-18s " * names.size % names.sort) end end # Commandline interface. def self.main(*argv) session = Options.parse(*argv) session.run end end end