diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c81bf37..fa3deaf3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +### 2.1.87 + +2024-12-14 15:27 + +#### FIXED + +- Trying to fix runtime slowness + ### 2.1.86 2024-03-26 10:57 diff --git a/Gemfile.lock b/Gemfile.lock index a4b75f65..8f1aea71 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - doing (2.1.86) + doing (2.1.87) chronic (~> 0.10, >= 0.10.2) deep_merge (~> 1.2, >= 1.2.1) gli (~> 2.20, >= 2.20.1) @@ -22,12 +22,12 @@ GEM chronic (0.10.2) deep_merge (1.2.2) github-markup (4.0.2) - gli (2.21.1) + gli (2.22.0) haml (5.0.4) temple (>= 0.8.0) tilt - kramdown (2.4.0) - rexml + kramdown (2.5.1) + rexml (>= 3.3.9) parallel (1.24.0) parallel_tests (3.13.0) parallel @@ -39,23 +39,21 @@ GEM rake (13.0.6) rdoc (6.3.4.1) redcarpet (3.6.0) - rexml (3.3.6) - strscan - rouge (4.2.0) + rexml (3.3.9) + rouge (4.5.1) safe_yaml (1.0.5) strings (0.2.1) strings-ansi (~> 0.2) unicode-display_width (>= 1.5, < 3.0) unicode_utils (~> 1.4) strings-ansi (0.2.0) - strscan (3.1.0) temple (0.10.3) test-unit (3.4.9) power_assert - tilt (2.3.0) + tilt (2.4.0) tty-color (0.6.0) tty-cursor (0.7.1) - tty-link (0.1.1) + tty-link (0.2.0) tty-markdown (0.7.2) kramdown (>= 1.16.2, < 3.0) pastel (~> 0.8) @@ -63,7 +61,7 @@ GEM strings (~> 0.2.0) tty-color (~> 0.5) tty-screen (~> 0.8) - tty-progressbar (0.18.2) + tty-progressbar (0.18.3) strings-ansi (~> 0.2) tty-cursor (~> 0.7) tty-screen (~> 0.8) @@ -76,7 +74,7 @@ GEM tty-spinner (0.9.3) tty-cursor (~> 0.7) tty-which (0.5.0) - unicode-display_width (2.5.0) + unicode-display_width (2.6.0) unicode_utils (1.4.0) wisper (2.0.1) yard (0.9.36) diff --git a/README.md b/README.md index 6ce3f892..9412a76d 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ _If you're one of the rare people like me who find this useful, feel free to -The current version of `doing` is 2.1.85. +The current version of `doing` is 2.1.86. Find all of the documentation in the [doing wiki][wiki]. diff --git a/docs/doc/Array.html b/docs/doc/Array.html index 24f6f480..f2112dff 100644 --- a/docs/doc/Array.html +++ b/docs/doc/Array.html @@ -263,25 +263,7 @@
- - - -31 -32 -33- |
-
- # File 'lib/doing/array/array.rb', line 31 - -def cap_first - map(&:cap_first) -end- |
-
- - - -59 -60 -61- |
-
- # File 'lib/doing/good.rb', line 59 - -def good? - !nil? && !empty? -end- |
-
- - - -17 -18 -19 -20 -21 -22 -23 -24- |
-
- # File 'lib/doing/array/array.rb', line 17 - -def utf8 - c = self.class - if String.method_defined? :force_encoding - replace c.new(map(&:utf8)) - else - self - end -end- |
-
- - - -42 -43 -44 -45- |
-
- # File 'lib/doing/boolean_term_parser.rb', line 42 - -def initialize(operator, term) - self.operator = Operator.symbol(operator) - self.term = term -end- |
-
- - - -40 -41 -42- |
-
- # File 'lib/doing/boolean_term_parser.rb', line 40 - -def operator - @operator -end- |
-
- - - -40 -41 -42- |
-
- # File 'lib/doing/boolean_term_parser.rb', line 40 - -def term - @term -end- |
-
- - - -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36- |
-
- # File 'lib/doing/boolean_term_parser.rb', line 25 - -def self.symbol(str) - case str - when '+' - :must - when '-' - :must_not - when nil - :should - else - raise "Unknown operator: #{str}" - end -end- |
-
- - - -51 -52 -53 -54 -55 -56- |
-
- # File 'lib/doing/boolean_term_parser.rb', line 51 - -def initialize(clauses) - grouped = clauses.chunk { |c| c.operator }.to_h - self.should_terms = grouped.fetch(:should, []).map(&:term) - self.must_not_terms = grouped.fetch(:must_not, []).map(&:term) - self.must_terms = grouped.fetch(:must, []).map(&:term) -end- |
-
- - - -49 -50 -51- |
-
- # File 'lib/doing/boolean_term_parser.rb', line 49 - -def must_not_terms - @must_not_terms -end- |
-
- - - -49 -50 -51- |
-
- # File 'lib/doing/boolean_term_parser.rb', line 49 - -def must_terms - @must_terms -end- |
-
- - - -49 -50 -51- |
-
- # File 'lib/doing/boolean_term_parser.rb', line 49 - -def should_terms - @should_terms -end- |
-
- - - -82 -83 -84- |
-
- # File 'lib/doing/boolean_term_parser.rb', line 82 - -def match(term) - term -end- |
-
- - - -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80- |
-
- # File 'lib/doing/boolean_term_parser.rb', line 58 - -def to_elasticsearch - query = {} - - if should_terms.any? - query[:should] = should_terms.map do |term| - match(term) - end - end - - if must_terms.any? - query[:must] = must_terms.map do |term| - match(term) - end - end - - if must_not_terms.any? - query[:must_not] = must_not_terms.map do |term| - match(term) - end - end - - query -end- |
-
'2.1.86'
'2.1.87'
- - - -9 -10 -11- |
-
- # File 'lib/doing/array/cleanup.rb', line 9 - -def remove_bad - compact.map { |x| x.is_a?(String) ? x.strip : x }.select(&:good?) -end- |
-
- - - -13 -14 -15- |
-
- # File 'lib/doing/array/cleanup.rb', line 13 - -def remove_bad! - replace remove_empty -end- |
-
- - - -23 -24 -25- |
-
- # File 'lib/doing/array/cleanup.rb', line 23 - -def remove_empty - compact.map { |x| x.is_a?(String) ? x.strip : x }.reject { |x| x.is_a?(String) ? x.empty? : false } -end- |
-
- - - -27 -28 -29- |
-
- # File 'lib/doing/array/cleanup.rb', line 27 - -def remove_empty! - replace remove_empty -end- |
-
- - - -11 -12 -13 -14 -15- |
-
- # File 'lib/doing/array/nested_hash.rb', line 11 - -def nested_hash(value = nil) - hsh = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) } - hsh.dig(*self[0..-2])[fetch(-1)] = value - hsh -end- |
-
- - - -37 -38 -39- |
-
- # File 'lib/doing/array/tags.rb', line 37 - -def (color = 'cyan') - .map { |t| Doing::Color.send(color.to_sym, t) } -end- |
-
- - - -46 -47 -48- |
-
- # File 'lib/doing/array/tags.rb', line 46 - -def (color = 'cyan') - (color).join(', ') -end- |
-
Convert an array of tags to str -
- - - -15 -16 -17- |
-
- # File 'lib/doing/array/tags.rb', line 15 - -def - map(&:remove_at).map(&:strip) -end- |
-
Convert an array of strings wit -
- - - -25 -26 -27- |
-
- # File 'lib/doing/array/tags.rb', line 25 - -def - map(&:add_at) -end- |
-
Convert an array of strings wit
- - - -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65- |
-
- # File 'lib/doing/plugins/export/byday.rb', line 18 - -def self.render(wwid, items, variables: {}) - return if items.nil? - - days = {} - - items.each do |item| - date = item.date.strftime('%Y-%m-%d') - days[date] ||= [] - days[date].push(item) - end - - totals = {} - total = 0 - - days.each do |day, day_items| - day_items.each do |item| - totals[day] ||= 0 - duration = item.interval || 0 - totals[day] += duration - total += duration - end - end - width = wwid.config['plugins']['byday']['item_width'].to_i || 60 - divider = "{wd}+{xk}#{'-' *10}{wd}+{xk}#{'-' * width}{wd}+{xk}#{'-' * 8}{wd}+{x}" - out = [] - out << divider - out << "{wd}|{xm}date {wd}|{xbw}item#{' ' * (width - 4)}{wd}|{xy}duration{wd}|{x}" - out << divider - days.each do |day, day_items| - first = day_items.slice!(0, 1)[0] - interval = wwid.get_interval(first, formatted: true) || '00:00:00' - title = first.title.tag('done', remove: true).trunc(width - 2).ljust(width) - out << "{wd}|{xm}#{day}{wd}|{xbw}#{title}{wd}|{xy}#{interval}{wd}|{x}" - day_items.each do |item| - interval = wwid.get_interval(item, formatted: true) || '00:00:00' - title = item.title.tag('done', remove: true).trunc(width - 2).ljust(width) - out << "{wd}| |{xbw}#{title}{wd}|{xy}#{interval}{wd}|{x}" - end - day_total = "Total: #{totals[day].time_string(format: :clock)}" - out << divider - out << "{wd}|{xg}#{day_total.rjust(width + 20)}{wd}|{x}" - out << divider - end - all_total = "Grand Total: #{total.time_string(format: :clock)}" - out << "{wd}|{xrb}#{all_total.rjust(width + 20)}{wd}|{x}" - out << divider - Doing::Color.template(out.join("\n")) -end- |
-
- - - -9 -10 -11 -12 -13 -14 -15 -16- |
-
- # File 'lib/doing/plugins/export/byday.rb', line 9 - -def self.settings - { - trigger: 'byday', - config: { - 'item_width' => 60 - } - } -end- |
-
- - - -36 -37 -38 -39 -40 -41 -42 -43 -44- |
-
- # File 'lib/doing/plugins/export/csv_export.rb', line 36 - -def self.format_note(note) - out = '' - if note - arr = note.map(&:strip).delete_if { |e| e =~ /^\s*$/ } - out = arr.join("\n") unless arr.empty? - end - - out -end- |
-
- - - -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34- |
-
- # File 'lib/doing/plugins/export/csv_export.rb', line 20 - -def self.render(wwid, items, variables: {}) - return if items.nil? - - opt = variables[:options] - - output = [CSV.generate_line(%w[start end title note timer section])] - items.each do |i| - note = format_note(i.note) - end_date = i.end_date - interval = end_date && opt[:times] ? wwid.get_interval(i, formatted: false) : 0 - output.push(CSV.generate_line([i.date, end_date, i.title, note, interval, i.section])) - end - Doing.logger.debug('CSV Export:', "#{items.count} items output to CSV") - output.join('') -end- |
-
- - - -14 -15 -16 -17 -18- |
-
- # File 'lib/doing/plugins/export/csv_export.rb', line 14 - -def self.settings - { - trigger: 'csv' - } -end- |
-
- - - -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95- |
-
- # File 'lib/doing/plugins/import/calendar_import.rb', line 22 - -def self.import(wwid, _path, options: {}) - limit_start = [:start].to_i - limit_end = [:end].to_i - - section = [:section] || Doing.setting('current_section') - [:no_overlap] ||= false - [:autotag] ||= Doing.auto_tag - - wwid.content.add_section(section) unless wwid.content.section?(section) - - = [:tag] ? [:tag].split(/[ ,]+/).map { |t| t.sub(/^@?/, '') } : [] - prefix = [:prefix] || '[Calendar.app]' - - script = File.join(File.dirname(__FILE__), 'cal_to_json.scpt') - res = `/usr/bin/osascript "#{script}" #{limit_start} #{limit_end}`.strip - data = JSON.parse(res) - - new_items = [] - data.each do |entry| - # Only process entries with a start and end date - next unless entry.key?('start') && entry.key?('end') - - # Round down seconds and convert UTC to local time - start_time = Time.parse(entry['start']).getlocal - end_time = Time.parse(entry['end']).getlocal - next unless start_time && end_time - - title = "#{prefix} " - title += entry['name'] - .each do |tag| - if title =~ /\b#{tag}\b/i - title.sub!(/\b#{tag}\b/i, "@#{tag}") - else - title += " @#{tag}" - end - end - title = wwid.autotag(title) if [:autotag] - title += " @done(#{end_time.strftime('%Y-%m-%d %H:%M')})" - title.gsub!(/ +/, ' ') - title.strip! - new_entry = Item.new(start_time, title, section) - new_entry.note = entry['notes'].split(/\n/).map(&:chomp) if entry.key?('notes') - - is_match = true - - if [:search] - is_match = new_entry.search([:search], case_type: [:date], negate: [:not]) - end - - if is_match && [:date_filter] - is_match = start_time > [:date_filter][0] && start_time < [:date_filter][1] - is_match = [:not] ? !is_match : is_match - end - - new_items.push(new_entry) if is_match - end - total = new_items.count - - new_items = wwid.filter_items(new_items, opt: ) - filtered = total - new_items.count - Doing.logger.debug('Skipped:' , %(#{filtered} items that didn't match filter criteria)) if filtered.positive? - - new_items = wwid.dedup(new_items, no_overlap: [:no_overlap]) - dups = filtered - new_items.count - Doing.logger.info(%(Skipped #{dups} items with overlapping times)) if dups.positive? - - new_items.map { |item| Hooks.trigger :pre_entry_add, self, item } - - wwid.content.concat(new_items) - - new_items.map { |item| Hooks.trigger :post_entry_added, self, item } - - Doing.logger.info(%(Imported #{new_items.count} items to #{section})) -end- |
-
- - - -16 -17 -18 -19 -20- |
-
- # File 'lib/doing/plugins/import/calendar_import.rb', line 16 - -def self.settings - { - trigger: 'i?cal(?:endar)?' - } -end- |
-
- - - -12 -13 -14 -15 -16 -17 -18- |
-
- # File 'lib/doing/changelog/change.rb', line 12 - -def initialize(version, content, prefix: false, only: %i[changed new improved fixed]) - @version = Version.new(version) - @content = content - @prefix = prefix - @only = only - parse_entries -end- |
-
- - - -8 -9 -10- |
-
- # File 'lib/doing/changelog/change.rb', line 8 - -def change_date - @change_date -end- |
-
- - - -6 -7 -8- |
-
- # File 'lib/doing/changelog/change.rb', line 6 - -def content - @content -end- |
-
- - - -8 -9 -10- |
-
- # File 'lib/doing/changelog/change.rb', line 8 - -def entries - @entries -end- |
-
- - - -10 -11 -12- |
-
- # File 'lib/doing/changelog/change.rb', line 10 - -def prefix=(value) - @prefix = value -end- |
-
- - - -6 -7 -8- |
-
- # File 'lib/doing/changelog/change.rb', line 6 - -def version - @version -end- |
-
- - - -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104- |
-
- # File 'lib/doing/changelog/change.rb', line 94 - -def changes_only - out = [] - - split_items.each do |type, members| - next unless @only.include?(type) - - out << members.map(&:to_s).join("\n") - end - - out.join("\n") -end- |
-
- - - -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31- |
-
- # File 'lib/doing/changelog/change.rb', line 20 - -def parse_entries - date = @content.match(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/) - @change_date = Time.parse(date[0]) if date - - @entries = [] - types = @content.scan(/(?<=\n|\A)#### (CHANGED|NEW|IMPROVED|FIXED)(.*?)(?=\n####|\Z)/m) - types.each do |type| - type[1].scan(/\s*- +(.*?)$/).each do |entry| - @entries << Entry.new(entry[0].strip, type[0], prefix: @prefix) - end - end -end- |
-
- - - -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57- |
-
- # File 'lib/doing/changelog/change.rb', line 33 - -def search_entries(search_string) - case_type = :ignore - - matches = [] - - if search_string.rx? - matches = @entries.select { |e| e.string =~ search_string.to_rx(distance: 2, case_type: case_type) } - else - query = search_string.gsub(/(-)?--/, '\1]]').to_phrase_query - - if query[:must].nil? && query[:must_not].nil? - query[:must] = query[:should] - query[:should] = [] - end - - @entries.each do |entry| - m = no_searches?(entry.string, query[:must_not]) - m &&= all_searches?(entry.string, query[:must]) - m &&= any_searches?(entry.string, query[:should]) - matches << entry if m - end - end - - @entries = matches.count.positive? ? matches : nil -end- |
-
- - - -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76- |
-
- # File 'lib/doing/changelog/change.rb', line 63 - -def split_items - items = { changed: [], new: [], improved: [], fixed: [], other: [] } - - @entries.each do |e| - type = e.type.downcase.to_sym - if items.key?(type) - items[type] << e - else - items[:other] << e - end - end - - items -end- |
-
- - - -59 -60 -61- |
-
- # File 'lib/doing/changelog/change.rb', line 59 - -def to_h - { version: @version, content: @content } -end- |
-
- - - -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92- |
-
- # File 'lib/doing/changelog/change.rb', line 78 - -def to_s - date = @change_date.nil? ? '' : " _(#{@change_date.strftime('%F')})_" - out = ["### __#{@version}__#{date}"] - - split_items.each do |type, members| - next unless @only.include?(type) - - if members.count.positive? - out << "#### #{type.to_s.capitalize}" - out << members.map(&:to_s).join("\n") - end - end - - out.join("\n\n") -end- |
-
- - - -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20- |
-
- # File 'lib/doing/changelog/changes.rb', line 9 - -def initialize(lookup: nil, search: nil, changes: false, sort: :desc, prefix: false, only: %i[changed new improved fixed]) - @changes_only = changes - @prefix = prefix - @only = only - changelog = File.(File.join(File.dirname(__FILE__), '..', '..', '..', 'CHANGELOG.md')) - raise 'Error locating changelog' unless File.exist?(changelog) - - @content = IO.read(changelog) - parse_changes(lookup, search) - - @changes.reverse! if sort == :asc -end- |
-
- - - -6 -7 -8- |
-
- # File 'lib/doing/changelog/changes.rb', line 6 - -def changes - @changes -end- |
-
- - - -7 -8 -9- |
-
- # File 'lib/doing/changelog/changes.rb', line 7 - -def changes_only=(value) - @changes_only = value -end- |
-
- - - -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44- |
-
- # File 'lib/doing/changelog/changes.rb', line 34 - -def interactive - Doing::Prompt.choose_from(versions, - prompt: 'Select a version to see its changelog', - sorted: false, - fzf_args: [ - %(--preview='doing changes --render -l {1}'), - '--disabled', - '--height=50', - '--preview-window="right,70%"' - ]) -end- |
-
- - - -22 -23 -24 -25 -26 -27 -28- |
-
- # File 'lib/doing/changelog/changes.rb', line 22 - -def latest - if @changes_only - @changes[0].changes_only.force_encoding('utf-8') - else - @changes[0].to_s.force_encoding('utf-8') - end -end- |
-
- - - -46 -47 -48 -49 -50 -51 -52- |
-
- # File 'lib/doing/changelog/changes.rb', line 46 - -def to_s - if @changes_only - @changes.map(&:changes_only).delete_if(&:empty?).join().gsub(/\n+/, "\n").force_encoding('utf-8') - else - @changes.map(&:to_s).join("\n\n").force_encoding('utf-8') - end -end- |
-
- - - -30 -31 -32- |
-
- # File 'lib/doing/changelog/changes.rb', line 30 - -def versions - @changes.select { |change| change.entries&.count > 0 }.map { |change| change.version } -end- |
-
- - - -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104- |
-
- # File 'lib/doing/chronify/array.rb', line 63 - -def time_string(format: :dhm) - raise InvalidArgument, 'Invalid array, must be [d,h,m]' unless count == 3 - - d, h, m = self - case format - when :clock - if d.zero? && h > 24 - d = (h / 24).floor - h = h % 24 - end - format('%<d>02d:%<h>02d:%<m>02d', d: d, h: h, m: m) - when :hmclock - h += d * 24 if d.positive? - format('%<h>02d:%<m>02d', h: h, m: m) - when :ydhm - to_abbr(years: true, separator: ' ') - when :dhm - to_abbr(years: false, separator: ' ') - when :hm - h += d * 24 if d.positive? - format('%<h> 4dh %<m>02dm', h: h, m: m) - when :m - h += d * 24 if d.positive? - m += h * 60 if h.positive? - format('%<m> 4dm', m: m) - when :tight - to_abbr(years: true, separator: '') - when :natural - to_natural.join(', ') - when :speech - human = to_natural - last = human.pop - case human.count - when 0 - last - when 1 - "#{human[0]} and #{last}" - else - human.join(', ') + ", and #{last}" - end - end -end- |
-
- - - -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54- |
-
- # File 'lib/doing/chronify/array.rb', line 35 - -def to_abbr(years: false, separator: '') - if years - y, d, h, m = to_years - else - y = 0 - d, h, m = self - - if d.zero? && h > 24 - d = (h / 24).floor - h = h % 24 - end - end - - output = [] - output.push(format('%<y>dy', y: y)) if y.positive? - output.push(format('%<d>dd', d: d)) if d.positive? - output.push(format('%<h>dh', h: h)) if h.positive? - output.push(format('%<m>dm', m: m)) if m.positive? - output.join(separator) -end- |
-
- - - -25 -26 -27 -28 -29 -30 -31 -32 -33- |
-
- # File 'lib/doing/chronify/array.rb', line 25 - -def to_natural - y, d, h, m = to_years - human = [] - human.push(format('%<y>d %<s>s', y: y, s: 'year'.to_p(y))) if y.positive? - human.push(format('%<d>d %<s>s', d: d, s: 'day'.to_p(d))) if d.positive? - human.push(format('%<h>d %<s>s', h: h, s: 'hour'.to_p(h))) if h.positive? - human.push(format('%<m>d %<s>s', m: m, s: 'minute'.to_p(m))) if m.positive? - human -end- |
-
- - - -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23- |
-
- # File 'lib/doing/chronify/array.rb', line 7 - -def to_years - d, h, m = self - - if d.zero? && h > 24 - d = (h / 24).floor - h = h % 24 - end - - if d > 365 - y = (d / 365).floor - d = d % 365 - else - y = 0 - end - - [y, d, h, m] -end- |
-
- - - -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29- |
-
- # File 'lib/doing/chronify/numeric.rb', line 14 - -def format_time(human: false) - return [0, 0, 0] if nil? - - seconds = dup.to_i - minutes = (seconds / 60).to_i - hours = (minutes / 60).to_i - if human - minutes = (minutes % 60).to_i - [0, hours, minutes] - else - days = (hours / 24).to_i - hours = (hours % 24).to_i - minutes = (minutes % 60).to_i - [days, hours, minutes] - end -end- |
-
- - - -37 -38 -39- |
-
- # File 'lib/doing/chronify/numeric.rb', line 37 - -def time_string(format: :dhm) - format_time(human: true).time_string(format: format) -end- |
-
- - - -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59- |
-
- # File 'lib/doing/chronify/string.rb', line 27 - -def chronify(**) - now = Time.now - raise Errors::InvalidTimeExpression, "Invalid time expression #{inspect}" if to_s.strip == '' - - secs_ago = if match(/^(\d+)$/) - # plain number, assume minutes - Regexp.last_match(1).to_i * 60 - elsif (m = match(/^(?:(?<day>\d+)d)? *(?:(?<hour>\d+)h)? *(?:(?<min>\d+)m)?$/i)) - # day/hour/minute format e.g. 1d2h30m - [[m['day'], 24 * 3600], - [m['hour'], 3600], - [m['min'], 60]].map { |qty, secs| qty ? (qty.to_i * secs) : 0 }.reduce(0, :+) - end - - if secs_ago - res = now - secs_ago - Doing.logger.debug('Parser:', %(date/time string "#{self}" interpreted as #{res} (#{secs_ago} seconds ago))) - else - date_string = dup - date_string = 'today' if date_string.match(Types::REGEX_DAY) && now.strftime('%a') =~ /^#{Regexp.last_match(1)}/i - date_string = "#{[:context].to_s} #{date_string}" if date_string =~ Types::REGEX_TIME && [:context] - - res = Chronic.parse(date_string, { - guess: .fetch(:guess, :begin), - context: .fetch(:future, false) ? :future : :past, - ambiguous_time_range: 8 - }) - - Doing.logger.debug('Parser:', %(date/time string "#{self}" interpreted as #{res})) - end - - res -end- |
-
- - - -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94- |
-
- # File 'lib/doing/chronify/string.rb', line 70 - -def chronify_qty - minutes = 0 - case self.strip - when /^(\d+):(\d\d)$/ - minutes += Regexp.last_match(1).to_i * 60 - minutes += Regexp.last_match(2).to_i - when /^(\d+(?:\.\d+)?)([hmd])?/ - scan(/(\d+(?:\.\d+)?)([hmd])?/).each do |m| - amt = m[0] - type = m[1].nil? ? 'm' : m[1] - - minutes += case type.downcase - when 'm' - amt.to_i - when 'h' - (amt.to_f * 60).round - when 'd' - (amt.to_f * 60 * 24).round - else - 0 - end - end - end - minutes * 60 -end- |
-
- - - -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162- |
-
- # File 'lib/doing/chronify/string.rb', line 130 - -def ( = nil) - iso_rx = /\d{4}-\d\d-\d\d \d\d:\d\d/ - - = [ - 'start(?:ed)?', - 'beg[ia]n', - 'done', - 'finished', - 'completed?', - 'waiting', - 'defer(?:red)?' - ] - - if - = - = .split(/ *, */) if .is_a?(String) - .map! do |tag| - tag.sub(/^@/, '').gsub(/\((?!\?:)(.*?)\)/, '(?:\1)').strip - end - .concat().uniq! - end - - done_rx = /(?<=^| )@(?<tag>#{.join('|')})\((?<date>.*?)\)/i - - gsub!(done_rx) do - m = Regexp.last_match - t = m['tag'] - d = m['date'] - future = t =~ /^(done|complete)/ ? false : true - parsed_date = d =~ iso_rx ? Time.parse(d) : d.chronify(guess: :begin, future: future) - parsed_date.nil? ? m[0] : "@#{t}(#{parsed_date.strftime('%F %R')})" - end -end- |
-
- - - -164 -165 -166- |
-
- # File 'lib/doing/chronify/string.rb', line 164 - -def is_range? - self =~ / (to|through|thru|(un)?til|-+) / -end- |
-
Process a natural language date -
- - - -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224- |
-
- # File 'lib/doing/chronify/string.rb', line 178 - -def split_date_range - time_rx = /^(\d{1,2}(:\d{1,2})?( *(am|pm))?|midnight|noon)$/ - range_rx = / (to|through|thru|(?:un)?til|-+) / - - date_string = dup - - if date_string.is_range? - # Do we want to differentiate between "to" and "through"? - # inclusive = date_string =~ / (through|thru|-+) / ? true : false - inclusive = true - - dates = date_string.split(range_rx) - - if dates[0].strip =~ time_rx && dates[-1].strip =~ time_rx - start = dates[0].strip - finish = dates[-1].strip - else - start = dates[0].chronify(guess: :begin, future: false) - finish = dates[-1].chronify(guess: inclusive ? :end : :begin, future: true) - end - - raise Errors::InvalidTimeExpression, "Unrecognized date string (#{dates[0]})" if start.nil? - - raise Errors::InvalidTimeExpression, "Unrecognized date string (#{dates[-1]})" if finish.nil? - - else - if date_string.strip =~ time_rx - start = date_string.strip - finish = '11:59pm' - else - start = date_string.strip.chronify(guess: :begin, future: false) - finish = start + (24 * 60 * 60) - end - raise Errors::InvalidTimeExpression, 'Unrecognized date string' unless start - - end - - - if start.is_a? String - Doing.logger.debug('Parser:', - "--from string interpreted as time span, from #{start || '12am'} to #{finish || '11:59pm'}") - else - Doing.logger.debug('Parser:', - "date range interpreted as #{start.strftime('%F %R')} -- #{finish ? finish.strftime('%F %R') : 'now'}") - end - [start, finish] -end- |
-
- - - -117 -118 -119- |
-
- # File 'lib/doing/chronify/string.rb', line 117 - -def time_string(format: :dhm) - to_seconds.time_string(format: format) -end- |
-
- - - -101 -102 -103 -104 -105 -106 -107 -108 -109 -110- |
-
- # File 'lib/doing/chronify/string.rb', line 101 - -def to_seconds - mtch = match(/(\d+):(\d+):(\d+)/) - - raise Errors::DoingRuntimeError, "Invalid time string: #{self}" unless mtch - - h = mtch[1] - m = mtch[2] - s = mtch[3] - (h.to_i * 60 * 60) + (m.to_i * 60) + s.to_i -end- |
-
Turn color on or off based on T
- - - -201 -202 -203- |
-
- # File 'lib/doing/colors.rb', line 201 - -def coloring - @coloring ||= true -end- |
-
- - - -190 -191 -192- |
-
- # File 'lib/doing/colors.rb', line 190 - -def coloring? - @coloring -end- |
-
Convert a templated string
-
- - - -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238- |
-
- # File 'lib/doing/colors.rb', line 224 - -def template(input) - input = input.join(' ') if input.is_a? Array - fmt = input.gsub(/%/, '%%') - fmt = fmt.gsub(/(?<!\\u|\$)\{(\w+)\}/i) do - Regexp.last_match(1).split('').map { |c| "%<#{c}>s" }.join('') - end - - colors = { w: white, k: black, g: green, l: blue, - y: yellow, c: cyan, m: magenta, r: red, - W: bgwhite, K: bgblack, G: bggreen, L: bgblue, - Y: bgyellow, C: bgcyan, M: bgmagenta, R: bgred, - d: dark, b: bold, u: underline, i: italic, x: reset } - - fmt.empty? ? input : format(fmt, colors) -end- |
-
- - - -314 -315 -316- |
-
- # File 'lib/doing/colors.rb', line 314 - -def attributes - ATTRIBUTE_NAMES -end- |
-
- - - -286 -287 -288 -289 -290 -291 -292 -293 -294 -295 -296 -297- |
-
- # File 'lib/doing/colors.rb', line 286 - -def rgb(hex) - is_bg = hex.match(/^bg?#/) ? true : false - hex_string = hex.sub(/^([fb]g?)?#/, '') - - parts = hex_string.match(/(?<r>..)(?<g>..)(?<b>..)/) - t = [] - %w[r g b].each do |e| - t << parts[e].hex - end - color = - "\e[#{is_bg ? '48' : '38'};2;#{t.join(';')}m" -end- |
-
- - - -90 -91 -92 -93 -94 -95- |
-
- # File 'lib/doing/colors.rb', line 90 - -def support?(feature) - case feature - when :clear - !String.instance_methods(false).map(&:to_sym).include?(:clear) - end -end- |
-
- - - -301 -302 -303 -304 -305 -306 -307 -308 -309 -310 -311- |
-
- # File 'lib/doing/colors.rb', line 301 - -def uncolor(string = nil) # :yields: - if block_given? - yield.to_str.gsub(ESCAPE_REGEX, '') - elsif string.respond_to?(:to_str) - string.to_str.gsub(ESCAPE_REGEX, '') - elsif respond_to?(:to_str) - to_str.gsub(ESCAPE_REGEX, '') - else - '' - end -end- |
-
- - - -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83- |
-
- # File 'lib/doing/completion.rb', line 68 - -def generate_completion(type: 'zsh', file: :default, link: true) - return generate_all if type =~ /^all$/i - - file = file == :default ? default_file(type) : file - file = validate_target(file) - result = generate_type(type) - - if file =~ /^stdout$/i - $stdout.puts result - else - File.open(file, 'w') { |f| f.puts result } - Doing.logger.warn('File written:', "#{type} completions written to #{file}") - - link_completion_type(type, file) if link - end -end- |
-
- - - -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29- |
-
- # File 'lib/doing/completion.rb', line 19 - -def get_help_sections(command = "") - res = `doing help #{command}|command cat`.strip - scanned = res.scan(SECTIONS_RX) - sections = {} - scanned.each do |sect| - title = sect[0].downcase.strip.gsub(/ +/, '_').to_sym - content = sect[1].split(/\n/).map(&:strip).delete_if(&:empty?) - sections[title] = content - end - sections -end- |
-
- - - -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107- |
-
- # File 'lib/doing/completion.rb', line 96 - -def install_builtin(type) - FileUtils.mkdir_p(default_dir) - src = File.(File.join(File.dirname(__FILE__), '..', 'completion', default_filenames[type])) - - if File.exist?(File.join(default_dir, default_filenames[type])) - return unless Doing::Prompt.yn("Update #{type} completion script", default_response: 'n') - - end - - FileUtils.cp(src, default_dir) - Doing.logger.warn('File written:', "#{type} completions saved to #{default_file(type)}") -end- |
-
- - - -85 -86 -87 -88 -89 -90 -91 -92 -93 -94- |
-
- # File 'lib/doing/completion.rb', line 85 - -def link_default(type) - type = normalize_type(type) - raise InvalidArgument, 'Unrecognized shell specified' if type == :invalid - - return %i[zsh bash fish fig].each { |t| link_default(t) } if type == :all - - install_builtin(type) - - link_completion_type(type, File.join(default_dir, default_filenames[type])) -end- |
-
- - - -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124- |
-
- # File 'lib/doing/completion.rb', line 109 - -def normalize_type(type) - case type.to_s - when /^fig/i - :fig - when /^f/i - :fish - when /^b/i - :bash - when /^z/i - :zsh - when /^a/i - :all - else - :invalid - end -end- |
-
- - - -47 -48 -49 -50 -51 -52 -53 -54 -55 -56- |
-
- # File 'lib/doing/completion.rb', line 47 - -def parse_command(command) - res = command.match(COMMAND_RX) - commands = [res['cmd']] - commands.concat(res['alias'].split(/, /).delete_if(&:empty?)) if res['alias'] - - { - commands: commands, - description: res['desc'].short_desc - } -end- |
-
- - - -58 -59 -60- |
-
- # File 'lib/doing/completion.rb', line 58 - -def parse_commands(commands) - commands.map { |cmd| parse_command(cmd) } -end- |
-
- - - -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41- |
-
- # File 'lib/doing/completion.rb', line 31 - -def parse_option(option) - res = option.match(OPTIONS_RX) - return nil unless res - - { - short: res['short'], - long: res['long'], - arg: res['arg'], - description: res['desc'].short_desc - } -end- |
-
- - - -43 -44 -45- |
-
- # File 'lib/doing/completion.rb', line 43 - -def () - .map { |opt| parse_option(opt) } -end- |
-
- - - -125 -126 -127 -128 -129 -130 -131 -132- |
-
- # File 'lib/doing/completion/bash_completion.rb', line 125 - -def initialize - data = Completion.get_help_sections - @global_options = Completion.(data[:global_options]) - @commands = Completion.parse_commands(data[:commands]) - @bar = TTY::ProgressBar.new("\033[0;0;33mGenerating Bash completions: \033[0;35;40m[:bar] :status\033[0m", total: @commands.count + 1, bar_format: :square, hide_cursor: true, status: 'Reading subcommands') - width = TTY::Screen.columns - 45 - @bar.resize(width) -end- |
-
- - - -7 -8 -9- |
-
- # File 'lib/doing/completion/bash_completion.rb', line 7 - -def commands - @commands -end- |
-
- - - -7 -8 -9- |
-
- # File 'lib/doing/completion/bash_completion.rb', line 7 - -def - @global_options -end- |
-
- - - -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93- |
-
- # File 'lib/doing/completion/bash_completion.rb', line 63 - -def command_function(command, , type) - = [] - = [] - - .each do |o| - next if o.nil? - - << o[:long] if o[:long] - << o[:short] if o[:short] - end - - long = .map! {|o| "--#{o}"}.join(' ') - short = .map! {|o| "-#{o}"}.join(' ') - words = '' - logic = '' - words, logic = get_words(type) if type && type.is_a?(String) - - func = <<~ENDFUNC - _doing_#{command}() { - #{words} - if [[ "$token" == --* ]]; then - COMPREPLY=( $( compgen -W '#{long}' -- $token ) ) - elif [[ "$token" == -* ]]; then - COMPREPLY=( $( compgen -W '#{short} #{long}' -- $token ) ) - #{logic} - fi - } - ENDFUNC - - func -end- |
-
- - - -134 -135 -136 -137 -138 -139 -140 -141 -142- |
-
- # File 'lib/doing/completion/bash_completion.rb', line 134 - -def generate_completions - @bar.start - out = [] - out << main_function - out << 'complete -F _doing doing' - @bar.advance(status: '✅') - @bar.finish - out.join("\n") -end- |
-
- - - -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123- |
-
- # File 'lib/doing/completion/bash_completion.rb', line 95 - -def get_words(type) - func = <<~EOFUNC - OLD_IFS="$IFS" - local token=${COMP_WORDS[$COMP_CWORD]} - IFS=$'\t' - local words=$(doing #{type}) - IFS="$OLD_IFS" - EOFUNC - - logic = <<~EOLOGIC - else - local nocasematchWasOff=0 - shopt nocasematch >/dev/null || nocasematchWasOff=1 - (( nocasematchWasOff )) && shopt -s nocasematch - local w matches=() - OLD_IFS="$IFS" - IFS=$'\t'‰ - for w in $words; do - if [[ "$w" == "$token"* ]]; then - matches+=("${w// /\ }") - fi - done - IFS="$OLD_IFS" - (( nocasematchWasOff )) && shopt -u nocasematch - COMPREPLY=("${matches[@]}") - EOLOGIC - - [func, logic] -end- |
-
- - - -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61- |
-
- # File 'lib/doing/completion/bash_completion.rb', line 9 - -def main_function - first = true - out = [] - logic = [] - - @commands.each_with_index do |cmd, i| - @bar.advance(status: cmd[:commands].first) - - data = Completion.get_help_sections(cmd[:commands].first) - - arg = data[:synopsis].join(' ').strip.split(/ /).last - case arg - when /(path|file)/i - type = :file - when /sect/i - type = 'sections' - when /view/i - type = 'views' - else - type = nil - end - - if data[:command_options] - = Completion.(data[:command_options]) - out << command_function(cmd[:commands].first, , type) - - if first - op = 'if' - first = false - else - op = 'elif' - end - logic << %(#{op} [[ $last =~ (#{cmd[:commands].join('|')}) ]]; then _doing_#{cmd[:commands].first}) - end - end - - out << <<~EOFUNC - _doing() - { - local last="${@: -1}" - local token=${COMP_WORDS[$COMP_CWORD]} - - #{logic.join("\n ")} - else - OLD_IFS="$IFS" - IFS=$'\n' - COMPREPLY=( $(compgen -W "$(doing help -c)" -- $token) ) - IFS="$OLD_IFS" - fi - } - EOFUNC - out.join("\n") -end- |
-
- - - -106 -107 -108 -109 -110 -111 -112 -113- |
-
- # File 'lib/doing/completion/fig_completion.rb', line 106 - -def initialize - data = Completion.get_help_sections - @global_options = Completion.(data[:global_options]) - @commands = Completion.parse_commands(data[:commands]) - @bar = TTY::ProgressBar.new(" \033[0;0;33mGenerating Fig completions: \033[0;35;40m[:bar] :status\033[0m", total: @commands.count + 1, bar_format: :square, hide_cursor: true, status: 'processing subcommands') - width = TTY::Screen.columns - 45 - @bar.resize(width) -end- |
-
- - - -13 -14 -15- |
-
- # File 'lib/doing/completion/fig_completion.rb', line 13 - -def commands - @commands -end- |
-
- - - -13 -14 -15- |
-
- # File 'lib/doing/completion/fig_completion.rb', line 13 - -def - @global_options -end- |
-
- - - -115 -116 -117 -118- |
-
- # File 'lib/doing/completion/fig_completion.rb', line 115 - -def generate_completions - @bar.start - generate_helpers -end- |
-
- - - -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29- |
-
- # File 'lib/doing/completion/fig_completion.rb', line 15 - -def generate_helpers - out=<<~EOFUNCTIONS - const completionSpec: Fig.Spec = { - name: "doing", - description: "A CLI for a What Was I Doing system", - subcommands: [ - #{generate_subcommand_completions.join("\n ")} - ], - }; - export default completionSpec; - EOFUNCTIONS - @bar.advance(status: '✅') - @bar.finish - out -end- |
-
- - - -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47- |
-
- # File 'lib/doing/completion/fig_completion.rb', line 31 - -def generate_subcommand_completions - out = [] - indent = ' ' - @commands.each do |cmd| - cmd[:commands].each do |c| - out << <<~EOCOMMAND - { - #{indent}name: "#{c}", - #{indent}description: "#{cmd[:description].sanitize}", - #{indent}#{generate_subcommand_option_completions(cmd)} - }, - EOCOMMAND - end - end - - out -end- |
-
- - -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104-
# File 'lib/doing/completion/fig_completion.rb', line 49 - -def generate_subcommand_option_completions(cmd, indent: ' ') - out = [] - - @bar.advance(status: cmd[:commands].first) - - data = Completion.get_help_sections(cmd[:commands].first) - - option_arr = [] - - if data[:command_options] - Completion.(data[:command_options]).each do |option| - next if option.nil? - - arg = '' - - if option[:arg] - arg =<<~EOARG - args: { - #{indent} name: "#{option[:arg]}", - #{indent} description: "#{option[:arg]}", - #{indent} }, - EOARG - end - - if option[:short] - opt_data =<<~EOOPT - { - #{indent} name: ["-#{option[:short]}", "--#{option[:long]}"], - #{indent} description: "#{option[:description].sanitize}", - #{indent} #{arg} - #{indent}}, - EOOPT - else - opt_data = <<~EOOPT - { - #{indent} name: ["--#{option[:long]}"], - #{indent} description: "#{option[:description].sanitize}", - #{indent} #{arg} - #{indent}}, - EOOPT - end - - option_arr << opt_data - - end - - cmd_opts = <<~EOCMD - options: [ - #{indent}#{option_arr.join("\n#{indent}")} - ], - EOCMD - out << cmd_opts - end - - out.join("\n") -end-
- - - -231 -232 -233 -234 -235 -236 -237 -238- |
-
- # File 'lib/doing/completion/fish_completion.rb', line 231 - -def initialize - data = Completion.get_help_sections - @global_options = Completion.(data[:global_options]) - @commands = Completion.parse_commands(data[:commands]) - @bar = TTY::ProgressBar.new("\033[0;0;33mGenerating Fish completions: \033[0;35;40m[:bar] :status\033[0m", total: @commands.count + 1, bar_format: :square, hide_cursor: true, status: 'processing subcommands') - width = TTY::Screen.columns - 45 - @bar.resize(width) -end- |
-
- - - -8 -9 -10- |
-
- # File 'lib/doing/completion/fish_completion.rb', line 8 - -def commands - @commands -end- |
-
- - - -8 -9 -10- |
-
- # File 'lib/doing/completion/fish_completion.rb', line 8 - -def - @global_options -end- |
-
- - - -240 -241 -242 -243 -244 -245 -246 -247 -248 -249- |
-
- # File 'lib/doing/completion/fish_completion.rb', line 240 - -def generate_completions - @bar.start - out = [] - out << generate_helpers - out << generate_subcommand_completions - out << generate_subcommand_option_completions - @bar.advance(status: '✅') - @bar.finish - out.join("\n") -end- |
-
- - - -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141- |
-
- # File 'lib/doing/completion/fish_completion.rb', line 10 - -def generate_helpers - <<~EOFUNCTIONS - function __fish_doing_needs_command - # Figure out if the current invocation already has a command. - - set -l opts color h-help config_file= f-doing_file= n-notes v-version stdout debug default x-noauto no p-pager q-quiet yes - set cmd (commandline -opc) - set -e cmd[1] - argparse -s $opts -- $cmd 2>/dev/null - or return 0 - # These flags function as commands, effectively. - if set -q argv[1] - # Also print the command, so this can be used to figure out what it is. - echo $argv[1] - return 1 - end - return 0 - end - - function __fish_doing_using_command - set -l cmd (__fish_doing_needs_command) - test -z "$cmd" - and return 1 - contains -- $cmd $argv - and return 0 - end - - function __fish_doing_cache_timer_expired - set -l timer __fish_doing_cache_timer_$argv[1] - if not set -q $timer - set -g $timer (date '+%s') - end - - if test (math (date '+%s') - $$timer) -gt $argv[2] - set -g $timer (date '+%s') - return 1 - end - - return 0 - end - - function __fish_doing_subcommands - if not set -q __fish_doing_subcommands_cache - or __fish_doing_cache_timer_expired subcommands 86400 - set -g -a __fish_doing_subcommands_cache (doing help -c) - end - printf '%s\n' $__fish_doing_subcommands_cache - end - - function __fish_doing_complete_sections - if not set -q __fish_doing_sections_cache - or __fish_doing_cache_timer_expired sections 3600 - set -g -a __fish_doing_sections_cache (doing sections -c) - end - printf '%s\n' $__fish_doing_sections_cache - __fish_doing_complete_show_tag - end - - function __fish_doing_complete_views - if not set -q __fish_doing_views_cache - or __fish_doing_cache_timer_expired views 3600 - set -g -a __fish_doing_views_cache (doing views -c) - end - printf '%s\n' $__fish_doing_views_cache - end - - function __fish_doing_export_plugin - if not set -q __fish_doing_export_plugin_cache - or __fish_doing_cache_timer_expired export_plugins 3600 - set -g -a __fish_doing_export_plugin_cache (doing plugins --type export -c) - end - printf '%s\n' $__fish_doing_export_plugin_cache - end - - function __fish_doing_import_plugin - if not set -q __fish_doing_import_plugin_cache - or __fish_doing_cache_timer_expired import_plugins 3600 - set -g -a __fish_doing_import_plugin_cache (doing plugins --type import -c) - end - printf '%s\n' $__fish_doing_import_plugin_cache - end - - function __fish_doing_complete_template - if not set -q __fish_doing_template_cache - or __fish_doing_cache_timer_expired template 3600 - set -g -a __fish_doing_template_cache (doing template -c) - end - printf '%s\n' $__fish_doing_template_cache - end - - function __fish_doing_complete_tag - if not set -q __fish_doing_tag_cache - or __fish_doing_cache_timer_expired tags 60 - set -g -a __fish_doing_tag_cache (doing tags) - end - printf '%s\n' $__fish_doing_tag_cache - end - - function __fish_doing_complete_show_tag - if not set -q __fish_doing_tag_cache - or __fish_doing_cache_timer_expired tags 60 - set -g -a __fish_doing_tag_cache (doing tags) - end - printf '@%s\n' $__fish_doing_tag_cache - end - - function __fish_doing_complete_args - for cmd in (doing commands_accepting -c $argv[1]) - complete -x -c doing -l $argv[1] -n "__fish_doing_using_command $cmd" -a "(__fish_doing_complete_$argv[1])" - end - end - - complete -c doing -f - complete -xc doing -n '__fish_doing_needs_command' -a '(__fish_doing_subcommands)' - - complete -f -c doing -n '__fish_doing_using_command show' -a '(__fish_doing_complete_sections)' - complete -f -c doing -n '__fish_doing_using_command view' -a '(__fish_doing_complete_views)' - complete -f -c doing -n '__fish_doing_using_command template' -a '(__fish_doing_complete_templates)' - complete -f -c doing -s t -l type -x -n '__fish_doing_using_command import' -a '(__fish_doing_import_plugins)' - complete -f -c doing -n '__fish_doing_using_command help' -a '(__fish_doing_subcommands)' - - # complete -xc doing -n '__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from (doing help -c)' -a "(doing help -c)" - - function __fish_doing_complete_args - for cmd in (doing commands_accepting -c $argv[1]) - complete -x -c doing -l $argv[1] -n "__fish_doing_using_command $cmd" -a "(__fish_doing_complete_$argv[1])" - end - end - - __fish_doing_complete_args tag - EOFUNCTIONS -end- |
-
- - - -143 -144 -145 -146 -147 -148 -149 -150 -151 -152- |
-
- # File 'lib/doing/completion/fish_completion.rb', line 143 - -def generate_subcommand_completions - out = [] - @commands.each do |cmd| - desc = Shellwords.escape(cmd[:description]) - cmds = cmd[:commands].join(' ') - out << "complete -xc doing -n '__fish_doing_needs_command' -a '#{cmds}' -d #{desc}" - end - - out.join("\n") -end- |
-
- - -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229-
# File 'lib/doing/completion/fish_completion.rb', line 154 - -def generate_subcommand_option_completions - - out = [] - need_export = [] - need_bool = [] - need_case = [] - need_sort = [] - need_tag_sort = [] - need_tag_order = [] - need_age = [] - need_section = [] - - @commands.each_with_index do |cmd, i| - @bar.advance(status: cmd[:commands].first) - data = Completion.get_help_sections(cmd[:commands].first) - - if data[:synopsis].join(' ').strip.split(/ /).last =~ /(path|file)/i - out << "complete -c doing -F -n '__fish_doing_using_command #{cmd[:commands].join(" ")}'" - end - - if data[:command_options] - Completion.(data[:command_options]).each do |option| - next if option.nil? - - arg = option[:arg] ? '-r' : '' - short = option[:short] ? "-s #{option[:short]}" : '' - long = option[:long] ? "-l #{option[:long]}" : '' - out << "complete -c doing #{long} #{short} -f #{arg} -n '__fish_doing_using_command #{cmd[:commands].join(' ')}' -d #{Shellwords.escape(option[:description])}" - - need_export.concat(cmd[:commands]) if option[:long] == 'output' - need_bool.concat(cmd[:commands]) if option[:long] == 'bool' - need_case.concat(cmd[:commands]) if option[:long] == 'case' - need_sort.concat(cmd[:commands]) if option[:long] == 'sort' - need_tag_sort.concat(cmd[:commands]) if option[:long] == 'tag_sort' - need_tag_order.concat(cmd[:commands]) if option[:long] == 'tag_order' - need_age.concat(cmd[:commands]) if option[:long] == 'age' - need_section.concat(cmd[:commands]) if option[:long] == 'section' - end - end - end - - unless need_export.empty? - out << "complete -f -c doing -s o -l output -x -n '__fish_doing_using_command #{need_export.join(' ')}' -a '(__fish_doing_export_plugin)'" - end - - unless need_bool.empty? - out << "complete -f -c doing -s b -l bool -x -n '__fish_doing_using_command #{need_bool.join(' ')}' -a 'and or not pattern'" - end - - unless need_case.empty? - out << "complete -f -c doing -l case -x -n '__fish_doing_using_command #{need_case.join(' ')}' -a 'case-sensitive ignore smart'" - end - - unless need_sort.empty? - out << "complete -f -c doing -l sort -x -n '__fish_doing_using_command #{need_sort.join(' ')}' -a 'asc desc'" - end - - unless need_tag_sort.empty? - out << "complete -f -c doing -l tag_sort -x -n '__fish_doing_using_command #{need_tag_sort.join(' ')}' -a 'name time'" - end - - unless need_tag_order.empty? - out << "complete -f -c doing -l tag_order -x -n '__fish_doing_using_command #{need_tag_order.join(' ')}' -a 'asc desc'" - end - - unless need_age.empty? - out << "complete -f -c doing -s a -l age -x -n '__fish_doing_using_command #{need_age.join(' ')}' -a 'oldest newest'" - end - - unless need_section.empty? - out << "complete -f -c doing -s s -l section -x -n '__fish_doing_using_command #{need_section.join(' ')}' -a '(__fish_doing_complete_sections)'" - end - - # clear - out.join("\n") -end-
- - - -19 -20 -21 -22 -23 -24 -25- |
-
- # File 'lib/doing/completion/completion_string.rb', line 19 - -def ltrunc(max) - if length > max - sub(/^.*?(.{#{max - 3}})$/, '...\1') - else - self - end -end- |
-
- - - -27 -28 -29- |
-
- # File 'lib/doing/completion/completion_string.rb', line 27 - -def ltrunc!(max) - replace ltrunc(max) -end- |
-
- - - -10 -11 -12- |
-
- # File 'lib/doing/completion/completion_string.rb', line 10 - -def short_desc - split(/[,.]/)[0].sub(/ \(.*?\)?$/, '').strip -end- |
-
- - - -92 -93 -94 -95 -96 -97 -98 -99- |
-
- # File 'lib/doing/completion/zsh_completion.rb', line 92 - -def initialize - data = Completion.get_help_sections - @global_options = Completion.(data[:global_options]) - @commands = Completion.parse_commands(data[:commands]) - @bar = TTY::ProgressBar.new(" \033[0;0;33mGenerating Zsh completions: \033[0;35;40m[:bar] :status\033[0m", total: @commands.count + 1, bar_format: :square, hide_cursor: true, status: 'processing subcommands') - width = TTY::Screen.columns - 45 - @bar.resize(width) -end- |
-
- - - -13 -14 -15- |
-
- # File 'lib/doing/completion/zsh_completion.rb', line 13 - -def commands - @commands -end- |
-
- - - -13 -14 -15- |
-
- # File 'lib/doing/completion/zsh_completion.rb', line 13 - -def - @global_options -end- |
-
- - - -101 -102 -103 -104- |
-
- # File 'lib/doing/completion/zsh_completion.rb', line 101 - -def generate_completions - @bar.start - generate_helpers -end- |
-
- - - -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48- |
-
- # File 'lib/doing/completion/zsh_completion.rb', line 15 - -def generate_helpers - out=<<~EOFUNCTIONS - compdef _doing doing - - function _doing() { - local line state - - function _commands { - local -a commands - - commands=( - #{generate_subcommand_completions.join("\n ")} - ) - _describe 'command' commands - } - - _arguments -C \ - "1: :_commands" \ - "*::arg:->args" - - - - case $line[1] in - #{generate_subcommand_option_completions(indent: ' ').join("\n ")} - esac - - _arguments -s $args - } - - EOFUNCTIONS - @bar.advance(status: '✅') - @bar.finish - out -end- |
-
- - - -50 -51 -52 -53 -54 -55 -56 -57 -58- |
-
- # File 'lib/doing/completion/zsh_completion.rb', line 50 - -def generate_subcommand_completions - out = [] - @commands.each_with_index do |cmd, i| - cmd[:commands].each do |c| - out << "'#{c}:#{cmd[:description].gsub(/'/, '\\\'')}'" - end - end - out -end- |
-
- - -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90-
# File 'lib/doing/completion/zsh_completion.rb', line 60 - -def generate_subcommand_option_completions(indent: ' ') - - out = [] - - @commands.each_with_index do |cmd, i| - @bar.advance(status: cmd[:commands].first) - - data = Completion.get_help_sections(cmd[:commands].first) - option_arr = [] - - if data[:command_options] - Completion.(data[:command_options]).each do |option| - next if option.nil? - - arg = option[:arg] ? ":#{option[:arg]}:" : '' - - option_arr << if option[:short] - %({'(--#{option[:long]})-#{option[:short]}','(-#{option[:short]})--#{option[:long]}'}"[#{option[:description].sanitize}]#{arg}") - else - %("--#{option[:long]}[#{option[:description].sanitize}]#{arg}") - end - end - end - - cmd[:commands].each do |c| - out << "#{c}) \n#{indent} args=( #{option_arr.join(' ')} )\n#{indent};;" - end - end - - out -end-
- - - -121 -122 -123 -124 -125- |
-
- # File 'lib/doing/configuration.rb', line 121 - -def initialize(file = nil, options: {}) - @config_file = file.nil? ? default_config_file : File.(file) - - @settings = configure() -end- |
-
- - - -127 -128 -129- |
-
- # File 'lib/doing/configuration.rb', line 127 - -def config_file - @config_file ||= default_config_file -end- |
-
- - - -12 -13 -14- |
-
- # File 'lib/doing/configuration.rb', line 12 - -def force_answer - @force_answer ||= false -end- |
-
- - - -10 -11 -12- |
-
- # File 'lib/doing/configuration.rb', line 10 - -def ignore_local=(value) - @ignore_local = value -end- |
-
- - - -8 -9 -10- |
-
- # File 'lib/doing/configuration.rb', line 8 - -def settings - @settings -end- |
-
- - - -160 -161 -162- |
-
- # File 'lib/doing/configuration.rb', line 160 - -def additional_configs - @additional_configs ||= find_local_config -end- |
-
- - - -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196- |
-
- # File 'lib/doing/configuration.rb', line 169 - -def choose_config(create: false, local: false) - if local && create - res = File.('.doingrc') - FileUtils.touch(res) - return res - end - - return @config_file if @force_answer - - if @additional_configs&.count&.positive? || create - choices = [@config_file].concat(@additional_configs) - choices.push('Create a new .doingrc in the current directory') if create && !File.exist?('.doingrc') - res = Doing::Prompt.choose_from(choices.uniq.sort.reverse, - sorted: false, - prompt: 'Local configs found, select which to update > ') - - raise UserCancelled, 'Cancelled' unless res - - if res =~ /^Create a new/ - res = File.('.doingrc') - FileUtils.touch(res) - end - - res.strip || @config_file - else - @config_file - end -end- |
-
- - - -131 -132 -133- |
-
- # File 'lib/doing/configuration.rb', line 131 - -def config_dir - @config_dir ||= File.join(Util.user_home, '.config', 'doing') -end- |
-
- - - -338 -339 -340 -341 -342 -343 -344 -345 -346 -347 -348 -349 -350 -351 -352 -353 -354 -355 -356 -357 -358 -359 -360 -361 -362 -363 -364 -365 -366 -367 -368 -369 -370 -371 -372 -373 -374 -375- |
-
- # File 'lib/doing/configuration.rb', line 338 - -def configure(opt = {}) - update_deprecated_config if config_file == default_config_file - - @ignore_local = opt[:ignore_local] if opt[:ignore_local] - - config = read_config.clone - - plugin_config = Util.deep_merge_hashes(DEFAULTS['plugins'], config['plugins'] || {}) - - load_plugins(plugin_config['plugin_path']) - - Plugins.plugins.each do |_type, plugins| - plugins.each do |title, plugin| - plugin_config[title] = plugin[:config] if plugin[:config].good? - config['export_templates'][title] ||= nil if plugin[:templates] && !plugin[:templates].empty? - end - end - - config = Util.deep_merge_hashes({ - 'plugins' => plugin_config - }, config) - - config = find_deprecations(config) - - if !File.exist?(config_file) || opt[:rewrite] - Util.write_to_file(config_file, YAML.dump(config), backup: true) - Doing.logger.warn('Config:', "Config file written to #{config_file}") - end - - Hooks.trigger :post_config, self - - config = local_config.deep_merge(config, { extend_existing_arrays: true, sort_merged_arrays: true }) unless @ignore_local - # config = Util.deep_merge_hashes(config, local_config) unless @ignore_local - - Hooks.trigger :post_local_config, self - - config -end- |
-
- - - -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158- |
-
- # File 'lib/doing/configuration.rb', line 146 - -def default_config_file - if File.exist?(config_dir) && !File.directory?(config_dir) - raise DoingRuntimeError, "#{config_dir} exists but is not a directory" - - end - - unless File.exist?(config_dir) - FileUtils.mkdir_p(config_dir) - Doing.logger.log_now(:warn, "Config directory created at #{config_dir}") - end - - File.join(config_dir, 'config.yml') -end- |
-
- - - -140 -141 -142 -143 -144- |
-
- # File 'lib/doing/configuration.rb', line 140 - -def exact_match? - search_settings = @settings['search'] - matching = search_settings.fetch('matching', 'pattern').normalize_matching - matching == :exact -end- |
-
- - - -198 -199 -200- |
-
- # File 'lib/doing/configuration.rb', line 198 - -def fetch(*path, default) - @settings.dig(*path) || default -end- |
-
- - - -296 -297 -298 -299- |
-
- # File 'lib/doing/configuration.rb', line 296 - -def from(user_config) - # Util.deep_merge_hashes(DEFAULTS, Configuration[user_config].stringify_keys) - Configuration[user_config].stringify_keys.deep_merge(DEFAULTS, { extend_existing_arrays: true, sort_merged_arrays: true }) -end- |
-
- - - -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242 -243 -244 -245 -246 -247 -248 -249 -250 -251 -252 -253 -254 -255 -256 -257 -258 -259 -260 -261 -262- |
-
- # File 'lib/doing/configuration.rb', line 212 - -def resolve_key_path(keypath, create: false, distance: 2, exact: false) - cfg = @settings - real_path = [] - unless keypath =~ /^[.*]?$/ - paths = keypath.split(/[:.]/) - element_count = paths.count - while paths.length.positive? && !cfg.nil? - path = paths.shift - new_cfg = nil - - if cfg.is_a?(Hash) - matches = if exact - cfg.select { |key, _| key == path } - else - cfg.select { |key, _| key =~ path.to_rx(distance: distance) } - end - if matches.count.positive? - shortest = matches.keys.group_by(&:length).min.last[0] - real_path << shortest - new_cfg = matches[shortest] - end - else - new_cfg = cfg - end - - if new_cfg.nil? - return real_path if real_path[-1] == path && real_path.count == element_count - - if distance < 5 && !create - return resolve_key_path(keypath, create: false, distance: distance + 1) - else - return nil unless create - end - - resolved = real_path.count.positive? ? "Resolved #{real_path.join('.')}, but " : '' - Doing.logger.log_now(:warn, "#{resolved}#{path} is unknown") - new_path = [*real_path, path, *paths].compact.join('.') - Doing.logger.log_now(:warn, "Continuing will create the path #{new_path}") - res = Prompt.yn('Key path not found, create it?', default_response: true) - raise InvalidArgument, 'Invalid key path' unless res - - real_path.push(path).concat(paths).compact! - Doing.logger.debug('Config:', "translated key path #{keypath} to #{real_path.join('.')}") unless keypath == real_path.join('.') - return real_path - end - cfg = new_cfg - end - end - Doing.logger.debug('Config:', "translated key path #{keypath} to #{real_path.join('.')}") unless keypath == real_path.join('.') - real_path -end- |
-
- - - -383 -384 -385 -386 -387 -388 -389 -390 -391 -392 -393 -394 -395 -396- |
-
- # File 'lib/doing/configuration.rb', line 383 - -def save_view(view, title) - title.downcase! - default_template = Doing.setting('templates.default') - user_config = Util.safe_load_file(config_file) - user_config['views'] = {} unless user_config.key?('views') - - view.delete_if { |k, v| v == default_template[k] } - - user_config['views'][title] = view - Util.write_to_file(config_file, YAML.dump(user_config), backup: true) - Doing.logger.warn('Config:', %(View "#{title}" saved to #{config_file})) - Doing.logger.info('Config:', %(to use, run `doing view #{title}`)) - Hooks.trigger :post_config, self -end- |
-
- - - -304 -305 -306 -307 -308 -309 -310 -311 -312 -313 -314 -315 -316 -317 -318 -319 -320 -321 -322 -323 -324 -325 -326 -327 -328 -329 -330 -331- |
-
- # File 'lib/doing/configuration.rb', line 304 - -def update_deprecated_config - # return # Until further notice - return if File.exist?(default_config_file) - - old_file = File.join(Util.user_home, '.doingrc') - return unless File.exist?(old_file) - - Doing.logger.log_now(:warn, 'Deprecated:', "main config file location has changed to #{config_file}") - res = Prompt.yn("Move #{old_file} to new location, preserving settings?", default_response: true) - - return unless res - - if File.exist?(default_config_file) - res = Prompt.yn("#{default_config_file} already exists, overwrite it?", default_response: false) - - unless res - @config_file = old_file - return - end - end - - FileUtils.mv old_file, default_config_file, force: true - Doing.logger.log_now(:warn, 'Config:', "Config file moved to #{default_config_file}") - Doing.logger.log_now(:warn, 'Config:', %(If ~/.doingrc exists in the future, - it will be considered a local config and its values will override the - default configuration.)) - Process.exit 0 -end- |
-
- - - -274 -275 -276 -277 -278 -279 -280 -281 -282 -283 -284 -285- |
-
- # File 'lib/doing/configuration.rb', line 274 - -def value_for_key(keypath = '') - cfg = @settings - real_path = ['config'] - unless keypath =~ /^[.*]?$/ - real_path = resolve_key_path(keypath, create: false) - return nil unless real_path&.count&.positive? - - cfg = cfg.dig(*real_path) - end - - cfg.nil? ? nil : { real_path[-1] => cfg } -end- |
-
- - - -13 -14 -15 -16 -17- |
-
- # File 'lib/doing/plugins/export/dayone_export.rb', line 13 - -def initialize(page_title, items, totals) - @page_title = page_title - @items = items - @totals = totals -end- |
-
- - - -11 -12 -13- |
-
- # File 'lib/doing/plugins/export/dayone_export.rb', line 11 - -def items - @items -end- |
-
- - - -11 -12 -13- |
-
- # File 'lib/doing/plugins/export/dayone_export.rb', line 11 - -def page_title - @page_title -end- |
-
- - - -11 -12 -13- |
-
- # File 'lib/doing/plugins/export/dayone_export.rb', line 11 - -def totals - @totals -end- |
-
- - - -19 -20 -21- |
-
- # File 'lib/doing/plugins/export/dayone_export.rb', line 19 - -def get_binding - binding() -end- |
-
- - - -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174- |
-
- # File 'lib/doing/plugins/export/dayone_export.rb', line 46 - -def self.render(wwid, items, variables: {}) - - return unless items.good? - - config = Doing.settings - - opt = variables[:options] - trigger = opt[:output] - digest = case trigger - when /-days?$/ - :day - when /-entries$/ - :entries - else - :digest - end - - all_items = [] - days = {} - flagged = false - = [] - - items.each do |i| - day_flagged = false - date_key = i.date.strftime('%Y-%m-%d') - - if String.method_defined? :force_encoding - title = i.title.force_encoding('utf-8').link_urls(format: :markdown) - note = i.note.map { |line| line.force_encoding('utf-8').strip.link_urls(format: :markdown) } if i.note - else - title = i.title.link_urls(format: :markdown) - note = i.note.map { |line| line.strip.link_urls(format: :markdown) } if i.note - end - - title = "#{title} @section(#{i.section})" unless variables[:is_single] - - .concat(i.tag_array).sort!.uniq! - flagged = day_flagged = true if i.(config['marker_tag']) - - interval = wwid.get_interval(i, record: true) if i.title =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times] - interval ||= false - human_time = false - if interval - d, h, m = wwid.get_interval(i, formatted: false).format_time - human_times = [] - human_times << format('%<d>d day%<p>s', d: d, p: d == 1 ? '' : 's') if d > 0 - human_times << format('%<h>d hour%<p>s', h: h, p: h == 1 ? '' : 's') if h > 0 - human_times << format('%<m>d minute%<p>s', m: m, p: m == 1 ? '' : 's') if m > 0 - human_time = human_times.join(', ') - end - - done = i.('done') ? ' ' : ' ' - - item = { - date_object: i.date, - date: i.date.strftime('%a %-I:%M%p'), - shortdate: i.date.relative_date, - done: done, - note: note, - section: i.section, - time: interval, - human_time: human_time, - title: title.strip, - starred: day_flagged, - tags: i.tag_array - } - all_items << item - - - if days.key?(date_key) - days[date_key][:starred] = true if day_flagged - days[date_key][:tags] = days[date_key][:tags].concat(i.tag_array).sort.uniq - days[date_key][:entries].push(item) - else - days[date_key] ||= { tags: [], entries: [], starred: false } - days[date_key][:starred] = true if day_flagged - days[date_key][:tags] = days[date_key][:tags].concat(i.tag_array).sort.uniq - days[date_key][:entries].push(item) - end - end - - - template = if config['export_templates']['dayone'] && File.exist?(File.(config['export_templates']['dayone'])) - IO.read(File.(config['export_templates']['dayone'])) - else - self.template('dayone') - end - - totals = opt[:totals] ? wwid.tag_times(format: :markdown, sort_by: opt[:sort_tags], sort_order: opt[:tag_order]) : '' - - case digest - when :day - days.each do |k, hsh| - title = "#{k}: #{variables[:page_title]}" - to_dayone(template: template, - title: title, - items: hsh[:entries], - totals: '', - date: Time.parse(k), - tags: , - starred: hsh[:starred]) - end - when :entries - entry_template = if config['export_templates']['dayone_entry'] && File.exist?(File.(config['export_templates']['dayone_entry'])) - IO.read(File.(config['export_templates']['dayone_entry'])) - else - self.template('dayone-entry') - end - all_items.each do |item| - to_dayone(template: entry_template, - title: '', - items: [item], - totals: '', - date: item[:date_object], - tags: item[:tags], - starred: item[:starred]) - end - else - to_dayone(template: template, - title: variables[:page_title], - items: all_items, - totals: totals, - date: Time.now, - tags: , - starred: flagged) - end - - @out = '' -end- |
-
- - - -27 -28 -29 -30 -31 -32 -33 -34 -35- |
-
- # File 'lib/doing/plugins/export/dayone_export.rb', line 27 - -def self.settings - { - trigger: 'day(?:one)?(?:-(?:days?|entries))?', - templates: [ - { name: 'dayone', trigger: 'day(?:one)?$', format: 'erb', filename: 'dayone.erb' }, - { name: 'dayone_entry', trigger: 'day(?:one)-entr(?:y|ies)?$', format: 'erb', filename: 'dayone-entry.erb' } - ] - } -end- |
-
- - - -37 -38 -39 -40 -41 -42 -43 -44- |
-
- # File 'lib/doing/plugins/export/dayone_export.rb', line 37 - -def self.template(trigger) - case trigger - when /day(?:one)-entr(?:y|ies)?$/ - IO.read(File.join(File.dirname(__FILE__), '../../../templates/doing-dayone-entry.erb')) - else - IO.read(File.join(File.dirname(__FILE__), '../../../templates/doing-dayone.erb')) - end -end- |
-
- - - -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205- |
-
- # File 'lib/doing/plugins/export/dayone_export.rb', line 176 - -def self.to_dayone(template: self.template(nil), title: 'doing', items: [], totals: '', date: Time.now, tags: [], starred: false) - mdx = DayOneRenderer.new(title, items, totals) - - engine = ERB.new(template) - content = engine.result(mdx.get_binding) - - uuid = SecureRandom.uuid - # uuid = `uuidgen`.strip - - plist = { - 'Creation Date' => date, - 'Creator' => { 'Software Agent' => 'Doing/2.0.0' }, - 'Entry Text' => content, - 'Starred' => starred, - 'Tags' => .sort.uniq.delete_if { |t| t =~ /(done|cancell?ed|from)/ }, - 'UUID' => uuid - } - - container = File.('~/Library/Group Containers/') - dayone_dir = Dir.glob('*.dayoneapp2', base: container).first - import_dir = File.join(container, dayone_dir, 'Data', 'Auto Import', 'Default Journal.dayone', 'entries') - FileUtils.mkdir_p(import_dir) unless File.exist?(import_dir) - entry_file = File.join(import_dir, "#{uuid}.doentry") - Doing.logger.debug('Day One Export:', "Exporting to #{entry_file}") - File.open(entry_file, 'w') do |f| - f.puts plist.to_plist - end - - Doing.logger.count(:exported, level: :info, count: items.count, message: '%count %items exported to Day One import folder') -end- |
-
- - - -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33- |
-
- # File 'lib/doing/plugins/export/doing_export.rb', line 15 - -def self.render(wwid, items, variables: {}) - return if items.nil? - - content = Doing::Items.new - items.each do |item| - content.add_section(item.section, log: false) - content.push(item) - end - - out = [] - content.sections.each do |section| - out.push(section.original) - is = content.in_section(section.title).sort_by { |i| [i.date, i.title] } - is.reverse! if Doing.setting('doing_file_sort').normalize_order == :desc - is.each { |item| out.push(item.to_s) } - end - - Doing::Pager.page out.join("\n") -end- |
-
- - - -9 -10 -11 -12 -13- |
-
- # File 'lib/doing/plugins/export/doing_export.rb', line 9 - -def self.settings - { - trigger: 'doing' - } -end- |
-
- - - -112 -113 -114 -115 -116 -117 -118- |
-
- # File 'lib/doing/plugins/import/doing_import.rb', line 112 - -def self.duplicate?(item) - @old_items.each do |oi| - return true if item.equal?(oi) - end - - false -end- |
-
- - - -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110- |
-
- # File 'lib/doing/plugins/import/doing_import.rb', line 26 - -def self.import(wwid, path, options: {}) - exit_now! 'Path to Doing file required' if path.nil? - - exit_now! 'File not found' unless File.exist?(File.(path)) - - [:no_overlap] ||= false - - [:autotag] ||= Doing.auto_tag - - = [:tag] ? [:tag].split(/[ ,]+/).map { |t| t.sub(/^@?/, '') } : [] - prefix = [:prefix] || '' - - @old_items = wwid.content.dup - - new_items = read_doing_file(path) - - total = new_items.count - - [:count] = 0 - new_items = wwid.filter_items(new_items, opt: ) - - skipped = total - new_items.count - Doing.logger.debug('Skipped:', %(#{skipped} items that didn't match filter criteria)) if skipped.positive? - - imported = [] - updated = 0 - - new_items.each do |item| - next if duplicate?(item) - - title = "#{prefix} #{item.title}" - .each do |tag| - if title =~ /\b#{tag}\b/i - title.sub!(/\b#{tag}\b/i, "@#{tag}") - else - title += " @#{tag}" - end - end - title = wwid.autotag(title) if [:autotag] - title.gsub!(/ +/, ' ') - title.strip! - section = [:section] || item.section - section ||= Doing.setting('current_section') - - new_item = Item.new(item.date, title, section, item.note, item.id) - - is_match = true - - if [:search] - is_match = new_item.search([:search], case_type: [:case], negate: [:not]) - end - - if is_match && [:date_filter] - is_match = new_item.date > [:date_filter][0] && new_item.date < [:date_filter][1] - is_match = [:not] ? !is_match : is_match - end - - if wwid.content.find_id(new_item.id) - old_index = wwid.content.index_for_id(new_item.id) - old_item = wwid.content[old_index].clone - wwid.content[old_index] = new_item - Hooks.trigger :post_entry_updated, self, new_item, old_item - updated += 1 - else - imported.push(new_item) if is_match - end - end - - dups = new_items.count - imported.count - Doing.logger.info('Skipped:', %(#{dups} duplicate items)) if dups.positive? - - imported = wwid.dedup(imported, no_overlap: [:no_overlap]) - overlaps = new_items.count - imported.count - dups - Doing.logger.debug('Skipped:', "#{overlaps} items with overlapping times") if overlaps.positive? - - imported.each do |item| - wwid.content.add_section(item.section) unless wwid.content.section?(item.section) - Hooks.trigger :pre_entry_add, self, item - wwid.content.push(item) - Hooks.trigger :post_entry_added, self, item - end - - Doing.logger.info('Updated:', %(#{updated} items)) - Doing.logger.info('Imported:', "#{imported.count} items") -end- |
-
- - - -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162- |
-
- # File 'lib/doing/plugins/import/doing_import.rb', line 120 - -def self.read_doing_file(path) - doing_file = File.(path) - - return nil unless File.exist?(doing_file) && File.file?(doing_file) && File.stat(doing_file).size.positive? - - input = IO.read(doing_file) - input = input.force_encoding('utf-8') if input.respond_to? :force_encoding - - lines = input.split(/[\n\r]/) - current = 0 - - items = [] - section = '' - - lines.each do |line| - next if line =~ /^\s*$/ - - case line - when /^(\S[\S ]+):(\s+@[\w\-_.]+(?= |$))*\s*$/ - section = Regexp.last_match(1) - current = 0 - when /^\s*- (\d{4}-\d\d-\d\d \d\d:\d\d) \| (.*?)(?: <([a-z0-9]{32})>)? *$/ - date = Regexp.last_match(1).strip - title = Regexp.last_match(2).strip - id = Regexp.last_match(3) - item = Item.new(date, title, section, nil, id) - items.push(item) - current += 1 - when /^\S/ - next - else - next if current.zero? - - prev_item = items[current - 1] - prev_item.note = Note.new unless prev_item.note - - prev_item.note.add(line) - # end - end - end - - items -end- |
-
- - - -11 -12 -13 -14 -15- |
-
- # File 'lib/doing/plugins/import/doing_import.rb', line 11 - -def self.settings - { - trigger: 'doing' - } -end- |
-
- - - -10 -11 -12 -13 -14- |
-
- # File 'lib/doing/changelog/entry.rb', line 10 - -def initialize(string, type, prefix: false) - @string = string - @type = type - @prefix = prefix -end- |
-
- - - -8 -9 -10- |
-
- # File 'lib/doing/changelog/entry.rb', line 8 - -def prefix=(value) - @prefix = value -end- |
-
- - - -6 -7 -8- |
-
- # File 'lib/doing/changelog/entry.rb', line 6 - -def string - @string -end- |
-
- - - -6 -7 -8- |
-
- # File 'lib/doing/changelog/entry.rb', line 6 - -def type - @type -end- |
-
- - - -16 -17 -18- |
-
- # File 'lib/doing/changelog/entry.rb', line 16 - -def clean(string) - string.gsub(/\|/, '\|') -end- |
-
- - - -20 -21 -22- |
-
- # File 'lib/doing/changelog/entry.rb', line 20 - -def print_prefix - @prefix ? "#{@type}: " : '' -end- |
-
- - - -24 -25 -26- |
-
- # File 'lib/doing/changelog/entry.rb', line 24 - -def to_s - "- #{print_prefix}#{clean(@string)}" -end- |
-
- - - -6 -7 -8 -9 -10 -11 -12 -13 -14- |
-
- # File 'lib/doing/errors.rb', line 6 - -def initialize(msg = nil, level: nil, topic: 'Error:', exit_code: 1) - level ||= :error - Doing.logger.output_results - if msg - Doing.logger.log_now(level, topic, msg) - end - - Process.exit exit_code -end- |
-
- - - -44 -45 -46 -47 -48 -49- |
-
- # File 'lib/doing/errors.rb', line 44 - -def initialize(msg = 'Runtime Error', exit_code = nil, topic: 'Error:') - Doing.logger.output_results - Doing.logger.log_now(:error, topic, msg) - - Process.exit exit_code if exit_code -end- |
-
- - - -30 -31 -32 -33 -34- |
-
- # File 'lib/doing/errors.rb', line 30 - -def initialize(msg = '') - Doing.logger.output_results - - super(msg) -end- |
-
- - - -24 -25 -26- |
-
- # File 'lib/doing/errors.rb', line 24 - -def initialize(msg = 'No input', topic = 'Exited:') - super(msg, level: :warn, topic: topic, exit_code: 6) -end- |
-
- - - -60 -61 -62- |
-
- # File 'lib/doing/errors.rb', line 60 - -def initialize(msg, exit_code = 24) - super(msg, level: :error, topic: 'History:', exit_code: exit_code) -end- |
-
- - - -72 -73 -74- |
-
- # File 'lib/doing/errors.rb', line 72 - -def initialize(kind = 'output', msg = nil) - super(%(Invalid #{kind} type (#{msg})), 128, topic: 'Plugin:') -end- |
-
- - - -66 -67 -68- |
-
- # File 'lib/doing/errors.rb', line 66 - -def initialize(msg, exit_code = 26) - super(msg, level: :error, topic: 'History:', exit_code: exit_code) -end- |
-
- - - -53 -54 -55 -56- |
-
- # File 'lib/doing/errors.rb', line 53 - -def initialize(msg = 'No results', topic = 'Exited:') - super(msg, level: :warn, topic: topic, exit_code: 0) - -end- |
-
- - - -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102- |
-
- # File 'lib/doing/errors.rb', line 80 - -def initialize(msg = 'Plugin error', type = nil, plugin = nil) - @plugin = plugin || 'Unknown Plugin' - - type ||= 'Unknown' - @type = case type.to_s - when /^i/ - 'Import plugin' - when /^e/ - 'Export plugin' - when /^h/ - 'Hook' - when /^u/ - 'Unrecognized' - else - type.to_s - end - - msg = "(#{@type}: #{@plugin}) #{msg}" - - Doing.logger.log_now(:error, 'Plugin:', msg) - - super(msg) -end- |
-
- - - -78 -79 -80- |
-
- # File 'lib/doing/errors.rb', line 78 - -def plugin - @plugin -end- |
-
- - - -18 -19 -20- |
-
- # File 'lib/doing/errors.rb', line 18 - -def initialize(msg = 'Cancelled', topic = 'Exited:') - super(msg, level: :warn, topic: topic, exit_code: 1) -end- |
-
- - - -38 -39 -40- |
-
- # File 'lib/doing/errors.rb', line 38 - -def initialize(msg = 'wrong command', topic: 'Error:') - super(msg, level: :warn, topic: topic, exit_code: 2) -end- |
-
- - - -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78- |
-
- # File 'lib/doing/plugins/export/html_export.rb', line 29 - -def self.render(wwid, items, variables: {}) - return if items.nil? - - opt = variables[:options] - - items_out = [] - items.each do |i| - # if i.has_key?('note') - # note = '<span class="note">' + i.note.map{|n| n.strip }.join('<br>') + '</span>' - # else - # note = '' - # end - if String.method_defined? :force_encoding - title = i.title.force_encoding('utf-8').link_urls - note = i.note.map { |line| line.force_encoding('utf-8').strip.link_urls } if i.note - else - title = i.title.link_urls - note = i.note.map { |line| line.strip.link_urls } if i.note - end - - interval = wwid.get_interval(i) if i.title =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times] - interval ||= false - - items_out << { - date: i.date.strftime('%a %-I:%M%p'), - title: title.gsub(/(@[^ (]+(\(.*?\))?)/im, '<span class="tag">\1</span>').strip, #+ " #{note}" - note: note, - time: interval, - section: i.section - } - end - - template = if Doing.setting('export_templates.haml') && File.exist?(File.(Doing.setting('export_templates.haml'))) - IO.read(File.(Doing.setting('export_templates.haml'))) - else - self.template('html') - end - - style = if Doing.setting('export_templates.css') && File.exist?(File.(Doing.setting('export_templates.css'))) - IO.read(File.(Doing.setting('export_templates.css'))) - else - self.template('css') - end - - totals = opt[:totals] ? wwid.tag_times(format: :html, sort_by: opt[:sort_tags], sort_order: opt[:tag_order]) : '' - engine = Haml::Engine.new(template) - Doing.logger.debug('HTML Export:', "#{items_out.count} items output to HTML") - @out = engine.render(Object.new, - { :@items => items_out, :@page_title => variables[:page_title], :@style => style, :@totals => totals }) -end- |
-
- - - -11 -12 -13 -14 -15 -16 -17 -18 -19- |
-
- # File 'lib/doing/plugins/export/html_export.rb', line 11 - -def self.settings - { - trigger: 'html?|web(?:page)?', - templates: [ - { name: 'html', trigger: 'h[ta]ml?|web', format: 'haml', filename: 'html_export.haml' }, - { name: 'html_style', trigger: 'css|styl(?:e|us)', format: 'css', filename: 'html_export.css' } - ] - } -end- |
-
- - - -21 -22 -23 -24 -25 -26 -27- |
-
- # File 'lib/doing/plugins/export/html_export.rb', line 21 - -def self.template(trigger) - if trigger =~ /^(css|sty)/ - IO.read(File.join(File.dirname(__FILE__), '../../../templates/doing.css')) - else - IO.read(File.join(File.dirname(__FILE__), '../../../templates/doing.haml')) - end -end- |
-
- - - -53 -54 -55 -56- |
-
- # File 'lib/doing/hooks.rb', line 53 - -def self.insert_hook(event, priority, &block) - @hook_priority[block] = [-priority, @hook_priority.size] - @registry[event] << block -end- |
-
- - - -34 -35 -36 -37 -38- |
-
- # File 'lib/doing/hooks.rb', line 34 - -def self.priority_value(priority) - return priority if priority.is_a?(Integer) - - PRIORITY_MAP[priority] || DEFAULT_PRIORITY -end- |
-
- - - -25 -26 -27 -28 -29 -30 -31- |
-
- # File 'lib/doing/hooks.rb', line 25 - -def self.register(event, priority: DEFAULT_PRIORITY, &block) - if event.is_a?(Array) - event.each { |ev| register_one(ev, priority_value(priority), &block) } - else - register_one(event, priority_value(priority), &block) - end -end- |
-
- - - -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51- |
-
- # File 'lib/doing/hooks.rb', line 41 - -def self.register_one(event, priority, &block) - unless @registry[event] - raise Doing::Errors::HookUnavailable.new("Invalid hook. Doing only supports #{@registry.keys.inspect}", 'hook', event) - end - - raise Doing::Errors::PluginUncallable.new('Hooks must respond to :call', 'hook', event) unless block.respond_to? :call - - Doing.logger.debug('Hook Manager:', "Registered #{event} hook") if ENV['DOING_PLUGIN_DEBUG'] - - insert_hook event, priority, &block -end- |
-
- - - -58 -59 -60 -61 -62 -63 -64 -65 -66- |
-
- # File 'lib/doing/hooks.rb', line 58 - -def self.trigger(event, *args) - hooks = @registry[event] - return unless hooks.good? - - # sort and call hooks according to priority and load order - hooks.sort_by { |h| @hook_priority[h] }.each do |hook| - hook.call(*args) - end -end- |
-
- - - -36 -37 -38 -39 -40 -41 -42- |
-
- # File 'lib/doing/item/item.rb', line 36 - -def initialize(date, title, section, note = nil, id = nil) - @date = date.is_a?(Time) ? date : Time.parse(date) - @title = title - @section = section - @note = Note.new(note) - @id = id&.valid_id? ? id.strip : gen_id -end- |
-
- - - -18 -19 -20- |
-
- # File 'lib/doing/item/item.rb', line 18 - -def date - @date -end- |
-
- - - -18 -19 -20- |
-
- # File 'lib/doing/item/item.rb', line 18 - -def id - @id -end- |
-
- - - -18 -19 -20- |
-
- # File 'lib/doing/item/item.rb', line 18 - -def note - @note -end- |
-
- - - -18 -19 -20- |
-
- # File 'lib/doing/item/item.rb', line 18 - -def section - @section -end- |
-
- - - -18 -19 -20- |
-
- # File 'lib/doing/item/item.rb', line 18 - -def title - @title -end- |
-
- - - -123 -124 -125- |
-
- # File 'lib/doing/item/item.rb', line 123 - -def clone - Marshal.load(Marshal.dump(self)) -end- |
-
- - - -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66- |
-
- # File 'lib/doing/item/item.rb', line 56 - -def equal?(other, match_section: false) - return false if @title.strip != other.title.strip - - return false if @date != other.date - - return false unless @note.equal?(other.note) - - return false if match_section && !@section.equal?(other.section) - - true -end- |
-
- - - -44 -45 -46- |
-
- # File 'lib/doing/item/item.rb', line 44 - -def gen_id - Digest::MD5.hexdigest(to_s) -end- |
-
- - - -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89- |
-
- # File 'lib/doing/item/item.rb', line 79 - -def move_to(new_section, label: true, log: true) - from = @section - - tag('from', rename_to: 'from', value: from, force: true) if label - @section = new_section - - Doing.logger.count(@section == 'Archive' ? :archived : :moved) if log - Doing.logger.debug("#{@section == 'Archive' ? 'Archived' : 'Moved'}:", - "#{@title.trunc(60)} from #{from} to #{@section}") - self -end- |
-
- - - -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115- |
-
- # File 'lib/doing/item/item.rb', line 101 - -def to_pretty(elements: %i[date title section]) - output = [] - elements.each do |e| - case e - when :date - output << format('%13s |', @date.relative_date).cyan - when :section - output << "#{magenta}(#{white(@section)}#{magenta})" - when :title - output << @title.white.('cyan') - end - end - - output.join(' ') -end- |
-
- - - -92 -93 -94- |
-
- # File 'lib/doing/item/item.rb', line 92 - -def to_s - "\t- #{@date.strftime('%Y-%m-%d %H:%M')} | #{@title} <#{@id}>#{@note.good? ? "\n#{@note}" : ''}" -end- |
-
- - - -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52- |
-
- # File 'lib/doing/item/dates.rb', line 35 - -def calculate_end_date(opt) - if opt[:took] - if @date + opt[:took] > Time.now - @date = Time.now - opt[:took] - Time.now - else - @date + opt[:took] - end - elsif opt[:back] - if opt[:back].is_a? Integer - @date + opt[:back] - else - @date + (opt[:back] - @date) - end - else - Time.now - end -end- |
-
- - - -8 -9 -10 -11 -12 -13 -14- |
-
- # File 'lib/doing/item/dates.rb', line 8 - -def duration - return nil unless should_time? && should_finish? - - return nil if @title =~ /(?<=^| )@done\b/ - - return Time.now - @date -end- |
-
- - - -31 -32 -33- |
-
- # File 'lib/doing/item/dates.rb', line 31 - -def end_date - @end_date ||= Time.parse(Regexp.last_match(1)) if @title =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ -end- |
-
- - - -94 -95 -96- |
-
- # File 'lib/doing/item/dates.rb', line 94 - -def ( = nil) - @title.() -end- |
-
- - - -22 -23 -24- |
-
- # File 'lib/doing/item/dates.rb', line 22 - -def interval - @interval ||= calc_interval -end- |
-
- - - -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83- |
-
- # File 'lib/doing/item/dates.rb', line 73 - -def overlapping_time?(item_b) - return true if same_time?(item_b) - - start_a = date - a_interval = interval - end_a = a_interval ? start_a + a_interval.to_i : start_a - start_b = item_b.date - b_interval = item_b.interval - end_b = b_interval ? start_b + b_interval.to_i : start_b - (start_a >= start_b && start_a <= end_b) || (end_a >= start_b && end_a <= end_b) || (start_a < start_b && end_a > end_b) -end- |
-
- - - -61 -62 -63- |
-
- # File 'lib/doing/item/dates.rb', line 61 - -def same_time?(item_b) - date == item_b.date ? interval == item_b.interval : false -end- |
-
- - - -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98- |
-
- # File 'lib/doing/item/query.rb', line 72 - -def highlight_search(search, distance: nil, negate: false, case_type: nil) - prefs = Doing.setting('search', {}) - matching = prefs.fetch('matching', 'pattern').normalize_matching - distance ||= prefs.fetch('distance', 3).to_i - case_type ||= prefs.fetch('case', 'smart').normalize_case - new_note = Note.new - - if search.rx? || matching == :fuzzy - rx = search.to_rx(distance: distance, case_type: case_type) - new_title = @title.gsub(rx) { |m| yellow(m) } - new_note.add(@note.to_s.gsub(rx) { |m| yellow(m) }) - else - query = search.strip.to_phrase_query - - if query[:must].nil? && query[:must_not].nil? - query[:must] = query[:should] - query[:should] = [] - end - query[:must].concat(query[:should]).each do |s| - rx = Regexp.new(s.wildcard_to_rx, ignore_case(s, case_type)) - new_title = @title.gsub(rx) { |m| yellow(m) } - new_note.add(@note.to_s.gsub(rx) { |m| yellow(m) }) - end - end - - Item.new(@date, new_title, @section, new_note) -end- |
-
- - - -68 -69 -70- |
-
- # File 'lib/doing/item/query.rb', line 68 - -def ignore_case(search, case_type) - (case_type == :smart && search !~ /[A-Z]/) || case_type == :ignore -end- |
-
- - - -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242 -243 -244 -245 -246 -247 -248 -249 -250 -251 -252 -253 -254 -255 -256 -257 -258 -259 -260 -261 -262 -263 -264 -265 -266 -267 -268 -269 -270 -271 -272 -273 -274 -275 -276 -277 -278 -279 -280- |
-
- # File 'lib/doing/item/query.rb', line 162 - -def keep_item?(opt) - item = dup - time_rx = /^(\d{1,2}+(:\d{1,2}+)?( *(am|pm))?|midnight|noon)$/i - - keep = true - if opt[:unfinished] - finished = item.('done', :and) - finished = opt[:not] ? !finished : finished - keep = false if finished - end - - if keep && opt[:val]&.count&.positive? - bool = opt[:bool].normalize_bool if opt[:bool] - bool ||= :and - bool = :and if bool == :pattern - - val_match = opt[:val].nil? || opt[:val].empty? ? true : item.tag_values?(opt[:val], bool) - keep = false unless val_match - keep = opt[:not] ? !keep : keep - end - - if keep && opt[:tag] - opt[:tag_bool] = opt[:bool].normalize_bool if opt[:bool] - opt[:tag_bool] ||= :and - tag_match = opt[:tag].nil? || opt[:tag].empty? ? true : item.(opt[:tag], opt[:tag_bool]) - keep = false unless tag_match - keep = opt[:not] ? !keep : keep - end - - if keep && opt[:search] - search_match = if opt[:search].nil? || opt[:search].empty? - true - else - item.search(opt[:search], case_type: opt[:case].normalize_case) - end - - keep = false unless search_match - keep = opt[:not] ? !keep : keep - end - - if keep && opt[:date_filter]&.length == 2 - start_date = opt[:date_filter][0] - end_date = opt[:date_filter][1] - - in_date_range = if end_date - item.date >= start_date && item.date <= end_date - else - item.date.strftime('%F') == start_date.strftime('%F') - end - keep = false unless in_date_range - keep = opt[:not] ? !keep : keep - end - - if keep && opt[:time_filter][0] || opt[:time_filter][1] - opt[:time_filter].map! { |v| v =~ /(12 *am|midnight)/i ? '00:00' : v } - - start_string = if opt[:time_filter][0].nil? - "#{item.date.strftime('%Y-%m-%d')} 00:00" - else - "#{item.date.strftime('%Y-%m-%d')} #{opt[:time_filter][0]}" - end - start_time = start_string.chronify(guess: :begin) - - end_string = if opt[:time_filter][1].nil? - "#{item.date.to_datetime.next_day.strftime('%Y-%m-%d')} 00:00" - else - "#{item.date.strftime('%Y-%m-%d')} #{opt[:time_filter][1]}" - end - end_time = end_string.chronify(guess: :end) || Time.now - - in_time_range = item.date >= start_time && item.date <= end_time - - keep = false unless in_time_range - keep = opt[:not] ? !keep : keep - end - - keep = false if keep && opt[:only_timed] && !item.interval - - if keep && opt[:tag_filter] - keep = item.(opt[:tag_filter]['tags'], opt[:tag_filter]['bool']) - keep = opt[:not] ? !keep : keep - end - - if keep && opt[:before] - before = opt[:before] - cutoff = if before.is_a?(String) && before =~ time_rx - "#{item.date.strftime('%Y-%m-%d')} #{before}".chronify(guess: :begin) - elsif before.is_a?(String) - before.chronify(guess: :begin) - else - before - end - keep = cutoff && item.date <= cutoff - keep = opt[:not] ? !keep : keep - end - - if keep && opt[:after] - after = opt[:after] - cutoff = if after.is_a?(String) && after =~ time_rx - "#{item.date.strftime('%Y-%m-%d')} #{after}".chronify(guess: :end) - elsif after.is_a?(String) - after.chronify(guess: :end) - else - after - end - keep = cutoff && item.date >= cutoff - keep = opt[:not] ? !keep : keep - end - - if keep && opt[:today] - keep = item.date >= Date.today.to_time && item.date < Date.today.next_day.to_time - keep = opt[:not] ? !keep : keep - elsif keep && opt[:yesterday] - keep = item.date >= Date.today.prev_day.to_time && item.date < Date.today.to_time - keep = opt[:not] ? !keep : keep - end - - keep -end- |
-
- - - -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153- |
-
- # File 'lib/doing/item/query.rb', line 111 - -def search(search, distance: nil, negate: false, case_type: nil) - prefs = Doing.setting('search', {}) - matching = prefs.fetch('matching', 'pattern').normalize_matching - distance ||= prefs.fetch('distance', 3).to_i - case_type ||= prefs.fetch('case', 'smart').normalize_case - - if search.rx? || matching == :fuzzy - matches = @title + @note.to_s =~ search.to_rx(distance: distance, case_type: case_type) - else - query = search.strip.to_phrase_query - - if query[:must].nil? && query[:must_not].nil? - query[:must] = query[:should] - query[:should] = [] - end - matches = no_searches?(query[:must_not], case_type: case_type) - matches &&= all_searches?(query[:must], case_type: case_type) - matches &&= any_searches?(query[:should], case_type: case_type) - end - # if search =~ /(?<=\A| )[+-]\S/ - # else - # text = @title + @note.to_s - # matches = text =~ search.to_rx(distance: distance, case_type: case_type) - # end - - # if search.rx? || !fuzzy - # matches = text =~ search.to_rx(distance: distance, case_type: case_type) - # else - # distance = 0.25 if distance > 1 - # score = if (case_type == :smart && search !~ /[A-Z]/) || case_type == :ignore - # text.downcase.pair_distance_similar(search.downcase) - # else - # score = text.pair_distance_similar(search) - # end - - # if score >= distance - # matches = true - # Doing.logger.debug('Fuzzy Match:', %(#{@title}, "#{search}" #{score})) - # end - # end - - negate ? !matches : matches -end- |
-
- - - -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58- |
-
- # File 'lib/doing/item/query.rb', line 46 - -def tag_values?(queries, bool = :and, negate: false) - bool = bool.normalize_bool - - matches = case bool - when :and - all_values?(queries) - when :not - no_values?(queries) - else - any_values?(queries) - end - negate ? !matches : matches -end- |
-
- - - -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35- |
-
- # File 'lib/doing/item/query.rb', line 15 - -def (, bool = :and, negate: false) - if bool == :pattern - = ...join(' ') - matches = tag_pattern?() - - return negate ? !matches : matches - end - - = () - bool = bool.normalize_bool - - matches = case bool - when :and - () - when :not - () - else - () - end - negate ? !matches : matches -end- |
-
- - - -9 -10 -11- |
-
- # File 'lib/doing/item/state.rb', line 9 - -def finished? - ('done') -end- |
-
- - - -28 -29 -30- |
-
- # File 'lib/doing/item/state.rb', line 28 - -def should_finish? - should?('never_finish') -end- |
-
- - - -38 -39 -40- |
-
- # File 'lib/doing/item/state.rb', line 38 - -def should_time? - should?('never_time') -end- |
-
- - - -18 -19 -20- |
-
- # File 'lib/doing/item/state.rb', line 18 - -def unfinished? - ('done', negate: true) -end- |
-
- - - -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51- |
-
- # File 'lib/doing/item/tags.rb', line 20 - -def tag(, **) - added = [] - removed = [] - - date = .fetch(:date, false) - [:value] ||= date ? Time.now.strftime('%F %R') : nil - .delete(:date) - - single = .fetch(:single, false) - .delete(:single) - - = . if .is_a? ::String - - remove = .fetch(:remove, false) - .each do |tag| - if tag =~ /^(\S+)\((.*?)\)$/ - m = Regexp.last_match - tag = m[1] - [:value] ||= m[2] - end - - bool = remove ? :and : :not - if (tag, bool) || [:value] - @title = @title.tag(tag, **).strip - remove ? removed.push(tag) : added.push(tag) - end - end - - Doing.logger.log_change(tags_added: added, tags_removed: removed, count: 1, item: self, single: single) - - self -end- |
-
- - - -77 -78 -79- |
-
- # File 'lib/doing/item/tags.rb', line 77 - -def tag_array - . -end- |
-
- - - -58 -59 -60- |
-
- # File 'lib/doing/item/tags.rb', line 58 - -def - @title.scan(/(?<= |\A)@([^\s(]+)/).map { |tag| tag[0] }.sort.uniq -end- |
-
- - - -68 -69 -70- |
-
- # File 'lib/doing/item/tags.rb', line 68 - -def - @title.scan(/(?<= |\A)@([^\s(]+)(?:\((.*?)\))?/).map { |tag| [tag[0], tag[1]] }.sort.uniq -end- |
-
- - - -13 -14 -15 -16- |
-
- # File 'lib/doing/items/items.rb', line 13 - -def initialize - super - @sections = [] -end- |
-
- - - -11 -12 -13- |
-
- # File 'lib/doing/items/items.rb', line 11 - -def sections - @sections -end- |
-
- - - -59 -60 -61 -62 -63 -64 -65 -66- |
-
- # File 'lib/doing/items/sections.rb', line 59 - -def add_section(section, log: false) - section = section.is_a?(Section) ? section : Section.new(section.cap_first) - - return if section?(section) - - @sections.push(section) - Doing.logger.info('New section:', %("#{section}" added)) if log -end- |
-
- - - -26 -27 -28 -29 -30- |
-
- # File 'lib/doing/items/util.rb', line 26 - -def - each_with_object([]) do |entry, | - .concat(entry.).sort!.uniq! - end -end- |
-
- - - -62 -63 -64 -65 -66- |
-
- # File 'lib/doing/items/filter.rb', line 62 - -def between_dates(start, finish) - start = start.chronify(guess: :begin, future: false) if start.is_a?(String) - finish = finish.chronify(guess: :end) if finish.is_a?(String) - WWID.new.filter_items(self, opt: { date_filter: [start, finish] }) -end- |
-
- - - -60 -61 -62 -63 -64 -65 -66 -67- |
-
- # File 'lib/doing/items/util.rb', line 60 - -def dedup(match_section: true) - unique = Items.new - each do |item| - unique.push(item) unless unique.include?(item, match_section: match_section) - end - - unique -end- |
-
- - - -70 -71 -72- |
-
- # File 'lib/doing/items/util.rb', line 70 - -def dedup!(match_section: true) - replace dedup(match_section: match_section) -end- |
-
- - - -10 -11 -12 -13 -14 -15 -16 -17 -18 -19- |
-
- # File 'lib/doing/items/util.rb', line 10 - -def delete(item) - deleted = nil - each_with_index do |i, idx| - if i.equal?(item, match_section: true) - deleted = delete_at(idx) - break - end - end - deleted -end- |
-
- - - -10 -11 -12 -13 -14 -15- |
-
- # File 'lib/doing/items/modify.rb', line 10 - -def delete_item(item, single: false) - deleted = delete(item) - Doing.logger.count(:deleted) - Doing.logger.info('Entry deleted:', deleted.title) if single - deleted -end- |
-
- - - -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81- |
-
- # File 'lib/doing/items/sections.rb', line 68 - -def delete_section(section, log: false) - return unless section?(section) - - raise DoingRuntimeError, 'Section not empty' if in_section(section).count.positive? - - @sections.each do |sect| - next unless sect.title == section && in_section(sect).count.zero? - - @sections.delete(sect) - Doing.logger.info('Removed section:', %("#{section}" removed)) if log - end - - Doing.logger.error('Not found:', %("#{section}" not found)) -end- |
-
- - - -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53- |
-
- # File 'lib/doing/items/util.rb', line 40 - -def diff(items) - a = clone - b = items.clone - - a.delete_if do |item| - if b.include?(item) - b.delete(item) - true - else - false - end - end - { added: b, deleted: a } -end- |
-
- - - -42 -43 -44- |
-
- # File 'lib/doing/items/items.rb', line 42 - -def find_id(id) - select { |item| item.id == id }[0] -end- |
-
- - - -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44- |
-
- # File 'lib/doing/items/sections.rb', line 32 - -def guess_section(frag, distance: 2) - section = nil - re = frag.to_rx(distance: distance, case_type: :ignore) - @sections.each do |sect| - next unless sect.title =~ /#{re}/i - - Doing.logger.debug('Match:', %(Assuming "#{sect.title}" from "#{frag}")) - section = sect - break - end - - section -end- |
-
- - - -12 -13 -14 -15 -16 -17 -18 -19 -20 -21- |
-
- # File 'lib/doing/items/filter.rb', line 12 - -def in_section(section) - sect = section.is_a?(Section) ? section.title : section - if sect =~ /^all$/i - dup - else - items = Items.new.concat(select { |item| !item.nil? && item.section == section }) - items.add_section(section, log: false) - items - end -end- |
-
- - - -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36- |
-
- # File 'lib/doing/items/items.rb', line 26 - -def include?(item, match_section: true) - includes = false - each do |other_item| - if other_item.equal?(item, match_section: match_section) - includes = true - break - end - end - - includes -end- |
-
- - - -51 -52 -53 -54 -55 -56 -57 -58 -59 -60- |
-
- # File 'lib/doing/items/items.rb', line 51 - -def index_for_id(id) - i = nil - each_with_index do |item, idx| - if item.id == id - i = idx - break - end - end - i -end- |
-
- - - -32 -33 -34- |
-
- # File 'lib/doing/items/filter.rb', line 32 - -def search(query, case_type: :smart) - WWID.new.fuzzy_filter_items(self, query, case_type: case_type) -end- |
-
- - - -19 -20 -21 -22- |
-
- # File 'lib/doing/items/sections.rb', line 19 - -def section?(section) - section = section.is_a?(Section) ? section.title.downcase : section.downcase - @sections.map { |i| i.title.downcase }.include?(section) -end- |
-
- - - -9 -10 -11- |
-
- # File 'lib/doing/items/sections.rb', line 9 - -def section_titles - @sections.map(&:title) -end- |
-
- - - -46 -47 -48- |
-
- # File 'lib/doing/items/filter.rb', line 46 - -def tagged(, bool: :and) - WWID.new.filter_items(self, opt: { tag: , bool: bool }) -end- |
-
- - - -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73- |
-
- # File 'lib/doing/items/items.rb', line 63 - -def to_s - out = [] - @sections.each do |section| - out.push(section.original) - items = in_section(section.title).sort_by { |i| [i.date, i.title] } - items.reverse! if Doing.setting('doing_file_sort').normalize_order == :desc - items.each { |item| out.push(item.to_s) } - end - - out.join("\n") -end- |
-
- - - -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34- |
-
- # File 'lib/doing/items/modify.rb', line 23 - -def update_item(old_item, new_item) - s_idx = index { |item| item.equal?(old_item) } - - raise ItemNotFound, 'Unable to find item in index, did it mutate?' unless s_idx - - return if fetch(s_idx).equal?(new_item) - - self[s_idx] = new_item - Doing.logger.count(:updated) - Doing.logger.info('Entry updated:', self[s_idx].title.trunc(60)) - new_item -end- |
-
- - - -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148- |
-
- # File 'lib/doing/plugins/export/json_export.rb', line 17 - -def self.render(wwid, items, variables: {}) - if items.nil? || items.empty? - return case variables[:options][:output] - when 'json' - { - 'section' => '', - 'items' => [], - 'timers' => "" - }.to_json - when 'timeline' - "<html></html>" - end - end - - opt = variables[:options] - opt[:output] = case opt[:output] - when /^t/ - 'timeline' - else - 'json' - end - items_out = [] - - last_date = items[-1].date + (60 * 60 * 24) - max = last_date.strftime('%F') - min = items[0].date.strftime('%F') - items.each_with_index do |i, index| - title = i.title.utf8 - note = i.note.utf8 - - end_date = i.end_date || '' - interval = wwid.get_interval(i, formatted: false) || 0 - duration = i.duration || 0 - note ||= '' - - = [] - attributes = {} - = %w[meanwhile done cancelled flagged] - i.title.scan(/@([^(\s]+)(?:\((.*?)\))?/).each do |tag| - .push(tag[0]) unless .include?(tag[0]) - attributes[tag[0]] = tag[1] if tag[1] - end - - case opt[:output] - when 'json' - i = { - date: i.date, - end_date: end_date, - title: title.strip, #+ " #{note}" - section: i.section, - note: note.to_s(prefix: ''), - time: interval.time_string(format: :clock), - duration: duration.time_string(format: :clock), - tags: , - id: i.id - } - - attributes.each { |attr, val| i[attr.to_sym] = val } - - items_out << i - - when 'timeline' - new_item = { - 'id' => index + 1, - 'content' => title.strip, #+ " #{note}" - 'title' => title.strip + " (#{interval.time_string(format: :clock)})", - 'start' => i.date.strftime('%F %T'), - 'type' => 'box', - 'style' => 'color:#4c566b;background-color:#d8dee9;' - } - - if interval.to_i&.positive? - new_item['end'] = end_date.strftime('%F %T') - if interval.to_i > 3600 - new_item['type'] = 'range' - new_item['style'] = 'color:white;background-color:#a2bf8a;' - end - end - new_item['style'] = 'color:white;background-color:#f7921e;' if i.(Doing.setting('marker_tag')) - items_out.push(new_item) - end - end - case opt[:output] - when 'json' - Doing.logger.debug('JSON Export:', "#{items_out.count} items output to JSON") - JSON.pretty_generate({ - 'section' => variables[:page_title], - 'items' => items_out, - 'timers' => wwid.tag_times(format: :json, - sort_by: opt[:sort_tags], - sort_order: opt[:tag_order]) - }) - when 'timeline' - template = <<~EOTEMPLATE - <!doctype html> - <html> - <head> - <link href="https://unpkg.com/vis-timeline@7.4.9/dist/vis-timeline-graph2d.min.css" rel="stylesheet" type="text/css" /> - <script src="https://unpkg.com/vis-timeline@7.4.9/dist/vis-timeline-graph2d.min.js"></script> - </head> - <body> - <div id="mytimeline"></div> - #{' '} - <script type="text/javascript"> - // DOM element where the Timeline will be attached - var container = document.getElementById('mytimeline'); - #{' '} - // Create a DataSet with data (enables two way data binding) - var data = new vis.DataSet(#{items_out.to_json}); - #{' '} - // Configuration for the Timeline - var options = { - width: '100%', - height: '800px', - margin: { - item: 20 - }, - stack: true, - min: '#{min}', - max: '#{max}' - }; - #{' '} - // Create a Timeline - var timeline = new vis.Timeline(container, data, options); - </script> - </body> - </html> - EOTEMPLATE - Doing.logger.debug('Timeline Export:', "#{items_out.count} items output to Timeline") - template - end -end- |
-
- - - -11 -12 -13 -14 -15- |
-
- # File 'lib/doing/plugins/export/json_export.rb', line 11 - -def self.settings - { - trigger: 'json|time(?:line)?' - } -end- |
-
- - - -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90- |
-
- # File 'lib/doing/plugins/import/json_import.rb', line 26 - -def self.import(wwid, path, options: {}) - exit_now! 'Path to JSON export required' if path.nil? - [:no_overlap] ||= false - [:autotag] ||= Doing.auto_tag - - exit_now! 'File not found' unless File.exist?(File.(path)) - - updated = 0 - added = 0 - skipped = 0 - - data = JSON.parse(IO.read(File.(path))) - new_items = [] - new_section = [:section] || Doing.setting('current_section') - - data['items'].each do |entry| - title = entry['title'] - date = Time.parse(entry['date']) - date ||= entry['date'].chronify - note = Doing::Note.new(entry['note']) - section = if entry['section'].empty? - new_section - else - entry['section'] - end - id = entry.key?('id') ? entry['id'] : nil - - new_item = Doing::Item.new(date, title, section, note, id) - - is_match = true - - if [:search] - is_match = new_item.search([:search], case_type: [:case], negate: [:not]) - end - - if is_match && [:date_filter] - is_match = start_time > [:date_filter][0] && start_time < [:date_filter][1] - is_match = [:not] ? !is_match : is_match - end - - unless is_match - skipped += 1 - next - - end - - if wwid.content.find_id(new_item.id) - old_index = wwid.content.index_for_id(entry['id']) - old_item = wwid.content[old_index].clone - wwid.content[old_index] = new_item - Hooks.trigger :post_entry_updated, self, new_item, old_item - updated += 1 - else - Hooks.trigger :pre_entry_add, self, item - wwid.content << new_entry - Hooks.trigger :post_entry_added, self, item - added += 1 - end - end - total = new_items.count - skipped = data.count - total - Doing.logger.debug('Skipped:', %(#{skipped} items)) if skipped.positive? - Doing.logger.info('Updated:', %(#{updated} items)) - Doing.logger.info('Imported:', %(#{added} new items to #{new_section})) -end- |
-
- - - -12 -13 -14 -15 -16- |
-
- # File 'lib/doing/plugins/import/json_import.rb', line 12 - -def self.settings - { - trigger: 'json' - } -end- |
-
- - - -49 -50 -51 -52 -53 -54 -55 -56 -57 -58- |
-
- # File 'lib/doing/logger.rb', line 49 - -def initialize(level = :info) - @messages = [] - @counters = {} - COUNT_KEYS.each { |key| @counters[key] = { tag: [], count: 0 } } - @results = [] - @logdev = $stderr - @max_length = TTY::Screen.columns - 5 || 85 - self.log_level = level - @prev_level = level -end- |
-
- - - -15 -16 -17- |
-
- # File 'lib/doing/logger.rb', line 15 - -def level - @level -end- |
-
- - - -9 -10 -11- |
-
- # File 'lib/doing/logger.rb', line 9 - -def logdev=(value) - @logdev = value -end- |
-
- - - -12 -13 -14- |
-
- # File 'lib/doing/logger.rb', line 12 - -def max_length=(value) - @max_length = value -end- |
-
- - - -17 -18 -19- |
-
- # File 'lib/doing/logger.rb', line 17 - -def - @messages -end- |
-
- - - -17 -18 -19- |
-
- # File 'lib/doing/logger.rb', line 17 - -def results - @results -end- |
-
- - - -187 -188 -189 -190- |
-
- # File 'lib/doing/logger.rb', line 187 - -def abort_with(topic, = nil, &block) - error(topic, , &block) - abort -end- |
-
- - - -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110- |
-
- # File 'lib/doing/logger.rb', line 100 - -def adjust_verbosity( = {}) - if [:log_level] - self.log_level = [:log_level].to_sym - elsif [:quiet] - self.log_level = :error - elsif [:verbose] || [:debug] - self.log_level = :debug - end - log_now :debug, 'Logging at level:', @level.to_s - # log_now :debug, 'Doing Version:', Doing::VERSION -end- |
-
- - - -270 -271 -272 -273 -274 -275 -276- |
-
- # File 'lib/doing/logger.rb', line 270 - -def benchmark(key, state) - return unless ENV['DOING_BENCHMARK'] - - @benchmarks ||= {} - @benchmarks[key] ||= { start: nil, finish: nil } - @benchmarks[key][state] = Process.clock_gettime(Process::CLOCK_MONOTONIC) -end- |
-
- - - -112 -113 -114 -115 -116 -117 -118 -119- |
-
- # File 'lib/doing/logger.rb', line 112 - -def count(key, level: :info, count: 1, tag: nil, message: nil) - raise ArgumentError, 'invalid counter key' unless COUNT_KEYS.include?(key) - - @counters[key][:count] += count - @counters[key][:tag].concat(tag).sort.uniq unless tag.nil? - @counters[key][:level] ||= level - @counters[key][:message] ||= -end- |
-
- - - -129 -130 -131- |
-
- # File 'lib/doing/logger.rb', line 129 - -def debug(topic, = nil, &block) - write(:debug, topic, , &block) -end- |
-
- - - -171 -172 -173- |
-
- # File 'lib/doing/logger.rb', line 171 - -def error(topic, = nil, &block) - write(:error, topic, , &block) -end- |
-
- - - -202 -203 -204 -205 -206 -207 -208 -209 -210- |
-
- # File 'lib/doing/logger.rb', line 202 - -def formatted_topic(topic, colon: false) - if colon - "#{topic}: ".rjust(TOPIC_WIDTH) - elsif topic =~ /:$/ - "#{topic} ".rjust(TOPIC_WIDTH) - else - "#{topic} " - end -end- |
-
- - - -143 -144 -145- |
-
- # File 'lib/doing/logger.rb', line 143 - -def info(topic, = nil, &block) - write(:info, topic, , &block) -end- |
-
- - - -278 -279 -280 -281 -282 -283 -284 -285 -286 -287 -288 -289 -290 -291 -292 -293 -294 -295 -296 -297 -298 -299 -300 -301 -302 -303 -304 -305 -306 -307 -308 -309 -310 -311 -312 -313 -314 -315 -316 -317 -318 -319 -320 -321 -322 -323 -324 -325- |
-
- # File 'lib/doing/logger.rb', line 278 - -def log_benchmarks - if ENV['DOING_BENCHMARK'] - - output = [] - beginning = @benchmarks[:total][:start] - ending = @benchmarks[:total][:finish] - total = ending - beginning - factor = TTY::Screen.columns / total - - cols = Array.new(TTY::Screen.columns) - - colors = %w[bgred bggreen bgyellow bgblue bgmagenta bgcyan bgwhite boldbgred boldbggreen boldbgyellow boldbgblue boldbgwhite] - idx = 0 - # @benchmarks.delete(:total) - - @benchmarks.sort_by { |_, timers| [timers[:start], timers[:finish]] }.each do |k, timers| - if timers[:finish] && timers[:start] - color = colors[idx % colors.count] - fg = if idx < 7 - Color.boldblack - else - Color.boldwhite - end - color = Color.send(color) + fg - - start = ((timers[:start] - beginning) * factor).floor - finish = ((timers[:finish] - beginning) * factor).ceil - - cols.fill("#{color}-", start..finish) - cols[start] = "#{color}|" - cols[finish] = "#{color}|" - output << "#{color}#{k}#{Color.default}: #{timers[:finish] - timers[:start]}" - else - output << "#{k}: error" - end - - idx += 1 - end - - output.each do |msg| - $stdout.puts (:debug, 'Benchmark:', msg) - end - - $stdout.puts (:debug, 'Benchmark:', "Total: #{total}") - - $stdout.puts cols[0..TTY::Screen.columns-1].join + Color.reset - end -end- |
-
- - - -327 -328 -329 -330 -331 -332 -333 -334 -335 -336 -337 -338 -339 -340 -341 -342 -343 -344 -345 -346 -347 -348 -349 -350 -351 -352 -353 -354 -355 -356 -357- |
-
- # File 'lib/doing/logger.rb', line 327 - -def log_change(tags_added: [], tags_removed: [], count: 1, item: nil, single: false) - if .empty? && .empty? - count(:skipped, level: :debug, message: '%count %items with no change', count: count) - else - if .empty? - count(:skipped, level: :debug, message: 'no tags added to %count %items') - elsif single && item - elapsed = if item && .include?('done') - item.interval ? " (#{item.interval&.time_string(format: :dhm)})" : '' - else - '' - end - - added = . - info('Tagged:', - %(added #{.count == 1 ? 'tag' : 'tags'} #{added}#{elapsed} to #{item.title})) - else - count(:added_tags, level: :info, tag: , message: '%tags added to %count %items') - end - - if .empty? - count(:skipped, level: :debug, message: 'no tags removed from %count %items') - elsif single && item - removed = . - info('Untagged:', - %(removed #{.count == 1 ? 'tag' : 'tags'} #{removed} from #{item.title})) - else - count(:removed_tags, level: :info, tag: , message: '%tags removed from %count %items') - end - end -end- |
-
- - - -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82- |
-
- # File 'lib/doing/logger.rb', line 67 - -def log_level=(level = 'info') - level = level.to_s - - level = case level - when /^[e0]/i - :error - when /^[w1]/i - :warn - when /^[d3]/i - :debug - else - :info - end - - @level = level -end- |
-
- - - -241 -242 -243 -244 -245 -246 -247 -248 -249- |
-
- # File 'lib/doing/logger.rb', line 241 - -def log_now(level, topic, = nil, &block) - return false unless (level) - - if @logdev == $stdout - @logdev.puts (topic, , &block) - else - @logdev.puts (level, topic, , &block) - end -end- |
-
- - - -256 -257 -258 -259 -260 -261 -262 -263 -264 -265 -266 -267 -268- |
-
- # File 'lib/doing/logger.rb', line 256 - -def output_results - total_counters - results = @results.select { |msg| (msg[:level]) }.uniq - - if @logdev == $stdout - $stdout.print results.map { |res| res[:message].uncolor }.join("\n") - $stdout.puts - else - results.each do |msg| - @logdev.puts (msg[:level], msg[:message]) - end - end -end- |
-
- - - -93 -94 -95 -96 -97 -98- |
-
- # File 'lib/doing/logger.rb', line 93 - -def restore_level - return if @prev_level.nil? || @prev_level == @log_level - - self.log_level = @prev_level - @prev_level = nil -end- |
-
- - - -85 -86 -87 -88 -89 -90- |
-
- # File 'lib/doing/logger.rb', line 85 - -def temp_level(level) - return if level.nil? || level.to_sym == @log_level - - @prev_level = log_level.dup - @log_level = level.to_sym -end- |
-
- - - -157 -158 -159- |
-
- # File 'lib/doing/logger.rb', line 157 - -def warn(topic, = nil, &block) - write(:warn, topic, , &block) -end- |
-
- - - -228 -229 -230 -231- |
-
- # File 'lib/doing/logger.rb', line 228 - -def write(, topic, = nil, &block) - @results << { level: , message: (topic, , &block) } - true -end- |
-
- - - -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86- |
-
- # File 'lib/doing/plugins/export/markdown_export.rb', line 38 - -def self.render(wwid, items, variables: {}) - return if items.nil? - - opt = variables[:options] - - all_items = [] - items.each do |i| - if String.method_defined? :force_encoding - title = i.title.force_encoding('utf-8').link_urls(format: :markdown) - note = i.note.map { |line| line.force_encoding('utf-8').strip.link_urls(format: :markdown) } if i.note - else - title = i.title.link_urls(format: :markdown) - note = i.note.map { |line| line.strip.link_urls(format: :markdown) } if i.note - end - - title = "#{title} @section(#{i.section})" unless variables[:is_single] - - interval = wwid.get_interval(i, record: true) if i.title =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times] - interval ||= false - - finished = i.title =~ /(?<= |^)@done/ ? true : false - done = finished ? 'x' : ' ' - - all_items << { - date: i.date.strftime('%a %-I:%M%p'), - shortdate: i.date.relative_date, - flagged: i.title =~ /(?<= |^)@#{Doing.setting('marker_tag')}/, - done: done, - finished: finished, - note: note, - section: i.section, - time: interval, - title: title.strip - } - end - - template = if Doing.setting('export_templates.markdown') && File.exist?(File.(Doing.setting('export_templates.markdown'))) - IO.read(File.(Doing.setting('export_templates.markdown'))) - else - self.template(nil) - end - - totals = opt[:totals] ? wwid.tag_times(format: :markdown, sort_by: opt[:sort_tags], sort_order: opt[:tag_order]) : '' - - mdx = MarkdownRenderer.new(variables[:page_title], all_items, totals) - Doing.logger.debug('Markdown Export:', "#{all_items.count} items output to Markdown") - engine = ERB.new(template) - @out = engine.result(mdx.get_binding) -end- |
-
- - - -27 -28 -29 -30 -31 -32- |
-
- # File 'lib/doing/plugins/export/markdown_export.rb', line 27 - -def self.settings - { - trigger: 'markdown|mk?d|gfm', - templates: [{ name: 'markdown', trigger: 'mk?d|markdown', format: 'erb', filename: 'doing-markdown.erb' }] - } -end- |
-
- - - -34 -35 -36- |
-
- # File 'lib/doing/plugins/export/markdown_export.rb', line 34 - -def self.template(_trigger) - IO.read(File.join(File.dirname(__FILE__), '../../../templates/doing-markdown.erb')) -end- |
-
- - - -14 -15 -16 -17 -18- |
-
- # File 'lib/doing/note.rb', line 14 - -def initialize(note = []) - super() - - add(note) if note -end- |
-
- - - -28 -29 -30 -31 -32 -33 -34 -35 -36- |
-
- # File 'lib/doing/note.rb', line 28 - -def add(note, replace: false) - clear if replace - case note - when String - append_string(note) - when Array - append(note) - end -end- |
-
- - - -43 -44 -45- |
-
- # File 'lib/doing/note.rb', line 43 - -def compress - delete_if { |l| l =~ /^\s*$/ || l =~ /^#/ } -end- |
-
- - - -47 -48 -49- |
-
- # File 'lib/doing/note.rb', line 47 - -def compress! - replace compress -end- |
-
- - - -99 -100 -101 -102 -103- |
-
- # File 'lib/doing/note.rb', line 99 - -def equal?(other) - return false unless other.is_a?(Note) - - to_s == other.to_s -end- |
-
- - - -57 -58 -59- |
-
- # File 'lib/doing/note.rb', line 57 - -def strip_lines - Note.new(map(&:strip)) -end- |
-
- - - -61 -62 -63- |
-
- # File 'lib/doing/note.rb', line 61 - -def strip_lines! - replace strip_lines -end- |
-
- - - -83 -84 -85- |
-
- # File 'lib/doing/note.rb', line 83 - -def to_line(separator: ' ') - compress.strip_lines.join(separator) -end- |
-
- - - -70 -71 -72- |
-
- # File 'lib/doing/note.rb', line 70 - -def to_s(prefix: "\t\t") - compress.strip_lines.map { |l| "#{prefix}#{l}" }.join("\n") -end- |
-
- - - -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64- |
-
- # File 'lib/doing/pager.rb', line 26 - -def page(text) - unless @paginate - puts text - return - end - - pager = which_pager - Doing.logger.debug('Pager:', "Using #{pager}") - - read_io, write_io = IO.pipe - - input = $stdin - - pid = Kernel.fork do - write_io.close - input.reopen(read_io) - read_io.close - - # Wait until we have input before we start the pager - IO.select [input] - - begin - exec(pager) - rescue SystemCallError => e - raise Errors::DoingStandardError, "Pager error, #{e}" - end - end - - begin - read_io.close - write_io.write(text) - write_io.close - rescue SystemCallError # => e - # raise Errors::DoingStandardError, "Pager error, #{e}" - end - - _, status = Process.waitpid2(pid) - status.success? -end- |
-
- - - -10 -11 -12- |
-
- # File 'lib/doing/pager.rb', line 10 - -def paginate - @paginate ||= false -end- |
-
- - - -17 -18 -19- |
-
- # File 'lib/doing/pager.rb', line 17 - -def paginate=(should_paginate) - @paginate = should_paginate -end- |
-
- - - -182 -183 -184 -185- |
-
- # File 'lib/doing/plugin_manager.rb', line 182 - -def available_plugins(type: :export) - type = valid_type(type) - plugins[type].keys.sort -end- |
-
- - - -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173- |
-
- # File 'lib/doing/plugin_manager.rb', line 158 - -def list_plugins( = {}) - separator = [:column] ? "\n" : "\t" - type = [:type].nil? || [:type] =~ /all/i ? 'all' : valid_type([:type]) - - case type - when :import - puts plugin_names(type: :import, separator: separator) - when :export - puts plugin_names(type: :export, separator: separator) - else - print 'Import plugins: ' - puts plugin_names(type: :import, separator: ', ') - print 'Export plugins: ' - puts plugin_names(type: :export, separator: ', ') - end -end- |
-
- - - -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47- |
-
- # File 'lib/doing/plugin_manager.rb', line 28 - -def load_plugins(add_dir = nil) - plugins_path(add_dir).each do |plugin_search_path| - Dir.glob(File.join(plugin_search_path, '**', '*.rb')).sort.each do |plugin| - require plugin - end - end - - Gem.find_latest_files('doing-plugin-*', true).sort.each do |plugin| - load plugin - end - - # Gem.path.each do |path| - # $LOAD_PATH.unshift path - # Dir.glob(File.join(path, 'gems', 'doing-plugin-*', 'lib', '*.rb')).sort.each do |plugin| - # require plugin.sub(%r{#{path}/gems/(.*?)-[\d.]+$}, '\1') - # end - # end - - plugins -end- |
-
- - - -195 -196 -197 -198- |
-
- # File 'lib/doing/plugin_manager.rb', line 195 - -def plugin_names(type: :export, separator: '|') - type = valid_type(type) - available_plugins(type: type).join(separator) -end- |
-
- - - -209 -210 -211 -212 -213 -214 -215 -216- |
-
- # File 'lib/doing/plugin_manager.rb', line 209 - -def plugin_regex(type: :export) - type = valid_type(type) - pattern = [] - plugins[type].each do |_, | - pattern << [:trigger].normalize_trigger - end - Regexp.new("^(?:#{pattern.sort.uniq.join('|')})$", true) -end- |
-
- - - -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239- |
-
- # File 'lib/doing/plugin_manager.rb', line 226 - -def plugin_templates(type: :export) - type = valid_type(type) - templates = [] - plugs = plugins[type].clone - plugs.delete_if { |_t, o| o[:templates].nil? }.each do |_, | - [:templates].each do |t| - out = t[:name] - out += " (#{t[:format]})" if t.key?(:format) - templates << out - end - end - - templates.sort.uniq -end- |
-
- - - -18 -19 -20 -21 -22 -23- |
-
- # File 'lib/doing/plugin_manager.rb', line 18 - -def plugins - @plugins ||= { - import: {}, - export: {} - } -end- |
-
- - - -56 -57 -58 -59 -60- |
-
- # File 'lib/doing/plugin_manager.rb', line 56 - -def plugins_path(add_dir = nil) - paths = Array(File.join(File.dirname(__FILE__), 'plugins')) - paths << File.join(add_dir) if add_dir - paths.map { |d| File.(d) } -end- |
-
- - - -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100- |
-
- # File 'lib/doing/plugin_manager.rb', line 74 - -def register(title, type, klass) - type = validate_plugin(title, type, klass) - return unless type - - if title.is_a?(Array) - title.each { |t| register(t, type, klass) } - return - end - - settings = if klass.respond_to? :settings - klass.settings - else - { trigger: title.normalize_trigger, config: {} } - end - - plugins[type] ||= {} - plugins[type][title] = { - trigger: settings[:trigger].normalize_trigger || title.normalize_trigger, - class: klass, - templates: settings[:templates] || nil, - config: settings[:config] || {} - } - - return unless ENV['DOING_PLUGIN_DEBUG'] - - Doing.logger.debug('Plugin Manager:', "Registered #{type} plugin \"#{title}\"") -end- |
-
- - - -276 -277 -278 -279 -280 -281 -282 -283 -284 -285 -286 -287 -288 -289 -290- |
-
- # File 'lib/doing/plugin_manager.rb', line 276 - -def template_for_trigger(trigger, type: :export, save_to: nil) - plugins[valid_type(type)].clone.delete_if { |_t, o| o[:templates].nil? }.each do |_, | - [:templates].each do |t| - next unless trigger =~ /^(?:#{t[:trigger].normalize_trigger})$/ - - tpl = [:class].template(trigger) - return tpl unless save_to - - raise PluginException.new('No default filename defined', :export, t[:name]) unless t.key?(:filename) - - return save_template(tpl, save_to, t[:filename]) - end - end - raise Errors::InvalidArgument, "No template type matched \"#{trigger}\"" -end- |
-
- - - -250 -251 -252 -253 -254 -255 -256 -257 -258 -259 -260- |
-
- # File 'lib/doing/plugin_manager.rb', line 250 - -def template_regex(type: :export) - type = valid_type(type) - pattern = [] - plugs = plugins[type].clone - plugs.delete_if { |_, o| o[:templates].nil? }.each do |_, | - [:templates].each do |t| - pattern << t[:trigger].normalize_trigger - end - end - Regexp.new("^(?:#{pattern.join('|')})$", true) -end- |
-
- - - -8 -9 -10- |
-
- # File 'lib/doing/plugin_manager.rb', line 8 - -def user_home - @user_home ||= Util.user_home -end- |
-
- - - -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149- |
-
- # File 'lib/doing/plugin_manager.rb', line 135 - -def valid_type(type, default: nil) - type ||= default - - t = type.to_s - type = case t - when /^i(m(p(o(r(t)?)?)?)?)?$/ - :import - when /^e(x(p(o(r(t)?)?)?)?)?$/ - :export - else - raise Errors::InvalidPluginType.new('Invalid plugin type', 'unrecognized') - end - - type.to_sym -end- |
-
- - - -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124- |
-
- # File 'lib/doing/plugin_manager.rb', line 113 - -def validate_plugin(title, type, klass) - type = valid_type(type) - if type == :import && !klass.respond_to?(:import) - raise Errors::PluginUncallable.new('Import plugins must respond to :import', type, title) - end - - if type == :export && !klass.respond_to?(:render) - raise Errors::PluginUncallable.new('Export plugins must respond to :render', type, title) - end - - type -end- |
-
- - - -36 -37 -38- |
-
- # File 'lib/doing/prompt/prompt.rb', line 36 - -def default_answer - @default_answer ||= false -end- |
-
- - - -27 -28 -29- |
-
- # File 'lib/doing/prompt/prompt.rb', line 27 - -def force_answer - @force_answer ||= nil -end- |
-
- - - -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35- |
-
- # File 'lib/doing/prompt/choose.rb', line 17 - -def choose_from(, prompt: 'Make a selection: ', multiple: false, sorted: true, fzf_args: []) - return nil unless $stdout.isatty - - # fzf_args << '-1' # User is expecting a menu, and even if only one it serves as confirmation - default_args = [] - default_args << %(--prompt="#{prompt}") - default_args << "--height=#{.count + 2}" - default_args << '--info=inline' - default_args << '--multi' if multiple - header = "esc: cancel,#{multiple ? ' tab: multi-select, ctrl-a: select all,' : ''} return: confirm" - default_args << %(--header="#{header}") - default_args.concat(fzf_args) - .sort! if sorted - - res = `echo #{Shellwords.escape(.join("\n"))}|#{fzf} #{default_args.join(' ')}` - return false if res.strip.size.zero? - - res -end- |
-
- - - -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116- |
-
- # File 'lib/doing/prompt/choose.rb', line 53 - -def choose_from_items(items, **opt) - return items unless $stdout.isatty - - return nil unless items.count.positive? - - case_sensitive = opt.fetch(:case, :smart).normalize_case - header = opt.fetch(:header, 'Arrows: navigate, tab: mark for selection, ctrl-a: select all, enter: commit') - prompt = opt.fetch(:prompt, 'Select entries to act on > ') - query = opt.fetch(:query) { opt.fetch(:search, '') } - include_section = opt.fetch(:include_section, false) - - pad = items.length.to_s.length - = items.map.with_index do |item, i| - out = [ - format("%#{pad}d", i), - ') ', - format('%16s', item.date.strftime('%Y-%m-%d %H:%M')), - ' | ', - item.title - ] - if include_section - out.concat([ - ' (', - item.section, - ') ' - ]) - end - out.join('') - end - - fzf_args = [ - %(--header="#{header}"), - %(--prompt="#{prompt.sub(/ *$/, ' ')}"), - opt.fetch(:multiple) ? '--multi' : '--no-multi', - '-0', - '--bind ctrl-a:select-all', - %(-q "#{query}"), - '--info=inline' - ] - fzf_args.push('-1') unless opt.fetch(:show_if_single, false) - fzf_args << case case_sensitive - when :sensitive - '+i' - when :ignore - '-i' - end - fzf_args << '-e' if opt.fetch(:exact, false) - - - unless opt.fetch(:menu) - raise InvalidArgument, "Can't skip menu when no query is provided" unless query && !query.empty? - - fzf_args.concat([%(--filter="#{query}"), opt.fetch(:sort) ? '' : '--no-sort']) - end - res = `echo #{Shellwords.escape(.join("\n"))}|#{fzf} #{fzf_args.join(' ')}` - - selected = [] - res.split(/\n/).each do |item| - idx = item.match(/^ *(\d+)\)/)[1].to_i - selected.push(items[idx]) - end - - opt.fetch(:multiple) ? selected : selected[0] -end- |
-
- - - -11 -12 -13- |
-
- # File 'lib/doing/prompt/fzf.rb', line 11 - -def fzf - @fzf ||= install_fzf -end- |
-
- - - -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82- |
-
- # File 'lib/doing/prompt/fzf.rb', line 47 - -def install_fzf(force: false) - if force - uninstall_fzf - elsif which_fzf - return which_fzf - end - - fzf_dir = File.join(File.dirname(__FILE__), '../../helpers/fzf') - FileUtils.mkdir_p(fzf_dir) unless File.directory?(fzf_dir) - fzf_bin = File.join(fzf_dir, 'bin/fzf') - return fzf_bin if File.exist?(fzf_bin) - - prev_level = Doing.logger.level - Doing.logger.adjust_verbosity({ log_level: :info }) - Doing.logger.log_now(:warn, 'fzf:', 'Compiling and installing fzf -- this will only happen once') - Doing.logger.log_now(:warn, 'fzf:', 'fzf is copyright Junegunn Choi, MIT License <https://github.com/junegunn/fzf/blob/master/LICENSE>') - - silence_std - `'#{fzf_dir}/install' --bin --no-key-bindings --no-completion --no-update-rc --no-bash --no-zsh --no-fish &> /dev/null` - unless File.exist?(fzf_bin) - restore_std - Doing.logger.log_now(:warn, 'Error installing, trying again as root') - silence_std - `sudo '#{fzf_dir}/install' --bin --no-key-bindings --no-completion --no-update-rc --no-bash --no-zsh --no-fish &> /dev/null` - end - restore_std - unless File.exist?(fzf_bin) - Doing.logger.error('fzf:', 'unable to install fzf. You can install manually and Doing will use the system version.') - Doing.logger.error('fzf:', 'see https://github.com/junegunn/fzf#installation') - raise RuntimeError.new('Error installing fzf, please report at https://github.com/ttscoff/doing/issues') - end - - Doing.logger.info('fzf:', "installed to #{fzf}") - Doing.logger.adjust_verbosity({ log_level: prev_level }) - fzf_bin -end- |
-
- - - -18 -19 -20 -21 -22- |
-
- # File 'lib/doing/prompt/fzf.rb', line 18 - -def uninstall_fzf - fzf_bin = File.join(File.dirname(__FILE__), '../../helpers/fzf/bin/fzf') - FileUtils.rm_f(fzf_bin) if File.exist?(fzf_bin) - Doing.logger.warn('fzf:', "removed #{fzf_bin}") -end- |
-
- - - -29 -30 -31 -32 -33 -34 -35 -36- |
-
- # File 'lib/doing/prompt/fzf.rb', line 29 - -def which_fzf - fzf_dir = File.join(File.dirname(__FILE__), '../../helpers/fzf') - fzf_bin = File.join(fzf_dir, 'bin/fzf') - return fzf_bin if File.exist?(fzf_bin) - - Doing.logger.debug('fzf:', 'Using user-installed fzf') - TTY::Which.which('fzf') -end- |
-
- - - -19 -20 -21 -22 -23 -24 -25- |
-
- # File 'lib/doing/prompt/input.rb', line 19 - -def enter_text(prompt, default_response: '') - $stdin.reopen('/dev/tty') - return default_response if @default_answer - - print "#{yellow(prompt).sub(/:?$/, ':')} #{reset}" - $stdin.gets.strip -end- |
-
- - - -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57- |
-
- # File 'lib/doing/prompt/input.rb', line 41 - -def read_line(prompt: 'Enter text', completions: [], default_response: '') - $stdin.reopen('/dev/tty') - return default_response if @default_answer - - unless completions.empty? - completions.sort! - comp = proc { |s| completions.grep(/^#{Regexp.escape(s)}/) } - Readline.completion_append_character = ' ' - Readline.completion_proc = comp - end - - begin - Readline.readline("#{yellow(prompt).sub(/:?$/, ':')} #{reset}", true).strip - rescue Interrupt - raise UserCancelled - end -end- |
-
- - - -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101- |
-
- # File 'lib/doing/prompt/input.rb', line 73 - -def read_lines(prompt: 'Enter text', completions: [], default_response: '') - $stdin.reopen('/dev/tty') - return default_response if @default_answer - - completions.sort! - comp = proc { |s| completions.grep(/^#{Regexp.escape(s)}/) } - Readline.completion_append_character = ' ' - Readline.completion_proc = comp - puts format(['%<promptcolor>s%<prompt>s %<textcolor>sEnter a blank line', - '(%<keycolor>sreturn twice%<textcolor>s)', - 'to end editing and save,', - '%<keycolor>sCTRL-C%<textcolor>s to cancel%<reset>s'].join(' '), - { promptcolor: boldgreen, prompt: prompt.sub(/:?$/, ':'), - textcolor: yellow, keycolor: boldwhite, reset: reset }) - - res = [] - - begin - while (line = Readline.readline('> ', true)) - break if line.strip.empty? - - res << line.chomp - end - rescue Interrupt - return nil - end - - res.join("\n").strip -end- |
-
- - - -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127- |
-
- # File 'lib/doing/prompt/input.rb', line 113 - -def request_lines(prompt: 'Enter text', default_response: '') - $stdin.reopen('/dev/tty') - return default_response if @default_answer - - ask_note = [] - reader = TTY::Reader.new(interrupt: -> { raise Errors::UserCancelled }, track_history: false) - puts "#{boldgreen(prompt.sub(/:?$/, ':'))} #{yellow('Hit return for a new line, ')}#{boldwhite('enter a blank line (')}#{boldyellow('return twice')}#{boldwhite(') to end editing')}" - loop do - res = reader.read_line(green('> ')) - break if res.strip.empty? - - ask_note.push(res) - end - ask_note.join("\n").strip -end- |
-
- - - -9 -10 -11 -12- |
-
- # File 'lib/doing/prompt/std.rb', line 9 - -def clear_screen(msg = nil) - puts "\e[H\e[2J" if $stdout.tty? - puts msg if msg.good? -end- |
-
- - - -27 -28 -29 -30- |
-
- # File 'lib/doing/prompt/std.rb', line 27 - -def restore_std - $stdout = STDOUT - $stderr = STDERR -end- |
-
- - - -19 -20 -21 -22- |
-
- # File 'lib/doing/prompt/std.rb', line 19 - -def silence_std(file = '/dev/null') - $stdout = File.new(file, 'w') - $stderr = File.new(file, 'w') -end- |
-
- - - -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62- |
-
- # File 'lib/doing/prompt/yn.rb', line 16 - -def yn(question, default_response: false) - return @force_answer == :yes ? true : false unless @force_answer.nil? - - $stdin.reopen('/dev/tty') - - default = if default_response.is_a?(String) - default_response =~ /y/i ? true : false - else - default_response - end - - # if global --default is set, answer default - return default if @default_answer - - # if this isn't an interactive shell, answer default - return default unless $stdout.isatty - - # clear the buffer - if ARGV&.length - ARGV.length.times do - ARGV.shift - end - end - system 'stty cbreak' - - cw = white - cbw = boldwhite - cbg = boldgreen - cd = Color.default - - = unless default.nil? - "#{cw}[#{default ? "#{cbg}Y#{cw}/#{cbw}n" : "#{cbw}y#{cw}/#{cbg}N"}#{cw}]#{cd}" - else - "#{cw}[#{cbw}y#{cw}/#{cbw}n#{cw}]#{cd}" - end - $stdout.syswrite "#{cbw}#{question.sub(/\?$/, '')} #{}#{cbw}?#{cd} " - res = $stdin.sysread 1 - puts - system 'stty cooked' - - res.chomp! - res.downcase! - - return default if res.empty? - - res =~ /y/i ? true : false -end- |
-
- - - -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18- |
-
- # File 'lib/doing/section.rb', line 8 - -def initialize(title, original: nil) - super() - - @title = title - - @original = if original.nil? - "#{title}:" - else - original =~ /:(\s+@[^ (]+(\([^)]*\))?)*?$/ ? original : "#{original}:" - end -end- |
-
- - - -6 -7 -8- |
-
- # File 'lib/doing/section.rb', line 6 - -def original - @original -end- |
-
- - - -6 -7 -8- |
-
- # File 'lib/doing/section.rb', line 6 - -def title - @title -end- |
-
- - - -20 -21 -22- |
-
- # File 'lib/doing/section.rb', line 20 - -def equal?(other) - @title == other.title -end- |
-
- - - -25 -26 -27- |
-
- # File 'lib/doing/section.rb', line 25 - -def to_s - @title -end- |
-
- - - -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60- |
-
- # File 'lib/doing/string/highlight.rb', line 31 - -def highlight_search(search, distance: nil, negate: false, case_type: nil) - out = dup - matching = Doing.setting('search.matching', 'pattern').normalize_matching - distance ||= Doing.setting('search.distance', 3).to_i - case_type ||= Doing.setting('search.case', 'smart').normalize_case - - if search.rx? || matching == :fuzzy - rx = search.to_rx(distance: distance, case_type: case_type) - out.gsub!(rx) { |m| m.bgyellow.black } - else - query = search.strip.to_phrase_query - - if query[:must].nil? && query[:must_not].nil? - query[:must] = query[:should] - query[:should] = [] - end - qs = [] - qs.concat(query[:must]) if query[:must] - qs.concat(query[:should]) if query[:should] - qs.each do |s| - rx = Regexp.new(s.wildcard_to_rx, ignore_case(s, case_type)) - out.gsub!(rx) do - m = Regexp.last_match - last = m.pre_match.last_color_code - "#{m[0].bgyellow.black}#{last}" - end - end - end - out -end- |
-
- - - -27 -28 -29- |
-
- # File 'lib/doing/string/highlight.rb', line 27 - -def highlight_search!(search, distance: nil, negate: false, case_type: nil) - replace highlight_search(search, distance: distance, negate: negate, case_type: case_type) -end- |
-
- - - -18 -19 -20 -21 -22 -23 -24 -25- |
-
- # File 'lib/doing/string/highlight.rb', line 18 - -def (color = 'yellow', last_color: nil) - unless last_color - color = color.split(' ') unless color.is_a?(Array) - tag_color = color.each_with_object([]) { |c, arr| arr << Doing::Color.send(c) }.join('') - last_color = last_color_code - end - gsub(/(\s|m)(@[^ ("']+)/, "\\1#{tag_color}\\2#{last_color}") -end- |
-
- - - -7 -8 -9- |
-
- # File 'lib/doing/string/highlight.rb', line 7 - -def (color = 'yellow', last_color: nil) - replace (color) -end- |
-
- - - -73 -74 -75- |
-
- # File 'lib/doing/string/highlight.rb', line 73 - -def last_color - scan(/\e\[[\d;]+m/).join('') -end- |
-
- - - -82 -83 -84- |
-
- # File 'lib/doing/string/highlight.rb', line 82 - -def uncolor - gsub(/\e\[[\d;]+m/, '') -end- |
-
- - - -89 -90 -91- |
-
- # File 'lib/doing/string/highlight.rb', line 89 - -def uncolor! - replace uncolor -end- |
-
- - - -54 -55 -56 -57 -58 -59 -60 -61 -62 -63- |
-
- # File 'lib/doing/normalize.rb', line 54 - -def normalize_age(default = :newest) - case self - when /^o/i - :oldest - when /^n/i - :newest - else - default.is_a?(Symbol) ? default : default.normalize_age - end -end- |
-
- - - -66 -67 -68- |
-
- # File 'lib/doing/normalize.rb', line 66 - -def normalize_age!(default = :newest) - replace normalize_age(default) -end- |
-
- - - -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131- |
-
- # File 'lib/doing/normalize.rb', line 118 - -def normalize_bool(default = :and) - case self - when /(and|all)/i - :and - when /(any|or)/i - :or - when /(not|none)/i - :not - when /^p/i - :pattern - else - default.is_a?(Symbol) ? default : default.normalize_bool - end -end- |
-
- - - -134 -135 -136- |
-
- # File 'lib/doing/normalize.rb', line 134 - -def normalize_bool!(default = :and) - replace normalize_bool(default) -end- |
-
- - - -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106- |
-
- # File 'lib/doing/normalize.rb', line 95 - -def normalize_case(default = :smart) - case self - when /^(c|sens)/i - :sensitive - when /^i/i - :ignore - when /^s/i - :smart - else - default.is_a?(Symbol) ? default : default.normalize_case - end -end- |
-
- - - -109 -110 -111- |
-
- # File 'lib/doing/normalize.rb', line 109 - -def normalize_case!(default = :smart) - replace normalize_case(default) -end- |
-
- - - -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190- |
-
- # File 'lib/doing/normalize.rb', line 179 - -def normalize_change_type - case self - when /^c/i - :changed - when /^i/i - :improved - when /^f/i - :fixed - when /^n/i - :new - end -end- |
-
- - - -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47- |
-
- # File 'lib/doing/normalize.rb', line 36 - -def normalize_list_style(default = :space) - case self - when /^c(om|sv)/i - :comma - when /^c/i - :column - when /^t/i - :tab - else - default.is_a?(Symbol) ? default : default.normalize_list_style - end -end- |
-
- - - -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157- |
-
- # File 'lib/doing/normalize.rb', line 146 - -def normalize_matching(default = :pattern) - case self - when /^f/i - :fuzzy - when /^p/i - :pattern - when /^e/i - :exact - else - default.is_a?(Symbol) ? default : default.normalize_matching - end -end- |
-
- - - -160 -161 -162- |
-
- # File 'lib/doing/normalize.rb', line 160 - -def normalize_matching!(default = :pattern) - replace normalize_bool(default) -end- |
-
- - - -79 -80 -81 -82 -83 -84 -85 -86 -87 -88- |
-
- # File 'lib/doing/normalize.rb', line 79 - -def normalize_order(default = :asc) - case self - when /^a/i - :asc - when /^d/i - :desc - else - default.is_a?(Symbol) ? default : default.normalize_order - end -end- |
-
- - - -75 -76 -77- |
-
- # File 'lib/doing/normalize.rb', line 75 - -def normalize_order!(default = :asc) - replace normalize_order(default) -end- |
-
- - - -13 -14 -15 -16 -17 -18 -19 -20 -21 -22- |
-
- # File 'lib/doing/normalize.rb', line 13 - -def normalize_tag_sort(default = :name) - case self - when /^n/i - :name - when /^t/i - :time - else - default.is_a?(Symbol) ? default : default.normalize_tag_sort - end -end- |
-
- - - -25 -26 -27- |
-
- # File 'lib/doing/normalize.rb', line 25 - -def normalize_tag_sort!(default = :name) - replace normalize_tag_sort(default) -end- |
-
- - - -170 -171 -172- |
-
- # File 'lib/doing/normalize.rb', line 170 - -def normalize_trigger - gsub(/\((?!\?:)/, '(?:').downcase -end- |
-
- - - -175 -176 -177- |
-
- # File 'lib/doing/normalize.rb', line 175 - -def normalize_trigger! - replace normalize_trigger -end- |
-
- - - -24 -25 -26 -27- |
-
- # File 'lib/doing/string/query.rb', line 24 - -def ignore? - line = self - line =~ /^#/ || line =~ /^\s*$/ -end- |
-
- - - -15 -16 -17- |
-
- # File 'lib/doing/string/query.rb', line 15 - -def ignore_case(search, case_type) - (case_type == :smart && search !~ /[A-Z]/) || case_type == :ignore -end- |
-
- - - -34 -35 -36- |
-
- # File 'lib/doing/string/query.rb', line 34 - -def rx? - self =~ %r{(^/.*?/$|^')} -end- |
-
- - - -130 -131 -132 -133 -134 -135 -136 -137- |
-
- # File 'lib/doing/string/query.rb', line 130 - -def to_bool - case self - when /^[yt1]/i - true - else - false - end -end- |
-
- - - -93 -94 -95 -96 -97 -98- |
-
- # File 'lib/doing/string/query.rb', line 93 - -def to_phrase_query - parser = PhraseParser::QueryParser.new - transformer = PhraseParser::QueryTransformer.new - parse_tree = parser.parse(self) - transformer.apply(parse_tree).to_elasticsearch -end- |
-
- - - -105 -106 -107 -108 -109 -110- |
-
- # File 'lib/doing/string/query.rb', line 105 - -def to_query - parser = BooleanTermParser::QueryParser.new - transformer = BooleanTermParser::QueryTransformer.new - parse_tree = parser.parse(self) - transformer.apply(parse_tree).to_elasticsearch -end- |
-
- - - -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86- |
-
- # File 'lib/doing/string/query.rb', line 63 - -def to_rx(distance: nil, case_type: nil) - distance ||= Doing.config.fetch('search', 'distance', 3).to_i - case_type ||= Doing.config.fetch('search', 'case', 'smart')&.normalize_case - case_sensitive = case case_type - when :smart - self =~ /[A-Z]/ ? true : false - when :sensitive - true - else - false - end - - pattern = case dup.strip - when %r{^/.*?/$} - sub(%r{/(.*?)/}, '\1') - when /^'/ - sub(/^'(.*?)'?$/, '\1') - else - split(/ +/).map do |w| - w.split('').join(".{0,#{distance}}").gsub(/\+/, '\+').wildcard_to_rx - end.join('.*?') - end - Regexp.new(pattern, !case_sensitive) -end- |
-
- - - -117 -118 -119 -120 -121 -122 -123- |
-
- # File 'lib/doing/string/query.rb', line 117 - -def truthy? - if self =~ /^(0|f(alse)?|n(o)?)$/i - false - else - true - end -end- |
-
- - - -44 -45 -46- |
-
- # File 'lib/doing/string/query.rb', line 44 - -def wildcard_to_rx - gsub(/\?/, '\S').gsub(/\*/, '\S*?').gsub(/\]\]/, '--') -end- |
-
- - - -11 -12 -13- |
-
- # File 'lib/doing/string/tags.rb', line 11 - -def add_at - strip.sub(/^([+-]*)@?/, '\1@') -end- |
-
- - - -59 -60 -61 -62 -63 -64- |
-
- # File 'lib/doing/string/tags.rb', line 59 - -def (, remove: false) - title = dup - = . - .each { |tag| title.tag!(tag, remove: remove) } - title -end- |
-
- - - -67 -68 -69- |
-
- # File 'lib/doing/string/tags.rb', line 67 - -def (, remove: false) - replace (, remove: remove) -end- |
-
- - - -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174- |
-
- # File 'lib/doing/string/tags.rb', line 159 - -def - title = dup - = title.scan(/(?<=\A| )(@(\S+?)(\([^)]+\))?)(?= |\Z)/).uniq - .each do |tag| - found = false - title.gsub!(/( |^)#{Regexp.escape(tag[1])}(\([^)]+\))?(?= |$)/) do |m| - if found - '' - else - found = true - m - end - end - end - title -end- |
-
- - - -177 -178 -179- |
-
- # File 'lib/doing/string/tags.rb', line 177 - -def - replace -end- |
-
- - - -20 -21 -22- |
-
- # File 'lib/doing/string/tags.rb', line 20 - -def remove_at - strip.sub(/^([+-]*)@?/, '\1') -end- |
-
- - - -31 -32 -33- |
-
- # File 'lib/doing/string/tags.rb', line 31 - -def - gsub(/ *, */, ' ').scan(/(@?(?:\S+(?:\(.+\)))|@?(?:\S+))/).map(&:first).map(&:remove_at).sort.uniq -end- |
-
- - - -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152- |
-
- # File 'lib/doing/string/tags.rb', line 93 - -def tag(tag, value: nil, remove: false, rename_to: nil, regex: false, single: false, force: false) - log_level = single ? :info : :debug - title = dup - title.chomp! - tag = tag.sub(/^@?/, '') - case_sensitive = tag !~ /[A-Z]/ - - rx_tag = if regex - tag.gsub(/\./, '\S') - else - tag.gsub(/\?/, '.').gsub(/\*/, '\S*?') - end - - if remove || rename_to - rx = Regexp.new("(?<=^| )@#{rx_tag}(?<parens>\\((?<value>[^)]*)\\))?(?= |$)", case_sensitive) - m = title.match(rx) - - if m.nil? && rename_to && force - title.tag!(rename_to, value: value, single: single) - elsif m - title.gsub!(rx) do - rename_to ? "@#{rename_to}#{value.nil? ? m['parens'] : "(#{value})"}" : '' - end - - title. - title.chomp! - - if rename_to - f = "@#{tag}".cyan - t = "@#{rename_to}".cyan - Doing.logger.write(log_level, 'Tag:', %(renamed #{f} to #{t} in "#{title}")) - else - f = "@#{tag}".cyan - Doing.logger.write(log_level, 'Tag:', %(removed #{f} from "#{title}")) - end - else - Doing.logger.debug('Skipped:', "not tagged #{"@#{tag}".cyan}") - end - elsif title =~ /@#{tag}(?=[ (]|$)/ && !value.good? - Doing.logger.debug('Skipped:', "already tagged #{"@#{tag}".cyan}") - return title - else - add = tag - add += "(#{value})" unless value.nil? - - title.chomp! - - if value && title =~ /@#{tag}(?=[ (]|$)/ - title.sub!(/@#{tag}(\(.*?\))?/, "@#{add}") - else - title += " @#{add}" - end - - title. - title.chomp! - Doing.logger.write(log_level, 'Tag:', %(added #{('@' + tag).cyan} to "#{title}")) - end - - title.gsub(/ +/, ' ') -end- |
-
- - - -76 -77 -78- |
-
- # File 'lib/doing/string/tags.rb', line 76 - -def tag!(tag, **) - replace tag(tag, **) -end- |
-
- - - -42 -43 -44 -45 -46 -47 -48 -49- |
-
- # File 'lib/doing/string/tags.rb', line 42 - -def - arr = .map(&:add_at) - if block_given? - yield arr - else - arr - end -end- |
-
- - - -102 -103 -104 -105 -106- |
-
- # File 'lib/doing/string/transform.rb', line 102 - -def cap_first - sub(/^\w/) do |m| - m.upcase - end -end- |
-
- - - -9 -10 -11- |
-
- # File 'lib/doing/string/transform.rb', line 9 - -def compress - gsub(/ +/, ' ').strip -end- |
-
- - - -13 -14 -15- |
-
- # File 'lib/doing/string/transform.rb', line 13 - -def compress! - replace compress -end- |
-
- - - -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166- |
-
- # File 'lib/doing/string/transform.rb', line 130 - -def set_type(kind = nil) - if kind - case kind.to_s - when /^a/i - gsub(/^\[ *| *\]$/, '').split(/ *, */) - when /^i/i - to_i - when /^(fa|tr)/i - to_bool - when /^f/i - to_f - when /^sy/i - sub(/^:/, '').to_sym - when /^b/i - self =~ /^(true|yes)$/ ? true : false - else - to_s - end - else - case self - when /(^\[.*?\]$| *, *)/ - gsub(/^\[ *| *\]$/, '').split(/ *, */) - when /^[0-9]+$/ - to_i - when /^[0-9]+\.[0-9]+$/ - to_f - when /^:\w+/ - sub(/^:/, '').to_sym - when /^(true|yes)$/i - true - when /^(false|no)$/i - false - else - to_s - end - end -end- |
-
- - - -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38- |
-
- # File 'lib/doing/string/transform.rb', line 17 - -def simple_wrap(width) - str = gsub(/@\S+\(.*?\)/) { |tag| tag.gsub(/\s/, '%%%%') } - words = str.split(/ /).map { |word| word.gsub(/%%%%/, ' ') } - out = [] - line = [] - - words.each do |word| - if word.uncolor.length >= width - chars = word.uncolor.split('') - out << chars.slice!(0, width - 1).join('') while chars.count >= width - line << chars.join('') - next - elsif line.join(' ').uncolor.length + word.uncolor.length + 1 > width - out.push(line.join(' ')) - line.clear - end - - line << word.uncolor - end - out.push(line.join(' ')) - out.join("\n") -end- |
-
- - - -168 -169 -170 -171 -172- |
-
- # File 'lib/doing/string/transform.rb', line 168 - -def titlecase - tr('_', ' '). - gsub(/\s+/, ' '). - gsub(/\b\w/){ $`[-1,1] == "'" ? $& : $&.upcase } -end- |
-
- - - -114 -115 -116- |
-
- # File 'lib/doing/string/transform.rb', line 114 - -def to_p(number) - number == 1 ? self : "#{self}s" -end- |
-
- - - -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95- |
-
- # File 'lib/doing/string/transform.rb', line 47 - -def wrap(len, pad: 0, indent: ' ', offset: 0, prefix: '', color: '', after: '', reset: '', pad_first: false) - last_color = color.empty? ? '' : after.last_color - note_rx = /(?mi)(?<!\\)%(?<width>-?\d+)?(?:\^(?<mchar>.))?(?:(?<ichar>[ _t]|[^a-z0-9])(?<icount>\d+))?(?<prefix>.[ _t]?)?note/ - note = '' - after = after.dup if after.frozen? - after.sub!(note_rx) do - note = Regexp.last_match(0) - '' - end - - left_pad = ' ' * offset - left_pad += indent - - # return "#{left_pad}#{prefix}#{color}#{self}#{last_color} #{note}" unless len.positive? - - # Don't break inside of tag values - str = gsub(/@\S+\(.*?\)/) { |tag| tag.gsub(/\s/, '%%%%') }.gsub(/\n/, ' ') - - words = str.split(/ /).map { |word| word.gsub(/%%%%/, ' ') } - out = [] - line = [] - - words.each do |word| - if word.uncolor.length >= len - chars = word.uncolor.split('') - out << chars.slice!(0, len - 1).join('') while chars.count >= len - line << chars.join('') - next - elsif line.join(' ').uncolor.length + word.uncolor.length + 1 > len - out.push(line.join(' ')) - line.clear - end - - line << word.uncolor - end - out.push(line.join(' ')) - - last_color = '' - out[0] = format("%-#{pad}s%s%s", out[0], last_color, after) - - out.map.with_index { |l, idx| - if !pad_first && idx == 0 - "#{color}#{prefix}#{l}#{last_color}" - else - "#{left_pad}#{color}#{prefix}#{l}#{last_color}" - end - }.join("\n") + " #{note}".chomp - # res.join("\n").strip + last_color + " #{note}".chomp -end- |
-
- - - -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26- |
-
- # File 'lib/doing/string/truncate.rb', line 13 - -def trunc(len, ellipsis: '...') - return self if length <= len - - total = 0 - res = [] - - split(/ /).each do |word| - break if total + 1 + word.length > len - - total += 1 + word.length - res.push(word) - end - res.join(' ') + ellipsis -end- |
-
- - - -28 -29 -30- |
-
- # File 'lib/doing/string/truncate.rb', line 28 - -def trunc!(len, ellipsis: '...') - replace trunc(len, ellipsis: ellipsis) -end- |
-
- - - -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50- |
-
- # File 'lib/doing/string/truncate.rb', line 37 - -def truncend(len, ellipsis: '...') - return self if length <= len - - total = 0 - res = [] - - split(/ /).reverse.each do |word| - break if total + 1 + word.length > len - - total += 1 + word.length - res.unshift(word) - end - ellipsis + res.join(' ') -end- |
-
- - - -52 -53 -54- |
-
- # File 'lib/doing/string/truncate.rb', line 52 - -def truncend!(len, ellipsis: '...') - replace truncend(len, ellipsis: ellipsis) -end- |
-
- - - -62 -63 -64 -65 -66 -67 -68 -69- |
-
- # File 'lib/doing/string/truncate.rb', line 62 - -def truncmiddle(len, ellipsis: '...') - return self if length <= len - len -= (ellipsis.length / 2).to_i - half = (len / 2).to_i - start = trunc(half, ellipsis: ellipsis) - finish = truncend(half, ellipsis: '') - start + finish -end- |
-
- - - -71 -72 -73- |
-
- # File 'lib/doing/string/truncate.rb', line 71 - -def truncmiddle!(len, ellipsis: '...') - replace truncmiddle(len, ellipsis: ellipsis) -end- |
-
- - - -71 -72 -73 -74 -75 -76 -77 -78 -79 -80- |
-
- # File 'lib/doing/string/url.rb', line 71 - -def clean_unlinked_urls - gsub(/<(\w+:.*?)>/) do |match| - m = Regexp.last_match - if m[1] =~ /<a href/ - match - else - %(<a href="#{m[1]}" title="Link to #{m[1]}">[link]</a>) - end - end -end- |
-
- - - -16 -17 -18 -19 -20 -21 -22 -23 -24 -25- |
-
- # File 'lib/doing/string/url.rb', line 16 - -def link_urls(**opt) - fmt = opt.fetch(:format, :html) - return self unless fmt - - str = dup - - str = str.remove_self_links if fmt == :markdown - - str.replace_qualified_urls(format: fmt).clean_unlinked_urls -end- |
-
- - - -28 -29 -30 -31- |
-
- # File 'lib/doing/string/url.rb', line 28 - -def link_urls!(**opt) - fmt = opt.fetch(:format, :html) - replace link_urls(format: fmt) -end- |
-
- - - -34 -35 -36 -37 -38 -39 -40 -41 -42 -43- |
-
- # File 'lib/doing/string/url.rb', line 34 - -def remove_self_links - gsub(/<(.*?)>/) do |match| - m = Regexp.last_match - if m[1] =~ /^https?:/ - m[1] - else - match - end - end -end- |
-
- - - -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68- |
-
- # File 'lib/doing/string/url.rb', line 46 - -def replace_qualified_urls(**) - fmt = .fetch(:format, :html) - gsub(%r{(?mi)(?x: - (?<!["'\[(\\]) - (?<protocol>(?:http|https)://) - (?<domain>[\w\-]+(?:\.[\w\-]+)+) - (?<path>[\w\-.,@?^=%&;:/~+#]*[\w\-@^=%&;/~+#])? - )}) do |_match| - m = Regexp.last_match - url = "#{m['domain']}#{m['path']}" - proto = m['protocol'].nil? ? 'http://' : m['protocol'] - case fmt - when :terminal - TTY::Link.link_to("#{proto}#{url}", "#{proto}#{url}") - when :html - %(<a href="#{proto}#{url}" title="Link to #{m['domain']}">[#{url}]</a>) - when :markdown - "[#{url}](#{proto}#{url})" - else - m[0] - end - end -end- |
-
- - - -205 -206 -207- |
-
- # File 'lib/doing/normalize.rb', line 205 - -def normalize_age(default = :newest) - to_s.normalize_age(default) -end- |
-
- - - -201 -202 -203- |
-
- # File 'lib/doing/normalize.rb', line 201 - -def normalize_bool(default = :and) - to_s.normalize_bool(default) -end- |
-
- - - -213 -214 -215- |
-
- # File 'lib/doing/normalize.rb', line 213 - -def normalize_case(default = :smart) - to_s.normalize_case(default) -end- |
-
- - - -217 -218 -219- |
-
- # File 'lib/doing/normalize.rb', line 217 - -def normalize_matching(default = :pattern) - to_s.normalize_matching(default) -end- |
-
- - - -209 -210 -211- |
-
- # File 'lib/doing/normalize.rb', line 209 - -def normalize_order(default = :asc) - to_s.normalize_order(default) -end- |
-
- - - -197 -198 -199- |
-
- # File 'lib/doing/normalize.rb', line 197 - -def normalize_tag_sort(default = :name) - to_s.normalize_tag_sort -end- |
-
- - - -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30- |
-
- # File 'lib/doing/plugins/export/taskpaper_export.rb', line 17 - -def self.render(wwid, items, variables: {}) - return if items.nil? - - = variables[:options] - - [:highlight] = false - [:wrap_width] = 0 - [:tags_color] = false - [:output] = 'template' - [:template] = '- %title @date(%date)%note' - - Doing.logger.debug('TaskPaper Export:', "#{items.count} items output to TaskPaper format") - @out = wwid.list_section() -end- |
-
- - - -11 -12 -13 -14 -15- |
-
- # File 'lib/doing/plugins/export/taskpaper_export.rb', line 11 - -def self.settings - { - trigger: 'task(?:paper)?|tp' - } -end- |
-
- - - -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144- |
-
- # File 'lib/doing/plugins/export/template_export.rb', line 19 - -def self.render(wwid, items, variables: {}) - Doing.logger.benchmark(:template_render, :start) - return if items.nil? - - opt = variables[:options] - - out = '' - items.each do |item| - if opt[:highlight] && item.title =~ /@#{Doing.setting('marker_tag')}\b/i - flag = Doing::Color.send(Doing.setting('marker_color')) - reset = Doing::Color.reset + Doing::Color.default - else - flag = '' - reset = '' - end - - placeholders = {} - - if !item.note.empty? && Doing.setting('include_notes') - note = item.note.map(&:strip).delete_if(&:empty?) - note.map! { |line| "#{line.sub(/^\t*/, '')} " } - - if opt[:wrap_width]&.positive? - width = opt[:wrap_width] - note.map! do |line| - line.simple_wrap(width) - # line.chomp.gsub(/(.{1,#{width}})(\s+|\Z)/, "\\1\n") - end - note.delete_if(&:empty?) - end - else - note = [] - end - - placeholders['id'] = item.id - - placeholders['tags'] = item. - - placeholders['date'] = item.date.strftime(opt[:format]) - - interval = wwid.get_interval(item, record: true, formatted: false) if opt[:times] - if interval - interval = case opt[:interval_format].to_sym - when :human - interval.time_string(format: :hm) - when :text - interval.time_string(format: :clock) - else - interval.time_string(format: opt[:interval_format].to_sym) - end - end - - interval ||= '' - placeholders['interval'] = interval - - duration = item.duration if opt[:duration] - if duration - duration = case opt[:interval_format].to_sym - when :human - duration.time_string(format: :hm) - when :text - duration.time_string(format: :clock) - else - duration.time_string(format: opt[:interval_format].to_sym) - end - end - duration ||= '' - placeholders['duration'] = duration - - placeholders['shortdate'] = format('%13s', item.date.relative_date) - placeholders['section'] = item.section || '' - placeholders['title'] = item.title - placeholders['note'] = note - placeholders['idnote'] = note.empty? ? '' : "\n#{note.map { |l| "\t\t#{l.strip} " }.join("\n")}" - placeholders['odnote'] = note.empty? ? '' : "\n#{note.map { |l| "#{l.strip} " }.join("\n")}" - - chompnote = [] - unless note.empty? - chompnote = note.map do |l| - l.gsub(/\n+/, ' ').gsub(/(^\s*|\s*$)/, '').gsub(/\s+/, ' ') - end - end - placeholders['chompnote'] = chompnote.join(' ') - - template = opt[:template].dup - note_rx = /(?i-m)(?x:^([\s\S]*?) - (%(?:[io]d|(?:\^[\s\S])? - (?:(?:[ _t]|[^a-z0-9])?\d+)? - (?:[\s\S][ _t]?)?)?note) - ([\s\S]*?)$)/ - template.sub!(note_rx, '\1\3\2') - output = Doing::TemplateString.new(template, - color: flag, - placeholders: placeholders, - reset: reset, - tags_color: opt[:tags_color], - wrap_width: opt[:wrap_width]).colored - - output.gsub!(/(?<!\\)%(\S)?hr(_under)?/) do - o = '' - TTY::Screen.columns.to_i.times do - char = Regexp.last_match(2).nil? ? '-' : '_' - char = Regexp.last_match(1).nil? ? char : Regexp.last_match(1) - o += char - end - o - end - output.gsub!(/(?<!\\)%n/, "\n") - output.gsub!(/(?<!\\)%t/, "\t") - - output.gsub!(/\\%/, '%') - - output.highlight_search!(opt[:search]) if opt[:output] =~ /^temp/ && opt[:search] && !opt[:not] && opt[:hilite] - - out += "#{output}\n" - end - - # Doing.logger.debug('Template Export:', "#{items.count} items output to template #{opt[:output]}") - if opt[:totals] - out += wwid.tag_times(format: Doing.setting('timer_format').to_sym, - sort_by: opt[:sort_tags], - sort_order: opt[:tag_order]) - end - Doing.logger.benchmark(:template_render, :finish) - out -end- |
-
- - - -13 -14 -15 -16 -17- |
-
- # File 'lib/doing/plugins/export/template_export.rb', line 13 - -def self.settings - { - trigger: 'template' - } -end- |
-
- - - -11 -12 -13 -14 -15 -16 -17 -18- |
-
- # File 'lib/doing/template_string.rb', line 11 - -def initialize(string, placeholders: {}, force_color: false, wrap_width: 0, color: '', tags_color: '', reset: '') - Color.coloring = true if force_color - @colors = nil - @original = string - super(Color.reset + string) - - placeholders.each { |k, v| fill(k, v, wrap_width: wrap_width, color: color, tags_color: ) } -end- |
-
- - - -8 -9 -10- |
-
- # File 'lib/doing/template_string.rb', line 8 - -def original - @original -end- |
-
- - - -88 -89 -90 -91 -92 -93 -94 -95- |
-
- # File 'lib/doing/template_string.rb', line 88 - -def apply_colors(color_array) - str = dup - color_array.reverse.each do |color| - c = color[:color].empty? ? Color.send(color[:name]) : color[:color] - str.insert(color[:index], c) - end - str -end- |
-
- - - -41 -42 -43 -44- |
-
- # File 'lib/doing/template_string.rb', line 41 - -def colored - reparse - parsed_colors[:string].apply_colors(parsed_colors[:colors]) -end- |
-
- - - -25 -26 -27 -28 -29 -30- |
-
- # File 'lib/doing/template_string.rb', line 25 - -def colors? - scan(/%([a-z]+)/).each do - return true if Regexp.last_match(1).validate_color - end - false -end- |
-
- - - -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182- |
-
- # File 'lib/doing/template_string.rb', line 97 - -def fill(placeholder, value, wrap_width: 0, color: '', tags_color: '', reset: '') - reparse - rx = /(?mi)(?<!\\)%(?<width>-?\d+)?(?:\^(?<mchar>.))?(?:(?<ichar>[ _t]|[^a-z0-9])(?<icount>\d+))?(?<prefix>.[ _t]?)?#{placeholder.sub(/^%/, '')}(?<after>.*?)$/ - ph = raw.match(rx) - - return unless ph - placeholder_offset = ph.begin(0) - last_colors = parsed_colors[:colors].select { |v| v[:index] <= placeholder_offset + 4 } - - last_color = last_colors.map { |v| v[:color] }.pop(3).join('') - - sub!(rx) do - m = Regexp.last_match - - after = m['after'] - - if !value.good? - after - else - pad = m['width'].to_i - mark = m['mchar'] || '' - if placeholder == 'shortdate' && m['width'].nil? - fmt_string = Doing.setting('shortdate_format.older', '%m/%d/%y %_I:%M%P', exact: true) - pad = Date.today.strftime(fmt_string).length - end - indent = nil - if m['ichar'] - char = m['ichar'] =~ /t/ ? "\t" : ' ' - indent = char * m['icount'].to_i - end - indent ||= placeholder =~ /^title/ ? '' : "\t" - prefix = m['prefix'] - - if placeholder =~ /^tags/ - prefix ||= '' - value = value.map { |t| "#{prefix}#{t.sub(/^#{prefix}?/, '')}" }.join(' ') - prefix = '' - end - - if placeholder =~ /^title/ - color = last_color + color - - if wrap_width.positive? || pad.positive? - width = pad.positive? ? pad : wrap_width - - out = value.gsub(/%/, '\%').strip.wrap(width, - pad: pad, - indent: indent, - offset: placeholder_offset, - prefix: prefix, - color: color, - after: after, - reset: reset, - pad_first: false) - out.(, last_color: color) if && !.empty? - out - else - out = format("%s%s%#{pad}s%s", prefix, color, value.gsub(/%/, '\%').sub(/\s*$/, ''), after) - out.(, last_color: color) if && !.empty? - out - end - elsif placeholder =~ /^note/ - if wrap_width.positive? || pad.positive? - width = pad.positive? ? pad : wrap_width - outstring = value.map do |l| - if l.empty? - ' ' - else - line = l.gsub(/%/, '\%').strip.wrap(width, pad: pad, indent: indent, offset: 0, prefix: prefix, color: last_color, after: after, reset: reset, pad_first: true) - line.(, last_color: last_color) unless ! || !.good? - "#{line} " - end - end.join("\n") - "\n#{last_color}#{mark}#{outstring} " - else - out = format("\n%s%s%s%#{pad}s%s", indent, prefix, last_color, value.join("\n#{indent}#{prefix}").gsub(/%/, '\%').sub(/\s*$/, ''), after) - out.(, last_color: last_color) if && !.empty? - out - end - else - format("%s%#{pad}s%s", prefix, value.gsub(/%/, '\%').sub(/\s*$/, ''), after) - end - end - end - @parsed_colors = parse_colors -end- |
-
- - - -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79- |
-
- # File 'lib/doing/template_string.rb', line 64 - -def parse_colors - working = dup - color_array = [] - - scan(/(?<!\\)(%((?:[fb]g?)?#[a-fA-F0-9]{6}|[a-z]+))/).each do |color| - valid_color = color[1].validate_color - next unless valid_color - - idx = working.match(/(?<!\\)%#{valid_color}/).begin(0) - color = Color.attributes.include?(valid_color.to_sym) ? Color.send(valid_color) : Color.rgb(valid_color) - color_array.push({ name: valid_color, color: color, index: idx }) - working.sub!(/(?<!\\)%#{valid_color}/, '') - end - - { string: working, colors: color_array } -end- |
-
- - - -55 -56 -57- |
-
- # File 'lib/doing/template_string.rb', line 55 - -def parsed_colors - @parsed_colors ||= parse_colors -end- |
-
- - - -51 -52 -53- |
-
- # File 'lib/doing/template_string.rb', line 51 - -def raw - parsed_colors[:string].uncolor -end- |
-
- - - -32 -33 -34- |
-
- # File 'lib/doing/template_string.rb', line 32 - -def reparse - @parsed_colors = nil -end- |
-
- - - -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99- |
-
- # File 'lib/doing/plugins/import/timing_import.rb', line 25 - -def self.import(wwid, path, options: {}) - exit_now! 'Path to JSON report required' if path.nil? - section = [:section] || Doing.setting('current_section') - [:no_overlap] ||= false - [:autotag] ||= Doing.auto_tag - wwid.content.add_section(section) unless wwid.content.section?(section) - - = [:tag] ? [:tag].split(/[ ,]+/).map { |t| t.sub(/^@?/, '') } : [] - prefix = [:prefix] || '[Timing.app]' - exit_now! 'File not found' unless File.exist?(File.(path)) - - data = JSON.parse(IO.read(File.(path))) - new_items = [] - data.each do |entry| - # Only process task entries - next if entry.key?('activityType') && entry['activityType'] != 'Task' - # Only process entries with a start and end date - next unless entry.key?('startDate') && entry.key?('endDate') - - # Round down seconds and convert UTC to local time - start_time = Time.parse(entry['startDate'].sub(/:\d\dZ$/, ':00Z')).getlocal - end_time = Time.parse(entry['endDate'].sub(/:\d\dZ$/, ':00Z')).getlocal - next unless start_time && end_time - - = entry['project'].split(/ ▸ /).map { |proj| proj.gsub(/[^a-z0-9]+/i, '').downcase } - .concat() - title = "#{prefix} " - title += entry.key?('activityTitle') && entry['activityTitle'] != '(Untitled Task)' ? entry['activityTitle'] : 'Working on' - .each do |tag| - if title =~ /\b#{tag}\b/i - title.sub!(/\b#{tag}\b/i, "@#{tag}") - else - title += " @#{tag}" - end - end - title = wwid.autotag(title) if [:autotag] - title += " @done(#{end_time.strftime('%Y-%m-%d %H:%M')})" - title.gsub!(/ +/, ' ') - title.strip! - new_item = Item.new(start_time, title, section) - new_item.note.add(entry['notes']) if entry.key?('notes') - - is_match = true - - if [:search] - is_match = new_item.search([:search], case_type: [:case], negate: [:not]) - end - - if is_match && [:date_filter] - is_match = start_time > [:date_filter][0] && start_time < [:date_filter][1] - is_match = [:not] ? !is_match : is_match - end - - new_items.push(new_item) if is_match - end - total = new_items.count - skipped = data.count - total - Doing.logger.debug('Skipped:' , %(#{skipped} items, invalid type or no time interval)) if skipped.positive? - - new_items = wwid.filter_items(new_items, opt: ) - filtered = skipped - new_items.count - Doing.logger.debug('Skipped:' , %(#{filtered} items that didn't match filter criteria)) if filtered.positive? - - new_items = wwid.dedup(new_items, no_overlap: [:no_overlap]) - dups = filtered - new_items.count - Doing.logger.debug('Skipped:' , %(#{dups} items with overlapping times)) if dups.positive? - - new_items.map { |item| Hooks.trigger :pre_entry_add, self, item } - - wwid.content.concat(new_items) - - new_items.map { |item| Hooks.trigger :post_entry_added, self, item } - - Doing.logger.info('Imported:', %(#{new_items.count} items to #{section})) -end- |
-
- - - -11 -12 -13 -14 -15- |
-
- # File 'lib/doing/plugins/import/timing_import.rb', line 11 - -def self.settings - { - trigger: 'tim(?:ing)?' - } -end- |
-
- - - -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161- |
-
- # File 'lib/doing/util.rb', line 149 - -def args_for_editor(editor) - return editor if editor =~ /-\S/ - - args = case editor - when /^(subl|code|mate)$/ - ['-w'] - when /^(vim|mvim)$/ - ['-f'] - else - [] - end - "#{editor} #{args.join(' ')}" -end- |
-
- - - -58 -59 -60- |
-
- # File 'lib/doing/util.rb', line 58 - -def deep_merge_hashes(master_hash, other_hash) - deep_merge_hashes!(master_hash.clone, other_hash) -end- |
-
- - - -77 -78 -79 -80 -81 -82 -83- |
-
- # File 'lib/doing/util.rb', line 77 - -def deep_merge_hashes!(target, overwrite) - merge_values(target, overwrite) - merge_default_proc(target, overwrite) - duplicate_frozen_values(target) - - target -end- |
-
- - - -141 -142 -143- |
-
- # File 'lib/doing/util.rb', line 141 - -def default_editor - @default_editor ||= find_default_editor -end- |
-
- - - -85 -86 -87 -88 -89 -90 -91 -92- |
-
- # File 'lib/doing/util.rb', line 85 - -def duplicable?(obj) - case obj - when nil, false, true, Symbol, Numeric - false - else - true - end -end- |
-
- - - -44 -45 -46 -47 -48- |
-
- # File 'lib/doing/util.rb', line 44 - -def duplicate_frozen_values(target) - target.each do |key, val| - target[key] = val.dup if val.frozen? && duplicable?(val) - end -end- |
-
- - - -145 -146 -147- |
-
- # File 'lib/doing/util.rb', line 145 - -def editor_with_args - args_for_editor(default_editor) -end- |
-
- - - -21 -22 -23 -24 -25- |
-
- # File 'lib/doing/util.rb', line 21 - -def exec_available(cli) - return false unless cli.good? - - !TTY::Which.which(cli).nil? -end- |
-
- - - -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209- |
-
- # File 'lib/doing/util.rb', line 163 - -def find_default_editor(editor_for = 'default') - # return nil unless $stdout.isatty || ENV['DOING_EDITOR_TEST'] - - return ENV['EDITOR'] if ENV['DOING_EDITOR_TEST'] - - editor_config = Doing.setting('editors') - - if editor_config.is_a?(String) - msg = "Please update your configuration, 'editors' should be a mapping." - msg << ' Delete the key and run `doing config --update`.' - Doing.logger.warn('Deprecated:', msg) - return editor_config - end - - if editor_config[editor_for] - editor = editor_config[editor_for] - Doing.logger.debug('Editor:', "Using #{editor} from config 'editors.#{editor_for}' for #{editor_for}") - return editor if editor.good? - end - - if editor_for != 'editor' && editor_config['default'] - editor = editor_config['default'] - Doing.logger.debug('Editor:', "Using #{editor} from config: 'editors.default' for #{editor_for}") - return editor if editor.good? - end - - editor ||= ENV['DOING_EDITOR'] || ENV['GIT_EDITOR'] || ENV['EDITOR'] - - if editor.good? - Doing.logger.debug('Editor:', "Found editor in environment variables: #{editor} for #{editor_for}") - return editor - end - - Doing.logger.debug('Editor:', 'No EDITOR environment variable, testing available editors') - editors = %w[vim vi code subl mate mvim nano emacs] - editors.each do |ed| - try = TTY::Which.which(ed) - if try - Doing.logger.debug('Editor:', "Using editor #{try} for #{editor_for}") - return try - end - - Doing.logger.debug('Editor:', "#{ed} not available") - end - - nil -end- |
-
- - - -33 -34 -35 -36- |
-
- # File 'lib/doing/util.rb', line 33 - -def first_available_exec(*commands) - commands.compact.map(&:strip).reject(&:empty?).uniq - .find { |cmd| exec_available(cmd.split.first) } -end- |
-
- - - -94 -95 -96- |
-
- # File 'lib/doing/util.rb', line 94 - -def mergable?(value) - value.is_a?(Hash) -end- |
-
- - - -38 -39 -40 -41 -42- |
-
- # File 'lib/doing/util.rb', line 38 - -def merge_default_proc(target, overwrite) - return unless target.is_a?(Hash) && overwrite.is_a?(Hash) && target.default_proc.nil? - - target.default_proc = overwrite.default_proc -end- |
-
- - - -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108- |
-
- # File 'lib/doing/util.rb', line 98 - -def merge_values(target, overwrite) - target.merge!(overwrite) do |_key, old_val, new_val| - if new_val.nil? - old_val - elsif mergable?(old_val) && mergable?(new_val) - deep_merge_hashes(old_val, new_val) - else - new_val - end - end -end- |
-
- - - -137 -138 -139- |
-
- # File 'lib/doing/util.rb', line 137 - -def safe_load_file(filename) - SafeYAML.load_file(filename) || {} -end- |
-
- - - -8 -9 -10 -11 -12 -13 -14- |
-
- # File 'lib/doing/util.rb', line 8 - -def user_home - if Dir.respond_to?('home') - Dir.home - else - File.('~') - end -end- |
-
- - - -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135- |
-
- # File 'lib/doing/util.rb', line 117 - -def write_to_file(file, content, backup: true) - unless file - puts content - return - end - Doing.logger.benchmark(:write_file, :start) - file = File.(file) - - Backup.write_backup(file) if backup - - File.open(file, 'w+') do |f| - f.puts content - Doing.logger.debug('Write:', "File written: #{file}") - end - Doing.logger.benchmark(:_post_write_hook, :start) - Hooks.trigger :post_write, file - Doing.logger.benchmark(:_post_write_hook, :finish) - Doing.logger.benchmark(:write_file, :finish) -end- |
-
- - - -33 -34 -35 -36 -37 -38- |
-
- # File 'lib/doing/util_backup.rb', line 33 - -def last_backup(filename = nil, count: 1) - filename ||= Doing.setting('doing_file') - - backup = get_backups(filename).slice(count - 1) - backup.nil? ? nil : File.join(backup_dir, backup) -end- |
-
- - - -16 -17 -18 -19 -20 -21 -22 -23 -24 -25- |
-
- # File 'lib/doing/util_backup.rb', line 16 - -def prune_backups(filename, limit = 10) - backups = get_backups(filename) - return unless backups.count > limit - - backups[limit..-1].each do |file| - FileUtils.rm(File.join(backup_dir, file)) - end - - clear_redo(filename) -end- |
-
- - - -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89- |
-
- # File 'lib/doing/util_backup.rb', line 67 - -def redo_backup(filename = nil, count: 1) - filename ||= Doing.setting('doing_file') - - undones = Dir.glob("undone*#{File.basename(filename)}", base: backup_dir).sort.reverse - total = undones.count - count = total if count > total - - skipped = undones.slice!(0, count) - undone = skipped.pop - - raise HistoryLimitError, 'End of redo history' if undone.nil? - - redo_file = File.join(backup_dir, undone) - - move_backup(redo_file, filename) - - skipped.each do |f| - FileUtils.mv(File.join(backup_dir, f), File.join(backup_dir, f.sub(/^undone/, ''))) - end - - Doing.logger.warn('File update:', "restored undo step #{count}/#{total}") - Doing.logger.debug('Backup:', "#{total - skipped.count - 1} redos remaining") -end- |
-
- - - -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60- |
-
- # File 'lib/doing/util_backup.rb', line 47 - -def restore_last_backup(filename = nil, count: 1) - Doing.logger.benchmark(:restore_backup, :start) - filename ||= Doing.setting('doing_file') - - backup_file = last_backup(filename, count: count) - raise HistoryLimitError, 'End of undo history' if backup_file.nil? - - save_undone(filename) - move_backup(backup_file, filename) - - prune_backups_after(File.basename(backup_file)) - Doing.logger.warn('File update:', "restored from #{backup_file}") - Doing.logger.benchmark(:restore_backup, :finish) -end- |
-
- - - -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151- |
-
- # File 'lib/doing/util_backup.rb', line 135 - -def select_backup(filename = nil) - filename ||= Doing.setting('doing_file') - - = get_backups(filename).each_with_object([]) do |file, arr| - d, _base = date_of_backup(file) - next if d.nil? - arr.push("#{d.time_ago}\t#{File.join(backup_dir, file)}") - end - - raise MissingBackupFile, 'No backup files to load' if .empty? - - backup_file = (, filename) - Util.write_to_file(File.join(backup_dir, "undone___#{File.basename(filename)}"), IO.read(filename), backup: false) - move_backup(backup_file, filename) - prune_backups_after(File.basename(backup_file)) - Doing.logger.warn('File update:', "restored from #{backup_file}") -end- |
-
- - - -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127- |
-
- # File 'lib/doing/util_backup.rb', line 97 - -def select_redo(filename = nil) - filename ||= Doing.setting('doing_file') - - undones = Dir.glob("undone*#{File.basename(filename)}", base: backup_dir).sort - raise HistoryLimitError, 'End of redo history' if undones.empty? - - total = undones.count - = undones.each_with_object([]) do |file, arr| - d, _base = date_of_backup(file) - next if d.nil? - - arr.push("#{d.time_ago}\t#{File.join(backup_dir, file)}") - end - raise MissingBackupFile, 'No backup files to load' if .empty? - - backup_file = (, filename) - idx = undones.index(File.basename(backup_file)) - skipped = undones.slice!(idx, undones.count - idx) - undone = skipped.shift - - redo_file = File.join(backup_dir, undone) - - move_backup(redo_file, filename) - - skipped.each do |f| - FileUtils.mv(File.join(backup_dir, f), File.join(backup_dir, f.sub(/^undone/, ''))) - end - - Doing.logger.warn('File update:', "restored undo step #{idx}/#{total}") - Doing.logger.debug('Backup:', "#{total - skipped.count - 1} redos remaining") -end- |
-
- - - -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179- |
-
- # File 'lib/doing/util_backup.rb', line 159 - -def write_backup(filename = nil) - Doing.logger.benchmark(:_write_backup, :start) - filename ||= Doing.setting('doing_file') - - unless File.exist?(filename) - Doing.logger.debug('Backup:', "original file doesn't exist (#{filename})") - return - end - - backup_file = File.join(backup_dir, "#{}___#{File.basename(filename)}") - # compressed = Zlib::Deflate.deflate(content) - # Zlib::GzipWriter.open(backup_file + '.gz') do |gz| - # gz.write(IO.read(filename)) - # end - - FileUtils.cp(filename, backup_file) - - prune_backups(filename, Doing.setting('history_size').to_i) - clear_undone(filename) - Doing.logger.benchmark(:_write_backup, :finish) -end- |
-
- - - -8 -9 -10- |
-
- # File 'lib/doing/changelog/version.rb', line 8 - -def initialize(string) - @maj, @min, @patch = version_to_a(string) -end- |
-
- - - -6 -7 -8- |
-
- # File 'lib/doing/changelog/version.rb', line 6 - -def maj - @maj -end- |
-
- - - -6 -7 -8- |
-
- # File 'lib/doing/changelog/version.rb', line 6 - -def min - @min -end- |
-
- - - -6 -7 -8- |
-
- # File 'lib/doing/changelog/version.rb', line 6 - -def patch - @patch -end- |
-
- - - -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99- |
-
- # File 'lib/doing/changelog/version.rb', line 40 - -def compare(other, comp, inclusive: false) - case comp - when :older - if @maj <= other.maj - if @maj < other.maj - true - elsif @maj == other.maj && (other.min.nil? || @min < other.min) - true - elsif @maj == other.maj && @min == other.min - if other.patch.nil? - false - else - inclusive ? @patch <= other.patch : @patch < other.patch - end - else - false - end - else - false - end - when :newer - if @maj >= other.maj - if @maj > other.maj - true - elsif @maj == other.maj && (other.min.nil? || @min > other.min) - true - elsif @maj == other.maj && @min == other.min - if other.patch.nil? - false - else - inclusive ? @patch >= other.patch : @patch > other.patch - end - else - false - end - else - false - end - when :equal - if @maj == other.maj - if other.min.nil? - true - elsif wild?(other.min) - @min.to_s =~ /^#{other.min}/ ? true : false - else - if @min == other.min - if other.patch.nil? - true - elsif wild?(other.patch) - @patch.to_s =~ /^#{other.patch}/ ? true : false - else - @patch == other.patch - end - else - false - end - end - end - end -end- |
-
- - - -101 -102 -103- |
-
- # File 'lib/doing/changelog/version.rb', line 101 - -def to_s - "#{@maj}.#{@min || 0}.#{@patch || 0}" -end- |
-
- - - -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33- |
-
- # File 'lib/doing/changelog/version.rb', line 12 - -def version_to_a(string) - raise 'Version not a string' unless string.is_a?(String) - - v = string.match(/(?<maj>\d+)(?:\.(?<min>[\d*?]+))?(?:\.(?<patch>[\d*?]+))?/) - - raise 'Error parsing semantic version string' if v.nil? - - maj = v['maj'].to_i - min = case v['min'] - when /[*?]/ - v['min'].sub(/(\d+)?[^\d]/, '\1\d+') - when /^[0-9]+$/ - v['min'].to_i - end - pat = case v['patch'] - when /[*?]/ - v['patch'].sub(/(\d+)?[^\d]/, '\1\d+') - when /^[0-9]+$/ - v['patch'].to_i - end - [maj, min, pat] -end- |
-
- - - -35 -36 -37- |
-
- # File 'lib/doing/changelog/version.rb', line 35 - -def wild?(val) - val.is_a?(String) -end- |
-
- - - -55 -56 -57 -58 -59 -60- |
-
- # File 'lib/doing/wwid/wwid.rb', line 55 - -def initialize - @timers = {} - @recorded_items = [] - @content = Items.new - Doing.auto_tag = true -end- |
-
- - - -27 -28 -29- |
-
- # File 'lib/doing/wwid/wwid.rb', line 27 - -def additional_configs - @additional_configs -end- |
-
- - - -42 -43 -44- |
-
- # File 'lib/doing/wwid/wwid.rb', line 42 - -def config - @config -end- |
-
- - - -45 -46 -47- |
-
- # File 'lib/doing/wwid/wwid.rb', line 45 - -def config_file - @config_file -end- |
-
- - - -36 -37 -38- |
-
- # File 'lib/doing/wwid/wwid.rb', line 36 - -def content - @content -end- |
-
- - - -30 -31 -32- |
-
- # File 'lib/doing/wwid/wwid.rb', line 30 - -def current_section - @current_section -end- |
-
- - - -48 -49 -50- |
-
- # File 'lib/doing/wwid/wwid.rb', line 48 - -def default_option - @default_option -end- |
-
- - - -33 -34 -35- |
-
- # File 'lib/doing/wwid/wwid.rb', line 33 - -def doing_file - @doing_file -end- |
-
- - - -39 -40 -41- |
-
- # File 'lib/doing/wwid/wwid.rb', line 39 - -def initial_content - @initial_content -end- |
-
- - - -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242 -243 -244 -245 -246 -247 -248 -249 -250 -251 -252 -253 -254 -255 -256 -257 -258 -259 -260 -261 -262 -263 -264 -265 -266 -267 -268 -269 -270 -271 -272 -273 -274 -275 -276 -277 -278 -279 -280 -281 -282 -283 -284 -285 -286 -287 -288 -289 -290 -291 -292 -293 -294 -295 -296 -297 -298 -299- |
-
- # File 'lib/doing/wwid/interactive.rb', line 66 - -def act_on(items, opt) - opt ||= {} - actions = %i[editor delete tag flag finish cancel archive output save_to again resume] - has_action = false - single = items.count == 1 - - actions.each do |a| - if opt[a] - has_action = true - break - end - end - - unless has_action - actions = [ - 'add tag', - 'remove tag', - 'autotag', - 'cancel', - 'delete', - 'finish', - 'flag', - 'archive', - 'move', - 'edit', - 'output formatted' - ] - - actions.concat(['resume/repeat', 'begin/reset']) if items.count == 1 - - choice = Prompt.choose_from(actions.map(&:titlecase), - prompt: 'What do you want to do with the selected items? > ', - multiple: true, - sorted: false, - fzf_args: ["--height=#{actions.count + 3}", '--tac', '--no-sort', '--info=hidden']) - return unless choice - - to_do = choice.strip.split(/\n/).map(&:downcase) - - to_do.each do |action| - action = 'resume' if action =~ /^resume/i - action = 'reset' if action =~ /^begin/i - - case action - when /(resume|reset|autotag|archive|delete|finish|cancel|flag)/ - opt[action.to_sym] = true - when /edit/ - opt[:editor] = true - when /(add|remove) tag/ - type = action =~ /^add/ ? 'add' : 'remove' - raise InvalidArgument, "'add tag' and 'remove tag' can not be used together" if opt[:tag] - - = type == 'add' ? (@content) : (items) - - add_msg = type == 'add' ? ', include values with tag(value)' : '' - puts "#{yellow}Separate multiple tags with spaces, hit tab to complete known tags#{add_msg}" - puts "#{boldgreen}Available tags: #{boldwhite}#{.sort.map(&:add_at).join(', ')}" if type == 'remove' - tag = Prompt.read_line(prompt: "Tags to #{type}", completions: ) - - # print "#{yellow("Tag to #{type}: ")}#{reset}" - # tag = $stdin.gets - next if tag =~ /^ *$/ - - opt[:tag] = tag.strip.sub(/^@/, '') - opt[:remove] = true if type == 'remove' - when /output formatted/ - plugins = Plugins.available_plugins(type: :export).sort - output_format = Prompt.choose_from(plugins, - prompt: 'Which output format? > ', - fzf_args: [ - "--height=#{plugins.count + 3}", - '--tac', - '--no-sort', - '--info=hidden' - ]) - next if output_format =~ /^ *$/ - - raise UserCancelled unless output_format - - opt[:output] = output_format.strip - res = opt[:force] ? false : Prompt.yn('Save to file?', default_response: 'n') - if res - # print "#{yellow('File path/name: ')}#{reset}" - # filename = $stdin.gets.strip - filename = Prompt.read_line(prompt: 'File path/name') - next if filename.empty? - - opt[:save_to] = filename - end - when /move/ - section = choose_section.strip - opt[:move] = section.strip unless section =~ /^ *$/ - end - end - end - - if opt[:resume] || opt[:reset] - raise InvalidArgument, 'resume and restart can only be used on a single entry' if items.count > 1 - - item = items[0] - if opt[:resume] && !opt[:reset] - repeat_item(item, { editor: opt[:editor] }) # hooked - elsif opt[:reset] - res = Prompt.enter_text('Start date (blank for current time)', default_response: '') - date = if res =~ /^ *$/ - Time.now - else - res.chronify(guess: :begin) - end - - res = if item.('done', :and) && !opt[:resume] - opt[:force] ? true : Prompt.yn('Remove @done tag?', default_response: 'y') - else - opt[:resume] - end - old_item = item.clone - new_entry = reset_item(item, date: date, resume: res) - @content.update_item(item, new_entry) - Hooks.trigger :post_entry_updated, self, new_entry, old_item - end - write(@doing_file) - - return - end - - if opt[:delete] - delete_items(items, force: opt[:force]) # hooked - write(@doing_file) - - return - end - - if opt[:flag] - tag = Doing.setting('marker_tag', 'flagged') - items.map! do |i| - old_item = i.clone - i.tag(tag, date: false, remove: opt[:remove], single: single) - Hooks.trigger :post_entry_updated, self, i, old_item - end - end - - if opt[:finish] || opt[:cancel] - tag = 'done' - items.map! do |i| - next unless i.should_finish? - - old_item = i.clone - should_date = !opt[:cancel] && i.should_time? - i.tag(tag, date: should_date, remove: opt[:remove], single: single) - Hooks.trigger :post_entry_updated, self, i, old_item - end - end - - if opt[:autotag] - items.map! do |i| - new_title = autotag(i.title) - if new_title == i.title - logger.count(:skipped, level: :debug, message: '%count unchaged %items') - # logger.debug('Autotag:', 'No changes') - else - logger.count(:added_tags) - logger.write(items.count == 1 ? :info : :debug, 'Tagged:', new_title) - old_item = i.clone - i.title = new_title - Hooks.trigger :post_entry_updated, self, i, old_item - end - end - end - - if opt[:tag] - tag = opt[:tag] - items.map! do |i| - old_item = i.clone - i.tag(tag, date: false, remove: opt[:remove], single: single) - i.(Doing.setting('date_tags')) - Hooks.trigger :post_entry_updated, self, i, old_item - end - end - - if opt[:archive] || opt[:move] - section = opt[:archive] ? 'Archive' : guess_section(opt[:move]) - items.map! do |i| - old_item = i.clone - i.move_to(section, label: true) - Hooks.trigger :post_entry_updated, self, i, old_item - end - end - - write(@doing_file) - - if opt[:editor] - sleep 2 # This seems to be necessary between running fzf - # and forking the editor, otherwise vim gets all - # screwy and I can't figure out why - edit_items(items) # hooked - - write(@doing_file) - end - - return unless opt[:output] - - items.each { |i| i.title = "#{i.title} @section(#{i.section})" } - - export_items = Items.new - export_items.concat(items) - export_items.add_section(Section.new('Export'), log: false) - = { section: 'All' } - - if opt[:output] =~ /doing/ - [:output] = 'template' - [:template] = '- %date | %title%note' - else - [:output] = opt[:output] - [:template] = opt[:template] || nil - end - - output = list_section(, items: export_items) # hooked - - if opt[:save_to] - file = File.(opt[:save_to]) - if File.exist?(file) - # Create a backup copy for the undo command - FileUtils.cp(file, "#{file}~") - end - - File.open(file, 'w+') do |f| - f.puts output - end - - logger.warn('File written:', file) - else - Doing::Pager.page output - end -end- |
-
- - - -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67- |
-
- # File 'lib/doing/wwid/modify.rb', line 18 - -def add_item(title, section = nil, opt) - opt ||= {} - section ||= Doing.setting('current_section') - @content.add_section(section, log: false) - opt[:back] ||= opt[:date] ? opt[:date] : Time.now - opt[:date] ||= Time.now - note = Note.new - opt[:timed] ||= false - - note.add(opt[:note]) if opt[:note] - - title = [title.strip.cap_first] - title = title.join(' ') - - if Doing.auto_tag - title = autotag(title) - title.(Doing.setting('default_tags')) if Doing.setting('default_tags').good? - end - - title.compress! - entry = Item.new(opt[:back], title.strip, section) - - if opt[:done] && entry.should_finish? - if entry.should_time? - finish = opt[:done].is_a?(String) ? opt[:done].chronify(guess: :end, context: :today) : opt[:done] - entry.tag('done', value: finish) - else - entry.tag('done') - end - end - - entry.note = note - - if opt[:timed] - last_item = last_entry({ section: section }) - if last_item.(['done'], :not) - finish_date = verify_duration(last_item.date, opt[:back], title: last_item.title) - last_item.tag('done', value: finish_date.strftime('%F %R')) - end - end - - Hooks.trigger :pre_entry_add, self, entry - - @content.push(entry) - # logger.count(:added, level: :debug) - logger.info('New entry:', %(added "#{entry.date.relative_date}: #{entry.title}" to #{section})) - - Hooks.trigger :post_entry_added, self, entry - entry -end- |
-
- - - -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123- |
-
- # File 'lib/doing/wwid/editor.rb', line 103 - -def add_with_editor(**) - raise MissingEditor, 'No EDITOR variable defined in environment' if Util.default_editor.nil? - - input = [:date].strftime('%F %R | ') - input += [:title] - input += "\n#{[:note]}" if [:note] - input = fork_editor(input).strip - - d, title, note = format_input(input) - raise EmptyInput, 'No content' if title.empty? - - if [:ask] - ask_note = Doing::Prompt.read_lines(prompt: 'Add a note') - note.add(ask_note) unless ask_note.empty? - end - - date = d.nil? ? [:date] : d - finish = [:finish_last] || false - add_item(title.cap_first, [:section], { note: note, back: date, timed: finish }) - write(@doing_file) -end- |
-
- - - -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37- |
-
- # File 'lib/doing/wwid/tags.rb', line 18 - -def (items, opt: {}, counts: false) - if counts - = {} - items.each do |item| - item..each do |tag| - if .key?(tag.downcase) - [tag.downcase] += 1 - else - [tag.downcase] = 1 - end - end - end - - .sort_by { |_, count| count } - else - = [] - items.each { |item| .concat(item..map(&:downcase)).uniq! } - .sort - end -end- |
-
- - - -417 -418 -419 -420 -421 -422 -423 -424 -425 -426 -427 -428 -429 -430 -431 -432 -433 -434 -435 -436 -437 -438 -439 -440- |
-
- # File 'lib/doing/wwid/modify.rb', line 417 - -def archive(section = Doing.setting('current_section'), ) - ||= {} - count = [:keep] || 0 - destination = [:destination] || 'Archive' - = [:tags] || [] - bool = [:bool] || :and - - section = section[0] if section.is_a?(Array) && section.count == 1 - section = choose_section if section.nil? || section.empty? || section.is_a?(String) && section =~ /choose/i - archive_all = section =~ /^all$/i # && !(tags.nil? || tags.empty?) - section = guess_section(section) unless archive_all - - @content.add_section(destination, log: true) - # add_section(Section.new('Archive')) if destination =~ /^archive$/i && !@content.section?('Archive') - - destination = guess_section(destination) - - if @content.section?(destination) && (@content.section?(section) || archive_all) - do_archive(section, destination, { count: count, tags: , bool: bool, search: [:search], label: [:label], before: [:before], after: [:after], from: [:from] }) - write(doing_file) - else - raise InvalidArgument, 'Either source or destination does not exist' - end -end- |
-
- - - -449 -450 -451 -452 -453 -454 -455 -456 -457 -458 -459 -460 -461 -462 -463 -464 -465 -466 -467 -468 -469 -470 -471 -472 -473 -474 -475 -476 -477 -478 -479 -480 -481 -482 -483 -484 -485 -486 -487 -488 -489 -490 -491 -492 -493 -494 -495 -496 -497 -498 -499 -500 -501 -502 -503 -504 -505 -506 -507 -508 -509 -510 -511 -512 -513 -514 -515 -516 -517 -518 -519 -520 -521 -522 -523 -524 -525 -526 -527 -528 -529 -530 -531 -532 -533 -534 -535 -536 -537 -538 -539 -540 -541 -542 -543 -544 -545 -546 -547- |
-
- # File 'lib/doing/wwid/modify.rb', line 449 - -def autotag(string) - return unless string - return string unless Doing.auto_tag - - original = string.dup - text = string.dup - - = text.scan(/@\w+/).map { |t| t.sub(/^@/, '') } - tagged = { - whitelisted: [], - synonyms: [], - transformed: [], - replaced: [] - } - - Doing.setting('autotag.whitelist').each do |tag| - next if text =~ /@#{tag}\b/i - - text.sub!(/(?<= |\A)(#{tag.strip})(?= |\Z)/i) do |m| - m.downcase! unless tag =~ /[A-Z]/ - tagged[:whitelisted].push(m) - "@#{m}" - end - end - - Doing.setting('autotag.synonyms').each do |tag, v| - v.each do |word| - word = word.wildcard_to_rx - next unless text =~ /\b#{word}\b/i - - unless .include?(tag) || tagged[:whitelisted].include?(tag) - tagged[:synonyms].push(tag) - tagged[:synonyms] = tagged[:synonyms].uniq - end - end - end - - if Doing.setting('autotag.transform') - Doing.setting('autotag.transform').each do |tag| - next unless tag =~ /\S+:\S+/ - - if tag =~ /::/ - rx, r = tag.split(/::/) - else - rx, r = tag.split(/:/) - end - - flag_rx = %r{/([r]+)$} - if r =~ flag_rx - flags = r.match(flag_rx)[1].split(//) - r.sub!(flag_rx, '') - end - r.gsub!(/\$/, '\\') - rx.sub!(/^@?/, '@') - regex = Regexp.new("(?<= |\\A)#{rx}(?= |\\Z)") - - text.sub!(regex) do - m = Regexp.last_match - new_tag = r - - m.to_a.slice(1, m.length - 1).each_with_index do |v, idx| - next if v.nil? - - new_tag.gsub!("\\#{idx + 1}", v) - end - # Replace original tag if /r - if flags&.include?('r') - tagged[:replaced].concat(new_tag.split(/ /).map { |t| t.sub(/^@/, '') }) - new_tag.split(/ /).map { |t| t.sub(/^@?/, '@') }.join(' ') - else - tagged[:transformed].concat(new_tag.split(/ /).map { |t| t.sub(/^@/, '') }) - tagged[:transformed] = tagged[:transformed].uniq - m[0] - end - end - end - end - - logger.debug('Autotag:', "whitelisted tags: #{tagged[:whitelisted].}") unless tagged[:whitelisted].empty? - logger.debug('Autotag:', "synonyms: #{tagged[:synonyms].}") unless tagged[:synonyms].empty? - logger.debug('Autotag:', "transforms: #{tagged[:transformed].}") unless tagged[:transformed].empty? - logger.debug('Autotag:', "transform replaced: #{tagged[:replaced].}") unless tagged[:replaced].empty? - - = tagged[:synonyms].concat(tagged[:transformed]) - .sort! - .uniq! - - text.() unless .empty? - - if text == original - logger.debug('Autotag:', "no change to \"#{text.strip}\"") - else - = tagged[:whitelisted].concat().concat(tagged[:replaced]) - logger.debug('Autotag:', "added #{.} to \"#{text.strip}\"") - logger.count(:autotag, level: :info, count: 1, message: 'autotag updated %count %items') - end - - text. -end- |
-
- - - -97 -98 -99- |
-
- # File 'lib/doing/wwid/wwidutil.rb', line 97 - -def changes - @content.diff(@initial_content) -end- |
-
- - - -306 -307 -308 -309 -310 -311- |
-
- # File 'lib/doing/wwid/interactive.rb', line 306 - -def choose_section(include_all: false) - = @content.section_titles.sort - .unshift('All') if include_all - choice = Prompt.choose_from(, prompt: 'Choose a section > ', fzf_args: ['--height=60%']) - choice ? choice.strip : choice -end- |
-
- - - -335 -336 -337 -338 -339 -340- |
-
- # File 'lib/doing/wwid/interactive.rb', line 335 - -def choose_section_tag - = @content.section_titles.sort - .concat(@content..sort.map { |t| "@#{t}" }) - choice = Prompt.choose_from(, prompt: 'Choose a section or tag > ', fzf_args: ['--height=60%']) - choice ? choice.strip : choice -end- |
-
- - - -318 -319 -320 -321 -322 -323 -324 -325 -326 -327 -328- |
-
- # File 'lib/doing/wwid/interactive.rb', line 318 - -def choose_tag(section = 'All', items: nil, include_all: false) - items ||= @content.in_section(section) - = (items, counts: true).map { |t, c| "@#{t} (#{c})" } - .unshift('No tag filter') if include_all - choice = Prompt.choose_from(, - sorted: false, - multiple: true, - prompt: 'Choose tag(s) > ', - fzf_args: ['--height=60%']) - choice ? choice.split(/\n/).map { |t| t.strip.sub(/ \(.*?\)$/, '') }.join(' ') : choice -end- |
-
- - - -347 -348 -349 -350- |
-
- # File 'lib/doing/wwid/interactive.rb', line 347 - -def choose_view - choice = Prompt.choose_from(views.sort, prompt: 'Choose a view > ', fzf_args: ['--height=60%']) - choice ? choice.strip : choice -end- |
-
- - - -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68- |
-
- # File 'lib/doing/wwid/wwidutil.rb', line 56 - -def configure(filename = nil) - logger.benchmark(:configure, :start) - - if filename - Doing.config_with(filename, { ignore_local: true }) - elsif ENV['DOING_CONFIG'] - Doing.config_with(ENV['DOING_CONFIG'], { ignore_local: true }) - end - - logger.benchmark(:configure, :finish) - - Doing.set('backup_dir', ENV['DOING_BACKUP_DIR']) if ENV['DOING_BACKUP_DIR'] -end- |
-
- - - -75 -76 -77 -78 -79 -80 -81 -82 -83 -84- |
-
- # File 'lib/doing/wwid/filetools.rb', line 75 - -def create(filename = nil) - filename = @doing_file if filename.nil? - return if File.exist?(filename) && File.stat(filename).size.positive? - - FileUtils.mkdir_p(File.dirname(filename)) unless File.directory?(File.dirname(filename)) - - File.open(filename, 'w+') do |f| - f.puts "#{Doing.setting('current_section')}:" - end -end- |
-
- - - -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25- |
-
- # File 'lib/doing/wwid/wwidutil.rb', line 14 - -def dedup(items, no_overlap: false) - items.delete_if do |item| - duped = false - @content.each do |comp| - duped = no_overlap ? item.overlapping_time?(comp) : item.same_time?(comp) - break if duped - end - logger.count(:skipped, level: :debug, message: '%count overlapping %items') if duped - # logger.log_now(:debug, 'Skipped:', "overlapping entry: #{item.title}") if duped - duped - end -end- |
-
- - - -399 -400 -401 -402 -403 -404 -405 -406 -407 -408- |
-
- # File 'lib/doing/wwid/modify.rb', line 399 - -def delete_items(items, force: false) - items.slice(0, 5).each { |i| puts i.to_pretty } unless force - puts softpurple("+ #{items.size - 5} additional #{'item'.to_p(items.size - 5)}") if items.size > 5 && !force - - res = force ? true : Prompt.yn("Delete #{items.size} #{'item'.to_p(items.size)}?", default_response: 'y') - return unless res - - items.each { |i| Hooks.trigger :post_entry_removed, self, @content.delete_item(i, single: items.count == 1) } - # write(@doing_file) -end- |
-
- - - -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173- |
-
- # File 'lib/doing/wwid/editor.rb', line 125 - -def edit_items(items) - items.sort_by! { |i| i.date } - editable_items = [] - - items.each do |i| - editable = "#{i.date.strftime('%F %R')} | #{i.title}" - old_note = i.note ? i.note.strip_lines.join("\n") : nil - editable += "\n#{old_note}" unless old_note.nil? - editable_items << editable - end - divider = "-----------" - notice =<<~EONOTICE - - # - You may delete entries, but leave all divider lines (---) in place. - # - Start and @done dates replaced with a time string (yesterday 3pm) will - # be parsed automatically. Do not delete the pipe (|) between start date - # and entry title. - EONOTICE - input = "#{editable_items.map(&:strip).join("\n#{divider}\n")}\n" - - new_items = fork_editor(input, message: notice).split(/^#{divider}/).map(&:strip) - - new_items.each_with_index do |new_item, i| - input_lines = new_item.split(/[\n\r]+/).delete_if(&:ignore?) - first_line = input_lines[0]&.strip - - if first_line.nil? || first_line =~ /^#{divider.strip}$/ || first_line.strip.empty? - deleted = @content.delete_item(items[i], single: new_items.count == 1) - Hooks.trigger :post_entry_removed, self, deleted - Doing.logger.info('Deleted:', deleted.title) - else - date, title, note = format_input(new_item) - - note.map!(&:strip) - note.delete_if(&:ignore?) - item = items[i] - old_item = item.clone - item.date = date || items[i].date - item.title = title - item.note = note - if (item.equal?(old_item)) - Doing.logger.count(:skipped, level: :debug) - else - Doing.logger.count(:updated) - Hooks.trigger :post_entry_updated, self, item, old_item - end - end - end -end- |
-
- - - -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212- |
-
- # File 'lib/doing/wwid/editor.rb', line 180 - -def edit_last(section: 'All', options: {}) - [:section] = guess_section(section) - - item = last_entry() - - if item.nil? - logger.debug('Skipped:', 'No entries found') - return - end - - old_item = item.clone - content = ["#{item.date.strftime('%F %R')} | #{item.title.dup}"] - content << item.note.strip_lines.join("\n") unless item.note.empty? - new_item = fork_editor(content.join("\n")) - raise UserCancelled, 'No change' if new_item.strip == content.join("\n").strip - - date, title, note = format_input(new_item) - date ||= item.date - - if title.nil? || title.empty? - logger.debug('Skipped:', 'No content provided') - elsif title == item.title && note.equal?(item.note) && date.equal?(item.date) - logger.debug('Skipped:', 'No change in content') - else - item.date = date unless date.nil? - item.title = title - item.note.add(note, replace: true) - logger.info('Edited:', item.title) - Hooks.trigger :post_entry_updated, self, item, old_item - - write(@doing_file) - end -end- |
-
- - - -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115- |
-
- # File 'lib/doing/wwid/filter.rb', line 61 - -def filter_items(items = Items.new, opt: {}) - logger.benchmark(:filter_items, :start) - time_rx = /^(\d{1,2}+(:\d{1,2}+)?( *(am|pm))?|midnight|noon)$/i - - if items.nil? || items.empty? - section = !opt[:section] || opt[:section].empty? ? 'All' : guess_section(opt[:section]) - if section.is_a?(Array) - section.each do |s| - s = s[0] if s.is_a?(Array) - items.concat(s =~ /^all$/i ? @content.clone : @content.in_section(s)) - end - else - items = section =~ /^all$/i ? @content.clone : @content.in_section(section) - end - end - - unless opt[:time_filter] - opt[:time_filter] = [nil, nil] - if opt[:from] && !opt[:date_filter] - if opt[:from][0].is_a?(String) && opt[:from][0] =~ time_rx - opt[:time_filter] = opt[:from] - elsif opt[:from][0].is_a?(Time) - opt[:date_filter] = opt[:from] - end - end - end - - if opt[:before].is_a?(String) && opt[:before] =~ time_rx - opt[:time_filter][1] = opt[:before] - opt[:before] = nil - end - - if opt[:after].is_a?(String) && opt[:after] =~ time_rx - opt[:time_filter][0] = opt[:after] - opt[:after] = nil - end - - items.sort_by! { |item| [item.date, item.title.downcase] }.reverse - - filtered_items = items.select { |item| item.keep_item?(opt) } - - count = opt[:count].to_i&.positive? ? opt[:count].to_i : filtered_items.count - - output = Items.new - - if opt[:age] && opt[:age].normalize_age == :oldest - output.concat(filtered_items.slice(0, count).reverse) - else - output.concat(filtered_items.reverse.slice(0, count)) - end - - logger.benchmark(:filter_items, :finish) - - output -end- |
-
- - - -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51- |
-
- # File 'lib/doing/wwid/editor.rb', line 10 - -def fork_editor(input = '', message: :default) - # raise NonInteractive, 'Non-interactive terminal' unless $stdout.isatty || ENV['DOING_EDITOR_TEST'] - - raise MissingEditor, 'No EDITOR variable defined in environment' if Util.default_editor.nil? - - tmpfile = Tempfile.new(['doing_temp', '.doing']) - - File.open(tmpfile.path, 'w+') do |f| - f.puts input - unless .nil? - f.puts == :default ? '# First line is the entry title, lines after are added as a note' : - end - end - - pid = Process.fork { system("#{Util.editor_with_args} #{tmpfile.path}") } - - trap('INT') do - begin - Process.kill(9, pid) - rescue StandardError - Errno::ESRCH - end - tmpfile.unlink - tmpfile.close! - exit 0 - end - - Process.wait(pid) - - begin - if $?.exitstatus == 0 - input = IO.read(tmpfile.path) - else - exit_now! 'Cancelled' - end - ensure - tmpfile.close - tmpfile.unlink - end - - input.split(/\n/).delete_if(&:ignore?).join("\n") -end- |
-
- - - -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101- |
-
- # File 'lib/doing/wwid/editor.rb', line 60 - -def format_input(input) - raise EmptyInput, 'No content in entry' if input.nil? || input.strip.empty? - - input_lines = input.split(/[\n\r]+/).delete_if(&:ignore?) - title = input_lines[0]&.strip - raise EmptyInput, 'No content in first line' if title.nil? || title.strip.empty? - - date = nil - iso_rx = /\d{4}-\d\d-\d\d \d\d:\d\d/ - date_rx = /^(?:\s*- )?(?<date>.*?) \| (?=\S)/ - - raise EmptyInput, 'No content' if title.sub(/^.*?\| */, '').strip.empty? - - title.(Doing.setting('date_tags')) - - if title =~ date_rx - m = title.match(date_rx) - d = m['date'] - date = if d =~ iso_rx - Time.parse(d) - else - d.chronify(guess: :begin) - end - title.sub!(date_rx, '').strip! - end - - note = Note.new - note.add(input_lines[1..-1]) if input_lines.length > 1 - # If title line ends in a parenthetical, use that as the note - if note.empty? && title =~ /\s+\(.*?\)$/ - title.sub!(/\s+\((?<note>.*?)\)$/) do - m = Regexp.last_match - note.add(m['note']) - '' - end - end - - note.strip_lines! - note.compress - - [date, title, note] -end- |
-
- - - -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38- |
-
- # File 'lib/doing/wwid/filter.rb', line 27 - -def fuzzy_filter_args(query, case_type) - fzf_args = ['--multi', %(--filter="#{query.sub(/^'?/, "'")}"), '--no-sort', '-d "\|"', '--nth=1'] - fzf_args << case case_type.normalize_case - when :smart - query =~ /[A-Z]/ ? '+i' : '-i' - when :sensitive - '+i' - when :ignore - '-i' - end - fzf_args -end- |
-
- - - -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25- |
-
- # File 'lib/doing/wwid/filter.rb', line 15 - -def fuzzy_filter_items(items, query, case_type: :smart) - scannable = items.map.with_index { |item, idx| "#{item.title} #{item.note.join(' ')}".gsub(/[|*?!]/, '') + "|#{idx}" }.join("\n") - - res = `echo #{Shellwords.escape(scannable)}|#{Prompt.fzf} #{fuzzy_filter_args(query, case_type).join(' ')}` - selected = Items.new - res.split(/\n/).each do |item| - idx = item.match(/\|(\d+)$/)[1].to_i - selected.push(items[idx]) - end - selected -end- |
-
- - - -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88- |
-
- # File 'lib/doing/wwid/wwidutil.rb', line 75 - -def get_diff(filename = nil) - configure if Doing.settings.nil? - - filename ||= Doing.setting('doing_file') - init_doing_file(filename) - current_content = @content.clone - backup_file = Util::Backup.last_backup(filename, count: 1) - raise DoingRuntimeError, 'No undo history to diff' if backup_file.nil? - - backup = WWID.new - backup.config = Doing.settings - backup.init_doing_file(backup_file) - current_content.diff(backup.content) -end- |
-
- - - -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164- |
-
- # File 'lib/doing/wwid/timers.rb', line 154 - -def get_interval(item, formatted: true, record: true) - if item.interval - seconds = item.interval - record_tag_times(item, seconds) if record - return seconds.positive? ? seconds : false unless formatted - - return seconds.positive? ? seconds.time_string(format: :clock) : false - end - - false -end- |
-
- - - -103 -104 -105- |
-
- # File 'lib/doing/wwid/wwid.rb', line 103 - -def get_view(title, fallback: nil) - Doing.setting(['views', title], fallback) -end- |
-
- - - -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57- |
-
- # File 'lib/doing/wwid/guess.rb', line 11 - -def guess_section(frag, guessed: false, suggest: false) - if frag.is_a?(Array) && frag.count == 1 - frag = frag[0] - end - - frag = frag.split(/ *, */).map(&:strip) if frag.is_a?(String) && frag =~ /,/ - - return frag.map { |s| guess_section(s, guessed: guessed, suggest: suggest) } if frag.is_a?(Array) - - return 'All' if frag.empty? || frag.nil? || frag =~ /^all$/i - - frag ||= Doing.setting('current_section') - - return frag.cap_first if @content.section?(frag) - - found = @content.guess_section(frag, distance: 2) - - section = found ? found.title : nil - - if section && suggest - Doing.logger.debug('Match:', %(Assuming "#{section}" from "#{frag}")) - return section - end - - unless section || guessed - alt = guess_view(frag, guessed: true, suggest: true) - if alt - prompt = Color.template("{bw}Did you mean `{xy}doing {by}view {xy}#{alt}`{bw}?{x}") - meant_view = Prompt.yn(prompt, default_response: 'n') - - msg = format('%<y>srun with `%<w>sdoing view %<alt>s%<y>s`', w: boldwhite, y: yellow, alt: alt) - raise Errors::WrongCommand.new(msg, topic: 'Try again:') if meant_view - - end - - res = Prompt.yn("#{boldwhite}Section #{frag.yellow}#{boldwhite} not found, create it", default_response: 'n') - - if res - @content.add_section(frag.cap_first, log: true) - write(@doing_file) - return frag.cap_first - end - - raise Errors::InvalidSection.new("unknown section #{frag.bold.white}", topic: 'Missing:') - end - section ? section.cap_first : nil -end- |
-
- - - -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94- |
-
- # File 'lib/doing/wwid/guess.rb', line 65 - -def guess_view(frag, guessed: false, suggest: false) - views.each { |view| return view if frag.downcase == view.downcase } - view = nil - re = frag.to_rx(distance: 2, case_type: :ignore) - views.each do |v| - next unless v =~ /#{re}/i - - logger.debug('Match:', %(Assuming "#{v}" from "#{frag}")) - view = v - break - end - unless view || guessed - alt = guess_section(frag, guessed: true, suggest: true) - - raise Errors::InvalidView.new(%(unknown view #{frag.bold.white}), topic: 'Missing:') unless alt - - prompt = Color.template("{bw}Did you mean `{xy}doing {by}show {xy}#{alt}`{bw}?{x}") - meant_view = Prompt.yn(prompt, default_response: 'n') - - if meant_view - msg = format('%<y>srun with `%<w>sdoing show %<alt>s%<y>s`', w: boldwhite, y: yellow, alt: alt) - raise Errors::WrongCommand.new(msg, topic: 'Try again:') - - end - - raise Errors::InvalidView.new(%(unknown view #{alt.bold.white}), topic: 'Missing:') - - end - view -end- |
-
- - - -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47- |
-
- # File 'lib/doing/wwid/wwidutil.rb', line 33 - -def import(paths, opt) - opt ||= {} - Plugins.plugins[:import].each do |_, | - next unless opt[:type] =~ /^(#{[:trigger].normalize_trigger})$/i - - if paths.count.positive? - paths.each do |path| - [:class].import(self, path, options: opt) - end - else - [:class].import(self, nil, options: opt) - end - break - end -end- |
-
- - - -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70- |
-
- # File 'lib/doing/wwid/filetools.rb', line 10 - -def init_doing_file(path = nil) - @doing_file = File.(Doing.setting('doing_file')) - - if path.nil? - create(@doing_file) unless File.exist?(@doing_file) - input = IO.read(@doing_file) - input = input.force_encoding('utf-8') if input.respond_to? :force_encoding - logger.debug('Read:', "read file #{@doing_file}") - elsif File.exist?(File.(path)) && File.file?(File.(path)) && File.stat(File.(path)).size.positive? - @doing_file = File.(path) - input = IO.read(File.(path)) - input = input.force_encoding('utf-8') if input.respond_to? :force_encoding - logger.debug('Read:', "read file #{File.(path)}") - elsif path.length < 256 - @doing_file = File.(path) - create(path) - input = IO.read(File.(path)) - input = input.force_encoding('utf-8') if input.respond_to? :force_encoding - logger.debug('Read:', "read file #{File.(path)}") - end - - @other_content_top = [] - @other_content_bottom = [] - - section = nil - lines = input.split(/[\n\r]/) - - lines.each do |line| - next if line =~ /^\s*$/ - - if line =~ /^(\S[\S ]+):\s*(@[\w\-_.]+\s*)*$/ - section = Regexp.last_match(1) - @content.add_section(Section.new(section, original: line), log: false) - elsif line =~ /^\s*- (\d{4}-\d\d-\d\d \d\d:\d\d) \| (.*?)(?: +<([a-z0-9]{32})>)? *$/ - if section.nil? - section = 'Uncategorized' - @content.add_section(Section.new(section, original: 'Uncategorized:'), log: false) - end - - date = Regexp.last_match(1).strip - title = Regexp.last_match(2).strip - id = Regexp.last_match(3) || nil - item = Item.new(date, title, section, [], id) - @content.push(item) - elsif @content.count.zero? - # if content[section].items.length - 1 == current - @other_content_top.push(line) - elsif line =~ /^\S/ - @other_content_bottom.push(line) - else - prev_item = @content.last - prev_item.note = Note.new unless prev_item.note - - prev_item.note.add(line) - # end - end - end - - Hooks.trigger :post_read, self - @initial_content = @content.clone -end- |
-
- - - -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43- |
-
- # File 'lib/doing/wwid/interactive.rb', line 12 - -def interactive(opt) - opt ||= {} - opt[:section] = opt[:section] ? guess_section(opt[:section]) : 'All' - - search = nil - - if opt[:search] - search = opt[:search] - search.sub!(/^'?/, "'") if opt[:exact] - opt[:search] = search - end - - # opt[:query] = opt[:search] if opt[:search] && !opt[:query] - opt[:query] = "!#{opt[:query]}" if opt[:query] && opt[:not] - opt[:multiple] = true - opt[:show_if_single] = true - = %i[after before case date_filter from fuzzy not search section val].each_with_object({}) { - |k, hsh| hsh[k] = opt[k] - } - items = filter_items(Items.new, opt: ) - - = %i[search query exact multiple show_if_single menu sort case].each_with_object({}) { - |k, hsh| hsh[k] = opt[k] - } - include_section = (opt[:section].is_a?(Array) && opt[:section][0] =~ /^all$/i) || (opt[:section].is_a?(String) && opt[:section] =~ /^all$/i) - - selection = Prompt.choose_from_items(items, include_section: include_section, **) - - raise NoResults, 'no items selected' if selection.nil? || selection.empty? - - act_on(selection, opt) -end- |
-
- - - -258 -259 -260 -261 -262 -263 -264 -265 -266 -267 -268 -269 -270 -271 -272 -273 -274 -275 -276 -277 -278 -279 -280 -281 -282 -283 -284 -285 -286 -287 -288 -289 -290 -291 -292 -293 -294 -295 -296 -297 -298 -299- |
-
- # File 'lib/doing/wwid/display.rb', line 258 - -def last(times: true, section: nil, options: {}) - section = section[0] if section.is_a?(Array) && section.count == 1 - section = section.nil? ? 'All' : guess_section(section) - cfg = Doing.setting(['templates', [:config_template]]).deep_merge(Doing.setting('templates.default'), { extend_existing_arrays: true, sort_merged_arrays: true }).deep_merge({ - 'wrap_width' => Doing.setting('wrap_width', 0), - 'date_format' => Doing.setting('default_date_format'), - 'order' => Doing.setting('order', :asc), - 'tags_color' => Doing.setting('tags_color'), - 'duration' => Doing.setting('duration'), - 'interval_format' => Doing.setting('interval_format') - }, { extend_existing_arrays: true, sort_merged_arrays: true }) - [:duration] ||= cfg['duration'] || false - [:interval_format] ||= cfg['interval_format'] || 'text' - - opts = { - case: [:case], - config_template: [:config_template] || 'last', - count: 1, - delete: [:delete], - duration: [:duration], - format: cfg['date_format'], - interval_format: [:interval_format], - not: [:negate], - output: [:output], - section: section, - template: [:template] || cfg['template'], - times: times, - val: [:val], - wrap_width: cfg['wrap_width'] - } - - if [:tag] - opts[:tag_filter] = { - 'tags' => [:tag], - 'bool' => [:tag_bool] - } - end - - opts[:search] = [:search] if [:search] - - list_section(opts) -end- |
-
- - - -325 -326 -327 -328 -329 -330 -331 -332 -333 -334 -335 -336 -337 -338 -339 -340 -341 -342 -343 -344 -345 -346 -347 -348- |
-
- # File 'lib/doing/wwid/display.rb', line 325 - -def last_entry(opt) - opt ||= {} - opt[:tag_bool] ||= :and - opt[:section] ||= Doing.setting('current_section') - - items = filter_items(Items.new, opt: opt) - - logger.debug('Filtered:', "Parameters matched #{items.count} entries") - - if opt[:interactive] - last_entry = Prompt.choose_from_items(items, include_section: opt[:section] =~ /^all$/i, - menu: true, - header: '', - prompt: 'Select an entry > ', - multiple: false, - sort: false, - show_if_single: true - ) - else - last_entry = items.max_by { |item| item.date } - end - - last_entry -end- |
-
- - - -307 -308 -309 -310 -311 -312 -313 -314 -315 -316 -317 -318- |
-
- # File 'lib/doing/wwid/display.rb', line 307 - -def last_note(section = 'All') - section = guess_section(section) - - last_item = last_entry({ section: section }) - - raise NoEntryError, 'No entry found' unless last_item - - logger.log_now(:info, 'Edit note:', last_item.title) - - note = last_item.note&.to_s || '' - "#{last_item.title}\n# EDIT BELOW THIS LINE ------------\n#{note}" -end- |
-
- - - -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128- |
-
- # File 'lib/doing/wwid/display.rb', line 107 - -def list_date(dates, section, times = nil, output = nil, opt) - opt ||= {} - opt[:totals] ||= false - opt[:sort_tags] ||= false - section = guess_section(section) - # :date_filter expects an array with start and end date - dates = dates.split_date_range if dates.instance_of?(String) - - opt[:section] = section - opt[:count] = 0 - opt[:order] = :asc - opt[:date_filter] = dates - opt[:times] = times - opt[:output] = output - - time_rx = /^(\d{1,2}+(:\d{1,2}+)?( *(am|pm))?|midnight|noon)$/ - if opt[:from] && opt[:from][0].is_a?(String) && opt[:from][0] =~ time_rx - opt[:time_filter] = opt[:from] - end - - list_section(opt) -end- |
-
- - - -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96- |
-
- # File 'lib/doing/wwid/display.rb', line 10 - -def list_section(opt, items: Items.new) - logger.benchmark(:list_section, :start) - opt[:config_template] ||= 'default' - - tpl_cfg = Doing.setting(['templates', opt[:config_template]]) - - cfg = if opt[:view_template] - Doing.setting(['views', opt[:view_template]]).deep_merge(tpl_cfg, { extend_existing_arrays: true, sort_merged_arrays: true }) - else - tpl_cfg - end - - cfg.deep_merge({ - 'wrap_width' => Doing.setting('wrap_width') || 0, - 'date_format' => Doing.setting('default_date_format'), - 'order' => Doing.setting('order') || :asc, - 'tags_color' => Doing.setting('tags_color'), - 'duration' => Doing.setting('duration'), - 'interval_format' => Doing.setting('interval_format') - }, { extend_existing_arrays: true, sort_merged_arrays: true }) - - opt[:duration] ||= cfg['duration'] || false - opt[:interval_format] ||= cfg['interval_format'] || 'text' - opt[:count] ||= 0 - opt[:age] ||= :newest - opt[:age] = opt[:age].normalize_age - opt[:format] ||= cfg['date_format'] - opt[:order] ||= cfg['order'] || :asc - opt[:tag_order] ||= :asc - opt[:tags_color] = cfg['tags_color'] || false if opt[:tags_color].nil? - opt[:template] ||= cfg['template'] - opt[:sort_tags] ||= opt[:tag_sort] - - # opt[:highlight] ||= true - title = '' - is_single = true - if opt[:section].nil? - opt[:section] = choose_section - title = opt[:section] - elsif opt[:section].is_a?(Array) - title = opt[:section].join(', ') - elsif opt[:section].is_a?(String) - title = if opt[:section] =~ /^all$/i - if opt[:page_title] - opt[:page_title] - elsif opt[:tag_filter] && opt[:tag_filter]['bool'].normalize_bool != :not - opt[:tag_filter]['tags'].map { |tag| "@#{tag}" }.join(' + ') - else - 'doing' - end - else - guess_section(opt[:section]) - end - end - - items = filter_items(items, opt: opt) - - items.reverse! unless opt[:order].normalize_order == :desc - - if opt[:delete] - delete_items(items, force: opt[:force]) - - write(@doing_file) - return - elsif opt[:editor] - edit_items(items) - - write(@doing_file) - return - elsif opt[:interactive] - opt[:menu] = !opt[:force] - opt[:query] = '' # opt[:search] - opt[:multiple] = true - selected = Prompt.choose_from_items(items.reverse, include_section: opt[:section] =~ /^all$/i, **opt) - - raise NoResults, 'no items selected' if selected.nil? || selected.empty? - - act_on(selected, opt) - return - end - - opt[:output] ||= 'template' - opt[:wrap_width] ||= Doing.setting('templates.default.wrap_width', 0) - - logger.benchmark(:list_section, :finish) - output(items, title, is_single, opt) -end- |
-
- - - -76 -77 -78- |
-
- # File 'lib/doing/wwid/wwid.rb', line 76 - -def logger - @logger ||= Doing.logger -end- |
-
- - - -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242 -243 -244 -245 -246 -247 -248 -249 -250- |
-
- # File 'lib/doing/wwid/display.rb', line 222 - -def recent(count = 10, section = nil, opt) - opt ||= {} - opt[:times] ||= false - opt[:totals] ||= false - opt[:sort_tags] ||= false - - cfg = Doing.setting('templates.recent').deep_merge(Doing.setting('templates.default'), { extend_existing_arrays: true, sort_merged_arrays: true }).deep_merge({ - 'wrap_width' => Doing.setting('wrap_width') || 0, - 'date_format' => Doing.setting('default_date_format'), - 'order' => Doing.setting('order') || :asc, - 'tags_color' => Doing.setting('tags_color'), - 'duration' => Doing.setting('duration'), - 'interval_format' => Doing.setting('interval_format') - }, { extend_existing_arrays: true, sort_merged_arrays: true }) - opt[:duration] ||= cfg['duration'] || false - opt[:interval_format] ||= cfg['interval_format'] || 'text' - - section ||= Doing.setting('current_section') - section = guess_section(section) - - opt[:section] = section - opt[:wrap_width] = cfg['wrap_width'] - opt[:count] = count - opt[:format] = cfg['date_format'] - opt[:template] = opt[:template] || cfg['template'] - opt[:order] = :asc - - list_section(opt) -end- |
-
- - - -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117- |
-
- # File 'lib/doing/wwid/wwid.rb', line 107 - -def rename_view_keys(view) - = view.symbolize_keys - # options.rename_key(:tags, :tag, keep: true) - .rename_key(:output_format, :output) - .rename_key(:tags_bool, :bool) - .rename_key(:tag_sort, :sort_tags) - .rename_key(:negate, :not) - .rename_key(:order, :sort) - - -end- |
-
- - - -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137- |
-
- # File 'lib/doing/wwid/modify.rb', line 100 - -def repeat_item(item, opt) - opt ||= {} - old_item = item.clone - if item.unfinished? && item.should_finish? - if item.should_time? - finish_date = verify_duration(item.date, Time.now, title: item.title) - item.title.tag!('done', value: finish_date.strftime('%F %R')) - else - item.title.tag!('done') - end - Hooks.trigger :post_entry_updated, self, item, old_item - end - - # Remove @done tag - title = item.title.sub(/\s*@done(\(.*?\))?/, '').chomp - section = opt[:in].nil? ? item.section : guess_section(opt[:in]) - Doing.auto_tag = false - - note = opt[:note] || Note.new - - if opt[:editor] - start = opt[:date] ? opt[:date] : Time.now - to_edit = "#{start.strftime('%F %R')} | #{title}" - to_edit += "\n#{note.strip_lines.join("\n")}" unless note.empty? - new_item = fork_editor(to_edit) - date, title, note = format_input(new_item) - - opt[:date] = date unless date.nil? - - if title.nil? || title.empty? - logger.warn('Skipped:', 'No content provided') - return - end - end - - # @content.update_item(original, item) - add_item(title, section, { note: note, back: opt[:date], timed: false }) -end- |
-
- - - -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160- |
-
- # File 'lib/doing/wwid/modify.rb', line 144 - -def repeat_last(opt) - opt ||= {} - opt[:section] ||= 'all' - opt[:section] = guess_section(opt[:section]) - opt[:note] ||= [] - opt[:tag] ||= [] - opt[:tag_bool] ||= :and - - last = last_entry(opt) - if last.nil? - logger.warn('Skipped:', 'No previous entry found') - return - end - - repeat_item(last, opt) - write(@doing_file) -end- |
-
- - - -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86- |
-
- # File 'lib/doing/wwid/modify.rb', line 75 - -def reset_item(item, date: nil, finish_date: nil, resume: false) - date ||= Time.now - item.date = date - if finish_date - item.tag('done', remove: true) - item.tag('done', value: finish_date.strftime('%F %R')) - else - item.tag('done', remove: true) if resume - end - logger.info('Reset:', %(Reset #{resume ? 'and resumed ' : ''} "#{item.title}" in #{item.section})) - item -end- |
-
- - - -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162- |
-
- # File 'lib/doing/wwid/filetools.rb', line 105 - -def rotate(opt) - opt ||= {} - keep = opt[:keep] || 0 - = [] - .concat(opt[:tag].split(/ *, */).map { |t| t.sub(/^@/, '').strip }) if opt[:tag] - bool = opt[:bool] || :and - - sect = opt[:section] !~ /^all$/i ? guess_section(opt[:section]) : 'all' - - section = guess_section(sect) - - section_items = @content.in_section(section) - max = section_items.count - keep.to_i - - counter = 0 - new_content = Items.new - - section_items.each do |item| - break if counter >= max - if opt[:before] - time_string = opt[:before] - cutoff = time_string.chronify(guess: :begin) - end - - unless ((!.empty? && !item.(, bool)) || (opt[:search] && !item.search(opt[:search].to_s)) || (opt[:before] && item.date >= cutoff)) - new_item = @content.delete(item) - Hooks.trigger :post_entry_removed, self, item.clone - raise DoingRuntimeError, "Error deleting item: #{item}" if new_item.nil? - - new_content.add_section(new_item.section, log: false) - new_content.push(new_item) - counter += 1 - end - end - - if counter.positive? - logger.count(:rotated, - level: :info, - count: counter, - message: "Rotated %count %items") - else - logger.info('Skipped:', 'No items were rotated') - end - - write(@doing_file) - - file = @doing_file.sub(/(\.\w+)$/, "_#{Time.now.strftime('%Y-%m-%d')}\\1") - if File.exist?(file) - init_doing_file(file) - @content.concat(new_content).uniq! - logger.warn('File update:', "added entries to existing file: #{file}") - else - @content = new_content - logger.warn('File update:', "created new file: #{file}") - end - - write(file, backup: false) -end- |
-
- - - -85 -86 -87- |
-
- # File 'lib/doing/wwid/wwid.rb', line 85 - -def sections - @content.section_titles -end- |
-
- - - -341 -342 -343 -344 -345 -346 -347 -348 -349 -350 -351 -352 -353 -354 -355 -356 -357 -358 -359 -360 -361 -362 -363 -364 -365 -366 -367 -368 -369 -370 -371 -372 -373 -374 -375 -376 -377 -378 -379 -380 -381 -382 -383 -384 -385 -386 -387 -388 -389 -390 -391- |
-
- # File 'lib/doing/wwid/modify.rb', line 341 - -def stop_start(target_tag, opt) - opt ||= {} - tag = target_tag.dup - opt[:section] ||= Doing.setting('current_section') - opt[:archive] ||= false - opt[:back] ||= Time.now - opt[:new_item] ||= false - opt[:note] ||= false - - opt[:section] = guess_section(opt[:section]) - - tag.sub!(/^@/, '') - - found_items = 0 - - @content.each_with_index do |item, i| - old_item = i.clone - next unless item.section == opt[:section] || opt[:section] =~ /all/i - - next unless item.title =~ /@#{tag}/ - - item.title.([tag, 'done'], remove: true) - item.tag('done', value: opt[:back].strftime('%F %R')) - - found_items += 1 - - if opt[:archive] && opt[:section] != 'Archive' - item.title = item.title.sub(/(?:@from\(.*?\))?(.*)$/, "\\1 @from(#{item.section})") - item.move_to('Archive', label: false, log: false) - logger.count(:completed_archived) - logger.info('Completed/archived:', item.title) - else - logger.count(:completed) - logger.info('Completed:', item.title) - end - Hooks.trigger :post_entry_updated, self, item, old_item - end - - - logger.debug('Skipped:', "No active @#{tag} tasks found.") if found_items.zero? - - if opt[:new_item] - date, title, note = format_input(opt[:new_item]) - opt[:back] = date unless date.nil? - note.add(opt[:note]) if opt[:note] - title.tag!(tag) - add_item(title.cap_first, opt[:section], { note: note, back: opt[:back] }) - end - - write(@doing_file) -end- |
-
- - - -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49- |
-
- # File 'lib/doing/wwid/tags.rb', line 39 - -def tag_groups(items, opt: {}) - all_items = filter_items(items, opt: opt) - = (all_items, opt: {}) - groups = {} - .each do |tag| - groups[tag] ||= [] - groups[tag] = filter_items(all_items, opt: { tag: tag, tag_bool: :or }) - end - - groups -end- |
-
- - - -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242 -243 -244 -245 -246 -247 -248 -249 -250 -251 -252 -253 -254 -255 -256 -257 -258 -259 -260 -261 -262 -263 -264 -265 -266 -267 -268 -269 -270 -271 -272 -273 -274 -275 -276 -277 -278 -279 -280 -281 -282 -283 -284 -285 -286 -287 -288 -289 -290 -291 -292 -293 -294 -295 -296 -297 -298 -299 -300 -301 -302 -303 -304 -305 -306 -307 -308 -309 -310 -311 -312 -313 -314 -315 -316 -317 -318 -319 -320 -321 -322 -323 -324- |
-
- # File 'lib/doing/wwid/modify.rb', line 171 - -def tag_last(opt) # hooked - opt ||= {} - opt[:count] ||= 1 - opt[:archive] ||= false - opt[:tags] ||= ['done'] - opt[:sequential] ||= false - opt[:date] ||= false - opt[:remove] ||= false - opt[:update] ||= false - opt[:autotag] ||= false - opt[:back] ||= false - opt[:unfinished] ||= false - opt[:section] = opt[:section] ? guess_section(opt[:section]) : 'All' - - items = filter_items(Items.new, opt: opt) - - if opt[:interactive] - items = Prompt.choose_from_items(items, include_section: opt[:section] =~ /^all$/i, menu: true, - header: '', - prompt: 'Select entries to tag > ', - multiple: true, - sort: true, - show_if_single: true) - - raise NoResults, 'no items selected' if items.empty? - - end - - raise NoResults, 'no items matched your search' if items.empty? - - if opt[:tags].empty? && !opt[:autotag] - completions = opt[:remove] ? (items) : (@content) - if opt[:remove] - puts "#{yellow}Available tags: #{boldwhite}#{completions.map(&:add_at).join(', ')}" - else - puts "#{yellow}Use tab to complete known tags" - end - opt[:tags] = Doing::Prompt.read_line(prompt: "Enter tag(s) to #{opt[:remove] ? 'remove' : 'add'}", - completions: completions, - default_response: ''). - raise UserCancelled, 'No tags provided' if opt[:tags].empty? - end - - items.each do |item| - old_item = item.clone - added = [] - removed = [] - - item.date = opt[:start_date] if opt[:start_date] - - if opt[:autotag] - new_title = autotag(item.title) if Doing.auto_tag - if new_title == item.title - logger.count(:skipped, level: :debug, message: '%count unchaged %items') - # logger.debug('Autotag:', 'No changes') - else - logger.count(:added_tags) - logger.write(items.count == 1 ? :info : :debug, 'Tagged:', new_title) - item.title = new_title - end - else - if opt[:done_date] - done_date = opt[:done_date] - elsif opt[:sequential] - next_entry = next_item(item) - - done_date = if next_entry.nil? - Time.now - else - next_entry.date - 60 - end - else - done_date = item.calculate_end_date(opt) - end - - opt[:tags].each do |tag| - if tag == 'done' && !item.should_finish? - - Doing.logger.debug('Skipped:', "Item in never_finish: #{item.title}") - logger.count(:skipped, level: :debug) - next - end - - tag = tag.strip - - if tag =~ /^(\S+)\((.*?)\)$/ - m = Regexp.last_match - tag = m[1] - opt[:value] ||= m[2] - end - - if tag =~ /^done$/ && opt[:date] && item.should_time? - max_elapsed = Doing.setting('interaction.confirm_longer_than', 0) - max_elapsed = max_elapsed.chronify_qty if max_elapsed.is_a?(String) - elapsed = done_date - item.date - - if max_elapsed.positive? && (elapsed > max_elapsed) && !opt[:took] - puts boldwhite(item.title) - human = elapsed.time_string(format: :natural) - res = Prompt.yn(yellow("Did this actually take #{human}"), default_response: true) - unless res - new_elapsed = Prompt.enter_text('How long did it take?').chronify_qty - raise InvalidTimeExpression, 'Unrecognized time span entry' unless new_elapsed > 0 - - opt[:took] = new_elapsed - done_date = item.calculate_end_date(opt) if opt[:took] - end - end - end - - if opt[:remove] || opt[:rename] || opt[:value] - rename_to = nil - - if opt[:value] - rename_to = tag - elsif opt[:rename] - rename_to = tag - tag = opt[:rename] - end - old_title = item.title.dup - force = opt[:value].nil? ? false : true - item.title.tag!(tag, remove: opt[:remove], rename_to: rename_to, regex: opt[:regex], value: opt[:value], force: force) - if old_title != item.title - removed << tag - added << rename_to if rename_to - else - logger.count(:skipped, level: :debug) - end - else - old_title = item.title.dup - should_date = opt[:date] && item.should_time? - item.title.tag!('done', remove: true) if tag =~ /done/ && (!should_date || opt[:update]) - item.title.tag!(tag, value: should_date ? done_date.strftime('%F %R') : nil) - added << tag if old_title != item.title - end - end - end - - logger.log_change(tags_added: added, tags_removed: removed, item: item, single: items.count == 1) - - item.note.add(opt[:note]) if opt[:note] - - if opt[:archive] && opt[:section] != 'Archive' && (opt[:count]).positive? - item.move_to('Archive', label: true) - elsif opt[:archive] && opt[:count].zero? - logger.warn('Skipped:', 'Archiving is skipped when operating on all entries') - end - - item.(Doing.setting('date_tags')) - Hooks.trigger :post_entry_updated, self, item, old_item - end - - write(@doing_file) -end- |
-
- - - -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138- |
-
- # File 'lib/doing/wwid/timers.rb', line 14 - -def tag_times(format: :text, sort_by: :time, sort_order: :asc) - return '' if @timers.empty? - - @timers.delete('meanwhile') - - max = @timers.keys.sort_by(&:length).reverse[0].length + 1 - - total = @timers.delete('All') - - = @timers.delete_if { |_k, v| v.zero? } - = if sort_by.normalize_tag_sort == :name - .sort_by { |k, _v| k } - else - .sort_by { |_k, v| v } - end - - .reverse! if sort_order.normalize_order == :asc - case format - when :html - - output = <<EOHEAD - <table> - <caption id="tagtotals">Tag Totals</caption> - <colgroup> - <col style="text-align:left;"/> - <col style="text-align:left;"/> - </colgroup> - <thead> - <tr> - <th style="text-align:left;">project</th> - <th style="text-align:left;">time</th> - </tr> - </thead> - <tbody> -EOHEAD - .reverse.each do |k, v| - if v.positive? - output += "<tr><td style='text-align:left;'>#{k}</td><td style='text-align:left;'>#{v.time_string(format: :clock)}</td></tr>\n" - end - end - tail = <<EOTAIL - <tr> - <td style="text-align:left;" colspan="2"></td> - </tr> - </tbody> - <tfoot> - <tr> - <td style="text-align:left;"><strong>Total</strong></td> - <td style="text-align:left;">#{total.time_string(format: :clock)}</td> - </tr> - </tfoot> - </table> -EOTAIL - output + tail - when :markdown - pad = .map { |k, _| k }.group_by(&:size).max.last[0].length - pad = 7 if pad < 7 - output = <<~EOHEADER - | #{' ' * (pad - 7)}project | time | - | #{'-' * (pad - 1)}: | :------- | - EOHEADER - .reverse.each do |k, v| - if v.positive? - output += "| #{' ' * (pad - k.length)}#{k} | #{v.time_string(format: :clock)} |\n" - end - end - tail = '[Tag Totals]' - output + tail - when :json - output = [] - .reverse.each do |k, v| - output << { - 'tag' => k, - 'seconds' => v, - 'formatted' => v.time_string(format: :clock) - } - end - output - when :human - output = [] - .reverse.each do |k, v| - spacer = '' - (max - k.length).times do - spacer += ' ' - end - output.push("┃ #{spacer}#{k}:#{v.time_string(format: :hm)} ┃") - end - - header = '┏━━ Tag Totals ' - (max - 2).times { header += '━' } - header += '┓' - = '┗' - (max + 12).times { += '━' } - += '┛' - divider = '┣' - (max + 12).times { divider += '━' } - divider += '┫' - output = output.empty? ? '' : "\n#{header}\n#{output.join("\n")}" - output += "\n#{divider}" - spacer = '' - (max - 6).times do - spacer += ' ' - end - total_time = total.time_string(format: :hm) - total = "┃ #{spacer}total: " - total += total_time - total += ' ┃' - output += "\n#{total}" - output += "\n#{}" - output - else - output = [] - .reverse.each do |k, v| - spacer = '' - (max - k.length).times do - spacer += ' ' - end - output.push("#{k}:#{spacer}#{v.time_string(format: :clock)}") - end - - output = output.empty? ? '' : "\n--- Tag Totals ---\n#{output.join("\n")}" - output += "\n\nTotal tracked: #{total.time_string(format: :clock)}\n" - output - end -end- |
-
- - - -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185- |
-
- # File 'lib/doing/wwid/display.rb', line 137 - -def today(times = true, output = nil, opt) - opt ||= {} - opt[:totals] ||= false - opt[:sort_tags] ||= false - - cfg = Doing.setting('templates') - .deep_merge(Doing.setting('templates.default'), { - extend_existing_arrays: true, - sort_merged_arrays: true - }).deep_merge({ - 'wrap_width' => Doing.setting('wrap_width') || 0, - 'date_format' => Doing.setting('default_date_format'), - 'order' => Doing.setting('order') || :asc, - 'tags_color' => Doing.setting('tags_color'), - 'duration' => Doing.setting('duration'), - 'interval_format' => Doing.setting('interval_format') - }, { - extend_existing_arrays: true, - sort_merged_arrays: true - }) - - template = opt[:template] || cfg['template'] - - opt[:duration] ||= cfg['duration'] || false - opt[:interval_format] ||= cfg['interval_format'] || 'text' - - = { - after: opt[:after], - before: opt[:before], - count: 0, - duration: opt[:duration], - from: opt[:from], - format: cfg['date_format'], - interval_format: opt[:interval_format], - only_timed: opt[:only_timed], - order: cfg['order'] || :asc, - output: output, - section: opt[:section], - sort_tags: opt[:sort_tags], - template: template, - times: times, - today: true, - totals: opt[:totals], - wrap_width: cfg['wrap_width'], - tags_color: cfg['tags_color'], - config_template: opt[:config_template] - } - list_section() -end- |
-
- - - -359 -360 -361 -362 -363 -364 -365 -366 -367 -368 -369 -370 -371 -372 -373 -374 -375 -376 -377 -378 -379 -380- |
-
- # File 'lib/doing/wwid/interactive.rb', line 359 - -def verify_duration(date, finish_date, title: nil) - max_elapsed = Doing.setting('interaction.confirm_longer_than', 0) - max_elapsed = max_elapsed.chronify_qty if max_elapsed.is_a?(String) - date = date.chronify(guess: :end, context: :today) if date.is_a?(String) - finish_date = finish_date.chronify(guess: :end, context: :today) if finish_date.is_a?(String) - - elapsed = finish_date - date - - if max_elapsed.positive? && (elapsed > max_elapsed) - puts boldwhite(title) if title - human = elapsed.time_string(format: :natural) - res = Prompt.yn(yellow("Did this entry actually take #{human}"), default_response: true) - unless res - new_elapsed = Prompt.enter_text('How long did it take?').chronify_qty - raise InvalidTimeExpression, 'Unrecognized time span entry' unless new_elapsed.positive? - - finish_date = date + new_elapsed if new_elapsed - end - end - - finish_date -end- |
-
- - - -119 -120 -121 -122 -123 -124 -125- |
-
- # File 'lib/doing/wwid/wwid.rb', line 119 - -def (title) - view = rename_view_keys(get_view(guess_view(title))) - view.deep_merge(rename_view_keys(get_view(guess_view(view[:parent]), fallback: {}))) if view.key?(:parent) - view.deep_merge(rename_view_keys(get_view(view[:config_template], fallback: {}))) if view.key?(:config_template) - view.deep_merge(Doing.setting('templates.default').symbolize_keys) - view -end- |
-
- - - -94 -95 -96- |
-
- # File 'lib/doing/wwid/wwid.rb', line 94 - -def views - Doing.setting('views') ? Doing.setting('views').keys : [] -end- |
-
- - - -91 -92 -93 -94 -95 -96 -97 -98 -99 -100- |
-
- # File 'lib/doing/wwid/filetools.rb', line 91 - -def write(file = nil, backup: true) - Hooks.trigger :pre_write, self, file - output = combined_content - if file.nil? - $stdout.puts output - else - Util.write_to_file(file, output, backup: backup) - run_after if Doing.setting('run_after') - end -end- |
-
- - - -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213- |
-
- # File 'lib/doing/wwid/display.rb', line 195 - -def yesterday(section, times = nil, output = nil, opt) - opt ||= {} - opt[:totals] ||= false - opt[:sort_tags] ||= false - opt[:config_template] ||= 'today' - opt[:yesterday] = true - - section = guess_section(section) - y = (Time.now - (60 * 60 * 24)).strftime('%Y-%m-%d') - opt[:after] = "#{y} #{opt[:after]}" if opt[:after] - opt[:before] = "#{y} #{opt[:before]}" if opt[:before] - - opt[:output] = output - opt[:section] = section - opt[:times] = times - opt[:count] = 0 - - list_section(opt) -end- |
-
- - - -72 -73 -74- |
-
- # File 'lib/doing/good.rb', line 72 - -def good? - false -end- |
-
- - - -76 -77 -78- |
-
- # File 'lib/doing/good.rb', line 76 - -def normalize_tag_sort - :time -end- |
-
- - - -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29- |
-
- # File 'lib/doing/help_monkey_patch.rb', line 7 - -def show_help(, , arguments, out, error) - Doing::Pager.paginate = true - - command_finder = HelpModules::CommandFinder.new(@app, arguments, error) - if [:c] - help_output = HelpModules::HelpCompletionFormat.new(@app, command_finder, arguments).format - out.puts help_output unless help_output.nil? - elsif arguments.empty? || [:c] - Doing::Pager.page HelpModules::GlobalHelpFormat.new(@app, @sorter, @text_wrapping_class).format - else - name = arguments.shift - command = command_finder.find_command(name) - unless command.nil? - Doing::Pager.page HelpModules::CommandHelpFormat.new( - command, - @app, - @sorter, - @synopsis_formatter_class, - @text_wrapping_class - ).format - end - end -end- |
-
- - - -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19- |
-
- # File 'lib/doing/markdown_document_listener.rb', line 9 - -def initialize(, , _arguments, app) - @exe = app.exe_name - if File.exist?('COMMANDS.md') # Back up existing README - FileUtils.mv('COMMANDS.md', 'COMMANDS.bak') - $stderr.puts "Backing up existing COMMANDS.md" - end - @io = File.new('COMMANDS.md', 'w') - @nest = '#' - @arg_name_formatter = GLI::Commands::HelpModules::ArgNameFormatter.new - @parent_command = [] -end- |
-
- - - -21 -22- |
-
- # File 'lib/doing/markdown_document_listener.rb', line 21 - -def beginning -end- |
-
- - - -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129- |
-
- # File 'lib/doing/markdown_document_listener.rb', line 118 - -def command(name, aliases, desc, long_desc, arg_name, ) - @parent_command.push ([name] + aliases).join('|') - arg_name_fmt = @arg_name_formatter.format(arg_name, , []) - arg_name_fmt = " `#{arg_name_fmt.strip}`" if arg_name_fmt - @io.puts header("`$ #{@exe}` <mark>`#{@parent_command.join(' ')}`</mark>#{arg_name_fmt}", 1) - @io.puts - @io.puts "*#{String(desc).strip}*" - @io.puts - cmd_desc = String(long_desc).strip.split("\n").map { |_| "> #{_}" }.join("\n") - @io.puts "#{cmd_desc}\n\n" unless cmd_desc.length == 0 - increment_nest -end- |
-
- - - -111 -112 -113 -114 -115- |
-
- # File 'lib/doing/markdown_document_listener.rb', line 111 - -def commands - @io.puts header("Commands", 1) - @io.puts - increment_nest -end- |
-
- - - -139 -140 -141- |
-
- # File 'lib/doing/markdown_document_listener.rb', line 139 - -def default_command(name) - @io.puts "#### [Default Command] #{name}" unless name.nil? -end- |
-
- - - -132 -133 -134 -135 -136- |
-
- # File 'lib/doing/markdown_document_listener.rb', line 132 - -def end_command(_name) - @parent_command.pop - decrement_nest - @io.puts "* * * * * *\n\n" unless @nest.size > 2 -end- |
-
- - - -143 -144 -145- |
-
- # File 'lib/doing/markdown_document_listener.rb', line 143 - -def end_commands - decrement_nest -end- |
-
- - - -108 -109- |
-
- # File 'lib/doing/markdown_document_listener.rb', line 108 - -def -end- |
-
- - - -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44- |
-
- # File 'lib/doing/markdown_document_listener.rb', line 25 - -def ending - if File.exist?('CREDITS.md') - @io.puts IO.read('CREDITS.md') - @io.puts - end - - if File.exist?('AUTHORS.md') - @io.puts IO.read('AUTHORS.md') - @io.puts - end - - if File.exist?('LICENSE.md') - @io.puts IO.read('LICENSE.md') - @io.puts - end - @io.puts - @io.puts "Documentation generated #{Time.now.strftime('%Y-%m-%d %H:%M')}" - @io.puts - @io.close -end- |
-
- - - -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91- |
-
- # File 'lib/doing/markdown_document_listener.rb', line 80 - -def flag(name, aliases, desc, long_desc, default_value, arg_name, must_match, _type) - invocations = ([name] + Array(aliases)).map { |_| "`" + add_dashes(_) + "`" }.join(' | ') - usage = "#{invocations} #{arg_name || 'arg'}" - @io.puts header(usage, 2) - @io.puts - @io.puts String(desc).strip - @io.puts "\n*Default Value:* `#{default_value || 'None'}`\n" unless default_value.nil? - @io.puts "\n*Must Match:* `#{must_match.to_s}`\n" unless must_match.nil? - cmd_desc = String(long_desc).strip - @io.puts "> #{cmd_desc}\n" unless cmd_desc.length == 0 - @io.puts -end- |
-
- - - -70 -71 -72 -73 -74 -75 -76 -77- |
-
- # File 'lib/doing/markdown_document_listener.rb', line 70 - -def - if @nest.size == 1 - @io.puts "## Global Options" - else - @io.puts header("Options", 1) - end - @io.puts -end- |
-
- - - -47 -48 -49 -50 -51 -52- |
-
- # File 'lib/doing/markdown_document_listener.rb', line 47 - -def program_desc(desc) - @io.puts "# #{@exe} CLI" - @io.puts - @io.puts desc - @io.puts -end- |
-
- - - -54 -55 -56 -57- |
-
- # File 'lib/doing/markdown_document_listener.rb', line 54 - -def program_long_desc(desc) - @io.puts "> #{desc}" - @io.puts -end- |
-
- - - -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106- |
-
- # File 'lib/doing/markdown_document_listener.rb', line 94 - -def switch(name, aliases, desc, long_desc, negatable) - if negatable - name = "[no-]#{name}" if name.to_s.length > 1 - aliases = aliases.map { |_| _.to_s.length > 1 ? "[no-]#{_}" : _ } - end - invocations = ([name] + aliases).map { |_| "`" + add_dashes(_).strip + "`" }.join('|') - @io.puts header("#{invocations}", 2) - @io.puts - @io.puts String(desc).strip - cmd_desc = String(long_desc).strip - @io.puts "\n> #{cmd_desc}\n" unless cmd_desc.length == 0 - @io.puts -end- |
-
- - - -60 -61 -62 -63 -64 -65 -66 -67 -68- |
-
- # File 'lib/doing/markdown_document_listener.rb', line 60 - -def version(version) - @io.puts "*v#{version}*" - @io.puts - # Hacking in the overview file - if File.exist?('OVERVIEW.md') - @io.puts IO.read('OVERVIEW.md') - @io.puts - end -end- |
-
- - - -37 -38 -39- |
-
- # File 'lib/doing/hash.rb', line 37 - -def clone - Marshal.load(Marshal.dump(self)) -end- |
-
- - - -11 -12 -13 -14 -15 -16 -17 -18- |
-
- # File 'lib/doing/hash.rb', line 11 - -def deep_freeze - chilled = {} - each do |k, v| - chilled[k] = v.is_a?(Hash) ? v.deep_freeze : v.freeze - end - - chilled.freeze -end- |
-
- - - -20 -21 -22- |
-
- # File 'lib/doing/hash.rb', line 20 - -def deep_freeze! - replace deep_thaw.deep_freeze -end- |
-
- - - -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125- |
-
- # File 'lib/doing/hash.rb', line 100 - -def deep_set(path, value) - if path.count == 1 - if value.nil? || (value.is_a?(String) && value =~ /^ *$/) || (value.is_a?(Array) && value.empty?) - delete(path[0]) - else - self[path[0]] = value - end - elsif value - self.default_proc = ->(h, k) { h[k] = Hash.new(&h.default_proc) } - dig(*path[0..-2])[path.fetch(-1)] = value - else - return self unless dig(*path) - - dig(*path[0..-2]).delete(path.fetch(-1)) - path.pop - cleaned = self - path.each do |key| - if cleaned[key].empty? - cleaned.delete(key) - break - end - cleaned = cleaned[key] - end - empty? ? nil : self - end -end- |
-
- - - -24 -25 -26 -27 -28 -29 -30 -31- |
-
- # File 'lib/doing/hash.rb', line 24 - -def deep_thaw - chilled = {} - each do |k, v| - chilled[k] = v.is_a?(Hash) ? v.deep_thaw : v.dup - end - - chilled.dup -end- |
-
- - - -33 -34 -35- |
-
- # File 'lib/doing/hash.rb', line 33 - -def deep_thaw! - replace deep_thaw -end- |
-
- - - -195 -196 -197 -198 -199- |
-
- # File 'lib/doing/hash.rb', line 195 - -def delete_unless_key(key, to_delete) - unless key?(key) - to_delete.each { |k| delete(k) } - end -end- |
-
- - - -155 -156 -157- |
-
- # File 'lib/doing/hash.rb', line 155 - -def remove_empty - delete_if { |k, v| !v.is_a?(FalseClass) && !v.good? } -end- |
-
- - - -135 -136 -137 -138 -139 -140 -141- |
-
- # File 'lib/doing/hash.rb', line 135 - -def rename_key(old_key, new_key, keep: false) - return unless key?(old_key) - - self[new_key] = self[old_key] - self[new_key.to_s] = self[old_key] if key?(new_key.to_s) - delete(old_key) unless keep -end- |
-
- - - -148 -149 -150- |
-
- # File 'lib/doing/hash.rb', line 148 - -def rename_keys(*pairs) - pairs.each { |p| rename_key(p[0], p[1]) } -end- |
-
- - - -49 -50 -51 -52 -53 -54 -55- |
-
- # File 'lib/doing/hash.rb', line 49 - -def stringify_keys - each_with_object({}) do |(k, v), hsh| - next if k.is_a?(Symbol) && key?(k.to_s) - - hsh[k.to_s] = v.is_a?(Hash) ? v.stringify_keys : v - end -end- |
-
- - - -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89- |
-
- # File 'lib/doing/hash.rb', line 79 - -def stringify_values - transform_values do |v| - if v.is_a?(Hash) - v.stringify_values - elsif v.is_a?(Symbol) - v.to_s - else - v - end - end -end- |
-
- - - -65 -66 -67 -68 -69 -70 -71- |
-
- # File 'lib/doing/hash.rb', line 65 - -def symbolize_keys - each_with_object({}) do |(k, v), hsh| - next if k.is_a?(String) && key?(k.to_sym) - - hsh[k.to_sym] = v.is_a?(Hash) ? v.symbolize_keys : v - end -end- |
-
- - - -159 -160 -161 -162 -163 -164 -165 -166 -167- |
-
- # File 'lib/doing/hash.rb', line 159 - -def - hsh = dup - if hsh.key?(:tag_filter) && hsh[:tag_filter] - hsh[:tags] = hsh[:tag_filter][:tags] - hsh[:bool] = hsh[:tag_filter][:bool] - hsh.delete(:tag_filter) - end - replace hsh -end- |
-
- - - -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187- |
-
- # File 'lib/doing/hash.rb', line 174 - -def to_view - hsh = symbolize_keys - %i[x save c a s o h e editor m menu i interactive d delete t fuzzy time_filter sort_tags].each do |key| - hsh.delete(key) if hsh.key?(key) - end - - hsh.delete_unless_key(:tag, %i[bool]) - hsh.delete_unless_key(:search, %i[exact case]) - hsh.rename_keys(%i[not negate], %i[tag tags]) - hsh. - - hsh = hsh.remove_empty.stringify_keys.stringify_values - hsh.keys.sort.each_with_object({}) { |k, out| out[k] = hsh[k] } -end- |
-
- - - -7 -8 -9- |
-
- # File 'lib/doing/good.rb', line 7 - -def good? - self >= 0 -end- |
-
- - - -20 -21 -22- |
-
- # File 'lib/doing/good.rb', line 20 - -def good? - !nil? && !self&.empty? || false -end- |
-
- - - -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48- |
-
- # File 'lib/doing/phrase_parser.rb', line 37 - -def self.symbol(str) - case str - when '+' - :must - when '-' - :must_not - when nil - :should - else - raise "Unknown operator: #{str}" - end -end- |
-
- - - -64 -65 -66 -67- |
-
- # File 'lib/doing/phrase_parser.rb', line 64 - -def initialize(operator, phrase) - self.operator = Operator.symbol(operator) - self.phrase = phrase -end- |
-
- - - -62 -63 -64- |
-
- # File 'lib/doing/phrase_parser.rb', line 62 - -def operator - @operator -end- |
-
- - - -62 -63 -64- |
-
- # File 'lib/doing/phrase_parser.rb', line 62 - -def phrase - @phrase -end- |
-
- - - -74 -75 -76 -77 -78 -79- |
-
- # File 'lib/doing/phrase_parser.rb', line 74 - -def initialize(clauses) - grouped = clauses.chunk(&:operator).to_h - self.should_clauses = grouped.fetch(:should, []) - self.must_not_clauses = grouped.fetch(:must_not, []) - self.must_clauses = grouped.fetch(:must, []) -end- |
-
- - - -72 -73 -74- |
-
- # File 'lib/doing/phrase_parser.rb', line 72 - -def must_clauses - @must_clauses -end- |
-
- - - -72 -73 -74- |
-
- # File 'lib/doing/phrase_parser.rb', line 72 - -def must_not_clauses - @must_not_clauses -end- |
-
- - - -72 -73 -74- |
-
- # File 'lib/doing/phrase_parser.rb', line 72 - -def should_clauses - @should_clauses -end- |
-
- - - -105 -106 -107 -108 -109 -110 -111 -112 -113 -114- |
-
- # File 'lib/doing/phrase_parser.rb', line 105 - -def clause_to_query(clause) - case clause - when TermClause - match(clause.term) - when PhraseClause - match_phrase(clause.phrase) - else - raise "Unknown clause type: #{clause}" - end -end- |
-
- - - -116 -117 -118- |
-
- # File 'lib/doing/phrase_parser.rb', line 116 - -def match(term) - term -end- |
-
- - - -120 -121 -122- |
-
- # File 'lib/doing/phrase_parser.rb', line 120 - -def match_phrase(phrase) - phrase -end- |
-
- - - -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103- |
-
- # File 'lib/doing/phrase_parser.rb', line 81 - -def to_elasticsearch - query = {} - - if should_clauses.any? - query[:should] = should_clauses.map do |clause| - clause_to_query(clause) - end - end - - if must_clauses.any? - query[:must] = must_clauses.map do |clause| - clause_to_query(clause) - end - end - - if must_not_clauses.any? - query[:must_not] = must_not_clauses.map do |clause| - clause_to_query(clause) - end - end - - query -end- |
-
- - - -54 -55 -56 -57- |
-
- # File 'lib/doing/phrase_parser.rb', line 54 - -def initialize(operator, term) - self.operator = Operator.symbol(operator) - self.term = term -end- |
-
- - - -52 -53 -54- |
-
- # File 'lib/doing/phrase_parser.rb', line 52 - -def operator - @operator -end- |
-
- - - -52 -53 -54- |
-
- # File 'lib/doing/phrase_parser.rb', line 52 - -def term - @term -end- |
-
- - - -25 -26 -27- |
-
- # File 'lib/doing/cli_status.rb', line 25 - -def clear - $stderr.print format("\r#{esc['kill']}") -end- |
-
- - - -2 -3 -4- |
-
- # File 'lib/doing/cli_status.rb', line 2 - -def cols - @cols ||= `tput cols`.strip.to_i -end- |
-
- - - -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73- |
-
- # File 'lib/doing/cli_status.rb', line 29 - -def esc - e = {} - e['kill'] = "\033[2K" - e['reset'] = "\033[A\033[2K" - e['black'] = "\033[0;0;30m" - e['red'] = "\033[0;0;31m" - e['green'] = "\033[0;0;32m" - e['yellow'] = "\033[0;0;33m" - e['blue'] = "\033[0;0;34m" - e['magenta'] = "\033[0;0;35m" - e['cyan'] = "\033[0;0;36m" - e['white'] = "\033[0;0;37m" - e['bgblack'] = "\033[40m" - e['bgred'] = "\033[41m" - e['bggreen'] = "\033[42m" - e['bgyellow'] = "\033[43m" - e['bgblue'] = "\033[44m" - e['bgmagenta'] = "\033[45m" - e['bgcyan'] = "\033[46m" - e['bgwhite'] = "\033[47m" - e['boldblack'] = "\033[1;30m" - e['boldred'] = "\033[1;31m" - e['boldgreen'] = "\033[0;1;32m" - e['boldyellow'] = "\033[0;1;33m" - e['boldblue'] = "\033[0;1;34m" - e['boldmagenta'] = "\033[0;1;35m" - e['boldcyan'] = "\033[0;1;36m" - e['boldwhite'] = "\033[0;1;37m" - e['boldbgblack'] = "\033[1;40m" - e['boldbgred'] = "\033[1;41m" - e['boldbggreen'] = "\033[1;42m" - e['boldbgyellow'] = "\033[1;43m" - e['boldbgblue'] = "\033[1;44m" - e['boldbgmagenta'] = "\033[1;45m" - e['boldbgcyan'] = "\033[1;46m" - e['boldbgwhite'] = "\033[1;47m" - e['softpurple'] = "\033[0;35;40m" - e['hotpants'] = "\033[7;34;40m" - e['knightrider'] = "\033[7;30;40m" - e['flamingo'] = "\033[7;31;47m" - e['yeller'] = "\033[1;37;43m" - e['whiteboard'] = "\033[1;30;47m" - e['default'] = "\033[0;39m" - e -end- |
-
- - - -21 -22 -23- |
-
- # File 'lib/doing/cli_status.rb', line 21 - -def msg(msg, reset: true, color: 'green', end_char: "\n") - $stderr.print format("#{esc['kill']}#{esc[color]}%s#{esc['default']}%s", msg, reset ? "\r" : end_char) -end- |
-
- - - -6 -7 -8 -9 -10 -11 -12 -13 -14 -15- |
-
- # File 'lib/doing/cli_status.rb', line 6 - -def progress(msg, idx, total, tail = []) - status_width = format("> %s [%#{total.to_s.length}d/%d]: ", msg, 0, total).length - max_width = cols - status_width - if tail.is_a? Array - tail.shift while tail.join(', ').length + 3 > max_width - tail = tail.join(', ') - end - tail.ltrunc!(max_width) - $stderr.print format("#{esc['kill']}#{esc['boldyellow']}> #{esc['boldgreen']}%s #{esc['white']}[#{esc['boldwhite']}%#{@commands.count.to_s.length}d#{esc['boldblack']}/#{esc['boldyellow']}%d#{esc['white']}]: #{esc['boldcyan']}%s#{esc['default']}\r", msg, idx, total, tail) -end- |
-
- - - -17 -18 -19- |
-
- # File 'lib/doing/cli_status.rb', line 17 - -def status(msg, reset: true, end_char: "\n") - $stderr.print format("#{esc['kill']}#{esc['boldyellow']}> #{esc['whiteboard']}%s#{esc['default']}%s", msg, reset ? "\r" : end_char) -end- |
-
- - - -46 -47 -48- |
-
- # File 'lib/doing/good.rb', line 46 - -def good? - !strip.empty? -end- |
-
- - - -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184- |
-
- # File 'lib/doing/colors.rb', line 135 - -def last_color_code - m = scan(ESCAPE_REGEX) - - em = ['0'] - fg = nil - bg = nil - rgbf = nil - rgbb = nil - - m.each do |c| - case c - when '0' - em = ['0'] - fg, bg, rgbf, rgbb = nil - when /^[34]8/ - case c - when /^3/ - fg = nil - rgbf = c - when /^4/ - bg = nil - rgbb = c - end - else - c.split(/;/).each do |i| - x = i.to_i - if x <= 9 - em << x - elsif x >= 30 && x <= 39 - rgbf = nil - fg = x - elsif x >= 40 && x <= 49 - rgbb = nil - bg = x - elsif x >= 90 && x <= 97 - rgbf = nil - fg = x - elsif x >= 100 && x <= 107 - rgbb = nil - bg = x - end - end - end - end - - escape = "\e[#{em.join(';')}m" - escape += "\e[#{rgbb}m" if rgbb - escape += "\e[#{rgbf}m" if rgbf - escape + "\e[#{[fg, bg].delete_if(&:nil?).join(';')}m" -end- |
-
- - - -126 -127 -128- |
-
- # File 'lib/doing/colors.rb', line 126 - -def normalize_color - gsub(/_/, '').sub(/bright/i, 'bold').sub(/bgbold/, 'boldbg') -end- |
-
- - - -6 -7 -8- |
-
- # File 'lib/doing/completion/fig_completion.rb', line 6 - -def sanitize - gsub(/"/, '\"') -end- |
-
- - - -33 -34 -35 -36 -37 -38 -39- |
-
- # File 'lib/doing/string/string.rb', line 33 - -def utf8 - if String.method_defined? :force_encoding - dup.force_encoding('utf-8') - else - self - end -end- |
-
- - - -24 -25 -26- |
-
- # File 'lib/doing/string/string.rb', line 24 - -def valid_id? - strip =~ /^[a-z0-9]{32}$/ ? true : false -end- |
-
- - - -108 -109 -110 -111 -112 -113 -114 -115 -116 -117- |
-
- # File 'lib/doing/colors.rb', line 108 - -def validate_color - valid_color = nil - compiled = '' - normalize_color.split('').each do |char| - compiled += char - valid_color = compiled if Color.attributes.include?(compiled.to_sym) || compiled =~ /^([fb]g?)?#([a-f0-9]{6})$/i - end - - valid_color -end- |
-
- - - -33 -34 -35- |
-
- # File 'lib/doing/good.rb', line 33 - -def good? - !nil? -end- |
-
- - - -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46- |
-
- # File 'lib/doing/time.rb', line 31 - -def humanize(seconds) - s = seconds - m = (s / 60).floor - s = (s % 60).floor - h = (m / 60).floor - m = (m % 60).floor - d = (h / 24).floor - h = h % 24 - - output = [] - output.push("#{d} #{'day'.to_p(d)}") if d.positive? - output.push("#{h} #{'hour'.to_p(h)}") if h.positive? - output.push("#{m} #{'minute'.to_p(m)}") if m.positive? - output.push("#{s} #{'second'.to_p(s)}") if s.positive? - output.join(', ') -end- |
-
- - - -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23- |
-
- # File 'lib/doing/time.rb', line 13 - -def relative_date - if self > Date.today.to_time - strftime(Doing.setting('shortdate_format.today', '%_I:%M%P', exact: true)) - elsif self > (Date.today - 6).to_time - strftime(Doing.setting('shortdate_format.this_week', '%a %_I:%M%P', exact: true)) - elsif year == Date.today.year || (year + 1 == Date.today.year && month > Date.today.month) - strftime(Doing.setting('shortdate_format.this_month', '%m/%d %_I:%M%P', exact: true)) - else - strftime(Doing.setting('shortdate_format.older', '%m/%d/%y %_I:%M%P', exact: true)) - end -end- |
-
- - - -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66- |
-
- # File 'lib/doing/time.rb', line 53 - -def time_ago - if self > Date.today.to_time - output = humanize(Time.now - self) - "#{output} ago" - elsif self > (Date.today - 1).to_time - "Yesterday at #{strftime('%_I:%M:%S%P')}" - elsif self > (Date.today - 6).to_time - strftime('%a %I:%M:%S%P') - elsif self.year == Date.today.year - strftime('%m/%d %I:%M:%S%P') - else - strftime('%m/%d/%Y %I:%M:%S%P') - end -end- |
-
- - - -89 -90 -91- |
-
- # File 'lib/doing/good.rb', line 89 - -def good? - true -end- |
-
- - - -93 -94 -95- |
-
- # File 'lib/doing/good.rb', line 93 - -def normalize_tag_sort - :name -end- |
-
The current version of doing
is <!--VER-->2.1.85<!--END VER-->.
The current version of doing
is <!--VER-->2.1.86<!--END VER-->.
Find all of the documentation in the doing wiki.
@@ -153,7 +153,7 @@The current version of doing
is <!--VER-->2.1.85<!--END VER-->.
The current version of doing
is <!--VER-->2.1.86<!--END VER-->.
Find all of the documentation in the doing wiki.
@@ -153,7 +153,7 @@
- - - -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229- |
-
- # File 'lib/doing/add_options.rb', line 27 - -def (type, cmd, default_template: 'default') - cmd_name = cmd.name.to_s - action = case cmd_name - when /again/ - 'Repeat' - when /grep/ - 'Search' - when /mark/ - 'Flag' - when /(last|tags|view|on)/ - 'Show' - else - cmd_name.capitalize - end - - case type - when :output_template - cmd.desc "Output to export format (#{Doing::Plugins.plugin_names(type: :export)})" - cmd.arg_name 'FORMAT' - cmd.flag %i[o output], type: ExportTemplate - - cmd.desc "Output using a template from configuration" - cmd.arg_name 'TEMPLATE_KEY' - cmd.flag [:config_template], type: TemplateName, default_value: default_template - - cmd.desc 'Override output format with a template string containing %placeholders' - cmd.arg_name 'TEMPLATE_STRING' - cmd.flag [:template] - when :output_template_no_defaults - cmd.desc "Output to export format (#{Doing::Plugins.plugin_names(type: :export)})" - cmd.arg_name 'FORMAT' - cmd.flag %i[o output] - - cmd.desc "Output using a template from configuration" - cmd.arg_name 'TEMPLATE_KEY' - cmd.flag [:config_template], type: TemplateName - - cmd.desc 'Override output format with a template string containing %placeholders' - cmd.arg_name 'TEMPLATE_STRING' - cmd.flag [:template] - when :add_entry - cmd.desc 'Exclude auto tags and default tags' - cmd.switch %i[X noauto], default_value: false, negatable: false - - cmd.desc 'Include a note' - cmd.arg_name 'TEXT' - cmd.flag %i[n note] - - cmd.desc 'Prompt for note via multi-line input' - cmd.switch %i[ask], negatable: false, default_value: false - - cmd.desc "Edit entry with #{Doing::Util.default_editor}" - cmd.switch %i[e editor], negatable: false, default_value: false - - cmd.desc 'Backdate start date for new entry to date string [4pm|20m|2h|yesterday noon]' - cmd.arg_name 'DATE_STRING' - cmd.flag %i[b back started since], type: DateBeginString - when :finish_entry - cmd.desc %(Set finish date to specific date/time (natural langauge parsed, e.g. --at=1:30pm). - Used with --took, backdates start date) - cmd.arg_name 'DATE_STRING' - cmd.flag %i[at finished], type: DateEndString - - cmd.desc %( - Start and end times as a date/time range `doing done --from "1am to 8am"`. - Overrides other date flags. - ) - cmd.arg_name 'TIME_RANGE' - cmd.flag [:from], must_match: REGEX_RANGE - - cmd.desc %(Set completion date to start date plus interval (XX[mhd] or HH:MM). - If used without the --back option, the start date will be moved back to allow - the completion date to be the current time.) - cmd.arg_name 'INTERVAL' - cmd.flag %i[t took for], type: DateIntervalString - when :search - cmd.desc 'Filter entries using a search query, surround with slashes for regex (e.g. "/query.*/"), - start with single quote for exact match ("\'query")' - cmd.arg_name 'QUERY' - cmd.flag [:search] - - cmd.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]' - cmd.arg_name 'TYPE' - cmd.flag [:case], must_match: REGEX_CASE, - default_value: Doing.settings.dig('search', 'case').normalize_case, - type: CaseSymbol - - cmd.desc 'Force exact search string matching (case sensitive)' - cmd.switch %i[x exact], default_value: Doing.config.exact_match?, negatable: Doing.config.exact_match? - when :time_display - cmd.desc 'Show time intervals on @done tasks' - cmd.switch %i[t times], default_value: true, negatable: true - - cmd.desc 'Show elapsed time on entries without @done tag' - cmd.switch [:duration] - - cmd.desc 'Show time totals at the end of output' - cmd.switch [:totals], default_value: false, negatable: false - - cmd.desc 'Sort tags by (name|time)' - default = Doing.setting('tag_sort').normalize_tag_sort || :name - cmd.arg_name 'KEY' - cmd.flag [:tag_sort], must_match: REGEX_TAG_SORT, default_value: default, type: TagSortSymbol - - cmd.desc 'Tag sort direction (asc|desc)' - cmd.arg_name 'DIRECTION' - cmd.flag [:tag_order], must_match: REGEX_SORT_ORDER, default_value: :asc, type: OrderSymbol - - cmd.desc 'Only show items with recorded time intervals' - cmd.switch [:only_timed], default_value: false, negatable: false - when :tag_filter - cmd.desc 'Filter entries by tag. Combine multiple tags with a comma. Wildcards allowed (*, ?)' - cmd.arg_name 'TAG' - cmd.flag [:tag], type: TagArray - - cmd.desc 'Perform a tag value query ("@done > two hours ago" or "@progress < 50"). - May be used multiple times, combined with --bool' - cmd.arg_name 'QUERY' - cmd.flag [:val], multiple: true, must_match: REGEX_VALUE_QUERY - - cmd.desc "#{action} items that *don't* match search/tag filters" - cmd.switch [:not], default_value: false, negatable: false - - cmd.desc 'Boolean used to combine multiple tags (AND|OR|NOT). Use PATTERN to parse + and - as booleans' - cmd.arg_name 'BOOLEAN' - cmd.flag [:bool], must_match: REGEX_BOOL, - default_value: :pattern, - type: BooleanSymbol - when :tag_filter_no_defaults - cmd.desc 'Filter entries by tag. Combine multiple tags with a comma. Wildcards allowed (*, ?)' - cmd.arg_name 'TAG' - cmd.flag [:tag], type: TagArray - - cmd.desc 'Perform a tag value query ("@done > two hours ago" or "@progress < 50"). - May be used multiple times, combined with --bool' - cmd.arg_name 'QUERY' - cmd.flag [:val], multiple: true, must_match: REGEX_VALUE_QUERY - - cmd.desc "#{action} items that *don't* match search/tag filters" - cmd.switch [:not], negatable: false - - cmd.desc 'Boolean used to combine multiple tags. Use PATTERN to parse + and - as booleans' - cmd.arg_name 'BOOLEAN' - cmd.flag [:bool], must_match: REGEX_BOOL, - type: BooleanSymbol - when :time_filter - cmd.desc 'View entries before specified time (e.g. 8am, 12:30pm, 15:00)' - cmd.arg_name 'TIME_STRING' - cmd.flag [:before], type: DateEndString - - cmd.desc 'View entries after specified time (e.g. 8am, 12:30pm, 15:00)' - cmd.arg_name 'TIME_STRING' - cmd.flag [:after], type: DateBeginString - - cmd.desc %( - Time range to show `doing #{cmd.name} --from "12pm to 4pm"` - ) - cmd.arg_name 'TIME_RANGE' - cmd.flag [:from], type: DateRangeString, must_match: REGEX_TIME_RANGE - when :date_filter - if action =~ /Archive/ - cmd.desc 'Archive entries older than date (natural language).' - else - cmd.desc "#{action} entries older than date (natural language). If this is only a time (8am, 1:30pm, 15:00), all - dates will be included, but entries will be filtered by time of day" - end - cmd.arg_name 'DATE_STRING' - cmd.flag [:before], type: DateBeginString - - if action =~ /Archive/ - cmd.desc 'Archive entries newer than date (natural language).' - else - cmd.desc "#{action} entries newer than date (natural language). If this is only a time (8am, 1:30pm, 15:00), all - dates will be included, but entries will be filtered by time of day" - end - cmd.arg_name 'DATE_STRING' - cmd.flag [:after], type: DateEndString - - if action =~ /Archive/ - cmd.desc %( - Date range (natural language) to archive: `doing archive --from "1/1/21 to 12/31/21"`. - ) - else - cmd.desc %( - Date range (natural language) to #{action.downcase}, or a single day to filter on. - To specify a range, use "to": `doing #{cmd_name} --from "monday 8am to friday 5pm"`. - - If values are only time(s) (6am to noon) all dates will be included, but entries will be filtered - by time of day. - ) - end - cmd.arg_name 'DATE_OR_RANGE' - cmd.flag [:from], type: DateRangeString - when :save - cmd.desc 'Save all current command line options as a new view' - cmd.arg_name 'VIEW_NAME' - cmd.flag %i[save], type: String - - cmd.desc 'Title string to be used for output formats that require it' - cmd.arg_name 'TITLE' - cmd.flag %i[title], type: String - end -end- |
-