diff --git a/lib/jekyll/watcher.rb b/lib/jekyll/watcher.rb index ea49ad6..5e8d50b 100644 --- a/lib/jekyll/watcher.rb +++ b/lib/jekyll/watcher.rb @@ -43,25 +43,34 @@ def watch(options, site = nil) private def build_listener(site, options) + ignore_regexps, ignore_paths = listen_ignore_paths(options) + Listen.to( options["source"], - :ignore => listen_ignore_paths(options), + :ignore => ignore_regexps, :force_polling => options["force_polling"], - &listen_handler(site) + &listen_handler(site, ignore_paths) ) end - def listen_handler(site) + def listen_handler(site, ignore_paths) proc do |modified, added, removed| - t = Time.now + modified = strip_ignore_paths(modified, ignore_paths) + added = strip_ignore_paths(added, ignore_paths) + removed = strip_ignore_paths(removed, ignore_paths) + c = modified + added + removed - n = c.length - Jekyll.logger.info "Regenerating:", - "#{n} file(s) changed at #{t.strftime("%Y-%m-%d %H:%M:%S")}" + unless c.empty? + t = Time.now + n = c.length + + Jekyll.logger.info "Regenerating:", + "#{n} file(s) changed at #{t.strftime("%Y-%m-%d %H:%M:%S")}" - c.each { |path| Jekyll.logger.info "", path["#{site.source}/".length..-1] } - process(site, t) + c.each { |path| Jekyll.logger.info "", path["#{site.source}/".length..-1] } + process(site, t) + end end end @@ -74,6 +83,10 @@ def normalize_encoding(obj, desired_encoding) end end + # paths that user specified to be excluded in their _config.yml file. + # could be nil, a single string or an array of strings. + # + # Returns an array of paths joined to the src directory. def custom_excludes(options) Array(options["exclude"]).map { |e| Jekyll.sanitized_path(options["source"], e) } end @@ -89,34 +102,54 @@ def to_exclude(options) config_files(options), options["destination"], custom_excludes(options), - ].flatten + ].flat_map { |e| normalize_encoding(e, options["source"].encoding) } end # Paths to ignore for the watch option # # options - A Hash of options passed to the command # - # Returns a list of relative paths from source that should be ignored + # Returns a tuple where the first entry is a list of regular + # expressions relating to exact (existing) files to + # ignore and the second is a list of fnmatch patterns + # to ignore. def listen_ignore_paths(options) source = Pathname.new(options["source"]).expand_path - paths = to_exclude(options) + exclusion_fnmatch_paths = [] - paths.map do |p| - absolute_path = Pathname.new(normalize_encoding(p, options["source"].encoding)).expand_path - next unless absolute_path.exist? + exclusion_regexps = to_exclude(options).map do |path| + # convert to absolute path from the source directory + absolute_path = Pathname.new(path).expand_path + relative_path = absolute_path.relative_path_from(source).to_s - begin - relative_path = absolute_path.relative_path_from(source).to_s + if absolute_path.exist? relative_path = File.join(relative_path, "") if absolute_path.directory? - unless relative_path.start_with?("../") - path_to_ignore = %r!^#{Regexp.escape(relative_path)}! - Jekyll.logger.debug "Watcher:", "Ignoring #{path_to_ignore}" - path_to_ignore + + begin + next if relative_path.start_with?("../") + + %r!^#{Regexp.escape(relative_path)}!.tap do |pattern| + Jekyll.logger.debug "Watcher:", "Ignoring #{pattern}" + end + rescue ArgumentError + # Could not find a relative path end - rescue ArgumentError - # Could not find a relative path + else + # maybe wildcard, or just a file that doesn't exist. + nil.tap { exclusion_fnmatch_paths << relative_path } end end.compact + [%r!^\.jekyll\-metadata!] + + [exclusion_regexps, exclusion_fnmatch_paths] + end + + # remove any paths from PATHS which're matched to by some fnmatch + # pattern in IGNORE_PATTERNS + def strip_ignore_paths(paths, ignore_patterns) + paths.select do |path| + # true when every patten in ignore_patterns does not match to path. + ignore_patterns.find { |pattern| File.fnmatch?(pattern, path) }.nil? + end end def sleep_forever diff --git a/spec/watcher_spec.rb b/spec/watcher_spec.rb index 98af237..76b6a57 100644 --- a/spec/watcher_spec.rb +++ b/spec/watcher_spec.rb @@ -1,3 +1,4 @@ +# coding: utf-8 require "spec_helper" describe(Jekyll::Watcher) do @@ -97,7 +98,7 @@ end context "#listen_ignore_paths" do - let(:ignored) { subject.send(:listen_ignore_paths, options) } + let(:ignored) { subject.send(:listen_ignore_paths, options)[0] } let(:metadata_path) { Jekyll.sanitized_path(options["source"], ".jekyll-metadata") } before(:each) { FileUtils.touch(metadata_path) }