{: rooto:"YARD::CodeObjects::RootObject:@childrenIC:&YARD::CodeObjects::CodeObjectList[o:$YARD::CodeObjects::ModuleObject;IC;[o:#YARD::CodeObjects::ClassObject;IC;[7o:$YARD::CodeObjects::MethodObject:@module_functionF: @scope: instance:@visibility: public: @pathI"Doing::Item#date:EF:@parameters[: @files[[I"lib/doing/item.rb;Ti :@current_file_has_commentsF: @name: date:@source_type: ruby: @tags[:@docstrings{:@docstringIC:YARD::Docstring")Returns the value of attribute date. ;T;[:@ref_tags[: @allI")Returns the value of attribute date.;T:@unresolved_reference0: @object@ :@hash_flagF: @summary0:@namespace@ : @sourceI"def date @date end;T:@signatureI" def date;T: @dynamicTo; ; F; ;;;;I"Doing::Item#date=;F;[[I" value;T0;[[@i ;F;: date=;;;[;{;IC;"Sets the attribute date ;T;[o:YARD::Tags::Tag :@tag_nameI" param;F: @textI",the value to set the attribute date to.;T;I" value;T: @types0;!@;[;I"QSets the attribute date @param value the value to set the attribute date to.;T; 0;!@;"F;#0;$@ ;%I")def date=(value) @date = value end;T;&I"def date=(value);T;'To; ; F; ;;;;I"Doing::Item#title;F;[;[[@i ;F;: title;;;[;{;IC;"*Returns the value of attribute title. ;T;[;[;I"*Returns the value of attribute title.;T; 0;!@-;"F;#0;$@ ;%I"def title @title end;T;&I"def title;T;'To; ; F; ;;;;I"Doing::Item#title=;F;[[@0;[[@i ;F;: title=;;;[;{;IC;"Sets the attribute title ;T;[o;) ;*I" param;F;+I"-the value to set the attribute title to.;T;I" value;T;,0;!@:;[;I"SSets the attribute title @param value the value to set the attribute title to.;T; 0;!@:;"F;#0;$@ ;%I"+def title=(value) @title = value end;T;&I"def title=(value);T;'To; ; F; ;;;;I"Doing::Item#section;F;[;[[@i ;F;: section;;;[;{;IC;",Returns the value of attribute section. ;T;[;[;I",Returns the value of attribute section.;T; 0;!@L;"F;#0;$@ ;%I"def section @section end;T;&I"def section;T;'To; ; F; ;;;;I"Doing::Item#section=;F;[[@0;[[@i ;F;: section=;;;[;{;IC;"Sets the attribute section ;T;[o;) ;*I" param;F;+I"/the value to set the attribute section to.;T;I" value;T;,0;!@Y;[;I"WSets the attribute section @param value the value to set the attribute section to.;T; 0;!@Y;"F;#0;$@ ;%I"/def section=(value) @section = value end;T;&I"def section=(value);T;'To; ; F; ;;;;I"Doing::Item#note;F;[;[[@i ;F;: note;;;[;{;IC;")Returns the value of attribute note. ;T;[;[;I")Returns the value of attribute note.;T; 0;!@k;"F;#0;$@ ;%I"def note @note end;T;&I" def note;T;'To; ; F; ;;;;I"Doing::Item#note=;F;[[@0;[[@i ;F;: note=;;;[;{;IC;"Sets the attribute note ;T;[o;) ;*I" param;F;+I",the value to set the attribute note to.;T;I" value;T;,0;!@x;[;I"QSets the attribute note @param value the value to set the attribute note to.;T; 0;!@x;"F;#0;$@ ;%I")def note=(value) @note = value end;T;&I"def note=(value);T;'To; ; F; ;;;;I"Doing::Item#initialize;F;[ [I" date;T0[I" title;T0[I" section;T0[I" note;TI"nil;T;[[@i;T;:initialize;;;[;{;IC;"DInitialize an item with date, title, section, and optional note;T;[ o;) ;*I" param;F;+I"The item's start date;T;I" date;T;,[I" Time;T;!@o;) ;*I" param;F;+I"The title;T;I" title;T;,[I" String;T;!@o;) ;*I" param;F;+I"*The section to which the item belongs;T;I" section;T;,[I" String;T;!@o;) ;*I" param;F;+I"The note (optional);T;I" note;T;,[I"Array or String;T;!@o;) ;*I" return;F;+I"a new instance of Item;T;0;,[I" Item;F;!@;[;I"E Initialize an item with date, title, section, and optional note @param date [Time] The item's start date @param title [String] The title @param section [String] The section to which the item belongs @param note [Array or String] The note (optional) ;T; 0;!@:@ref_tag_recurse_counti;"T:@line_rangeo: Range: exclF: begini:endi;$@ :@explicitT;%I"def initialize(date, title, section, note = nil) @date = date.is_a?(Time) ? date : Time.parse(date) @title = title @section = section @note = Note.new(note) end;T;&I"5def initialize(date, title, section, note = nil);T;'To; ; F; ;;;;I"Doing::Item#duration;F;[;[[@i*;T;: duration;;;[;{;IC;"DIf the entry doesn't have a @done date, return the elapsed time;T;[;[;I"DIf the entry doesn't have a @done date, return the elapsed time;T; 0;!@;4i;"T;5o;6;7F;8i);9i);$@ ;:T;%I"\def duration return nil if @title =~ /(?<=^| )@done\b/ return Time.now - @date end;T;&I"def duration;T;'To; ; F; ;;;;I"Doing::Item#interval;F;[;[[@i6;T;: interval;;;[;{;IC;"aGet the difference between the item's start date and the value of its @done tag (if present);T;[o;) ;*I" return;F;+I"Interval in seconds;T;0;,0;!@;[;I" Get the difference between the item's start date and the value of its @done tag (if present) @return Interval in seconds ;T; 0;!@;4i;"T;5o;6;7F;8i0;9i5;$@ ;:T;%I"3def interval @interval ||= calc_interval end;T;&I"def interval;T;'To; ; F; ;;;;I"Doing::Item#end_date;F;[;[[@i?;T;: end_date;;;[;{;IC;"*Get the value of the item's @done tag;T;[o;) ;*I" return;F;+I"@done value;T;0;,[I" Time;T;!@;[;I"L Get the value of the item's @done tag @return [Time] @done value ;T; 0;!@;4i;"T;5o;6;7F;8i:;9i>;$@ ;:T;%I"}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;T;&I"def end_date;T;'To; ; F; ;;;;I"#Doing::Item#calculate_end_date;F;[[I"opt;T0;[[@iC;F;:calculate_end_date;;;[;{;IC;" ;T;[;[;I";T; 0;!@;4i;$@ ;:T;%I"Kdef 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;T;&I" def calculate_end_date(opt);T;'To; ; F; ;;;;I"Doing::Item#id;F;[;[[@iY;T;:id;;;[;{;IC;".Generate a hash that represents the entry;T;[o;) ;*I" return;F;+I"entry hash;T;0;,[I" String;T;!@;[;I"KGenerate a hash that represents the entry @return [String] entry hash;T; 0;!@;4i;"F;5o;6;7F;8iV;9iX;$@ ;:T;%I"?def id @id ||= (@date.to_s + @title + @section).hash end;T;&I" def id;T;'To; ; F; ;;;;I"Doing::Item#equal?;F;[[I" other;T0;[[@id;T;: equal?;;;[;{;IC;"$Test for equality between items;T;[o;) ;*I" param;F;+I"The other item;T;I" other;T;,[I" Item;T;!@ o;) ;*I" return;F;+I"is equal?;T;0;,[I" Boolean;T;!@ ;[;I"p Test for equality between items @param other [Item] The other item @return [Boolean] is equal? ;T; 0;!@ ;4i;"T;5o;6;7F;8i];9ic;$@ ;:T;%I"def equal?(other) return false if @title.strip != other.title.strip return false if @date != other.date return false unless @note.equal?(other.note) true end;T;&I"def equal?(other);T;'To; ; F; ;;;;I"Doing::Item#same_time?;F;[[I" item_b;T0;[[@iu;T;:same_time?;;;[;{;IC;"RTest if two items occur at the same time (same start date and equal duration);T;[o;) ;*I" param;F;+I"The item to compare;T;I" item_b;T;,[I" Item;T;!@(o;) ;*I" return;F;+I"is equal?;T;0;,[I" Boolean;T;!@(;[;I" Test if two items occur at the same time (same start date and equal duration) @param item_b [Item] The item to compare @return [Boolean] is equal? ;T; 0;!@(;4i;"T;5o;6;7F;8in;9it;$@ ;:T;%I"[def same_time?(item_b) date == item_b.date ? interval == item_b.interval : false end;T;&I"def same_time?(item_b);T;'To; ; F; ;;;;I""Doing::Item#overlapping_time?;F;[[I" item_b;T0;[[@i|;T;:overlapping_time?;;;[;{;IC;"YTest if the interval between start date and @done value overlaps with another item's;T;[o;) ;*I" param;F;+I"The item to compare;T;I" item_b;T;,[I" Item;T;!@Co;) ;*I" return;F;+I"overlaps?;T;0;,[I" Boolean;T;!@C;[;I" Test if the interval between start date and @done value overlaps with another item's @param item_b [Item] The item to compare @return [Boolean] overlaps? ;T; 0;!@C;4i;"T;5o;6;7F;8iy;9i{;$@ ;:T;%I"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;T;&I""def overlapping_time?(item_b);T;'To; ; F; ;;;;I"!Doing::Item#expand_date_tags;F;[[I"additional_tags;TI"nil;T;[[@i;T;:expand_date_tags;;;[;{;IC;"Updates the title of the Item by expanding natural language dates within configured date tags (tags whose value is expected to be a date);T;[o;) ;*I" param;F;+I"7An array of additional tag names to consider dates;T;I"additional_tags;T;,0;!@^;[;I" Updates the title of the Item by expanding natural language dates within configured date tags (tags whose value is expected to be a date) @param additional_tags An array of additional tag names to consider dates ;T; 0;!@^;4i;"T;5o;6;7F;8i;9i;$@ ;:T;%I"_def expand_date_tags(additional_tags = nil) @title.expand_date_tags(additional_tags) end;T;&I"0def expand_date_tags(additional_tags = nil);T;'To; ; F; ;;;;I"Doing::Item#tag;F;[[I" tags;T0[I"**options;T0;[[@i;T;:tag;;;[;{;IC;"4Add (or remove) tags from the title of the item;T;[o;) ;*I" param;F;+I"The tags to apply;T;I" tags;T;,[I" Array;T;!@so;) ;*I" param;F;+I"Additional options;T;I" options;T;,0;!@so:YARD::Tags::OptionTag ;*I" option;F;+0;I" options;T;,0: @pairo:YARD::Tags::DefaultTag ;*I" option;F;+I"Include timestamp?;T;I" :date;T;,[I" Boolean;T:@defaults0;!@so;E ;*I" option;F;+0;I" options;T;,0;Fo;G ;*I" option;F;+I"Log as a single change?;T;I" :single;T;,[I" Boolean;T;H0;!@so;E ;*I" option;F;+0;I" options;T;,0;Fo;G ;*I" option;F;+I"&A value to include as @tag(value);T;I" :value;T;,[I" String;T;H0;!@so;E ;*I" option;F;+0;I" options;T;,0;Fo;G ;*I" option;F;+I"%if true remove instead of adding;T;I" :remove;T;,[I" Boolean;T;H0;!@so;E ;*I" option;F;+0;I" options;T;,0;Fo;G ;*I" option;F;+I"3if not nil, rename target tag to this tag name;T;I":rename_to;T;,[I" String;T;H0;!@so;E ;*I" option;F;+0;I" options;T;,0;Fo;G ;*I" option;F;+I"-treat target tag string as regex pattern;T;I" :regex;T;,[I" Boolean;T;H0;!@so;E ;*I" option;F;+0;I" options;T;,0;Fo;G ;*I" option;F;+I"0with rename_to, add tag if it doesn't exist;T;I" :force;T;,[I" Boolean;T;H0;!@s;[;I" Add (or remove) tags from the title of the item @param tags [Array] The tags to apply @param options Additional options @option options :date [Boolean] Include timestamp? @option options :single [Boolean] Log as a single change? @option options :value [String] A value to include as @tag(value) @option options :remove [Boolean] if true remove instead of adding @option options :rename_to [String] if not nil, rename target tag to this tag name @option options :regex [Boolean] treat target tag string as regex pattern @option options :force [Boolean] with rename_to, add tag if it doesn't exist ;T; 0;!@s;4i;"T;5o;6;7F;8i;9i;$@ ;:T;%I"def tag(tags, **options) added = [] removed = [] date = options.fetch(:date, false) options[:value] ||= date ? Time.now.strftime('%F %R') : nil options.delete(:date) single = options.fetch(:single, false) options.delete(:single) tags = tags.to_tags if tags.is_a? ::String remove = options.fetch(:remove, false) tags.each do |tag| bool = remove ? :and : :not if tags?(tag, bool) @title = @title.tag(tag, **options).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;T;&I"def tag(tags, **options);T;'To; ; F; ;;;;I"Doing::Item#tags;F;[;[[@i;T;: tags;;;[;{;IC;"#Get a list of tags on the item;T;[o;) ;*I" return;F;+I"array of tags (no values);T;0;,[I" Array;T;!@;[;I"T Get a list of tags on the item @return [Array] array of tags (no values) ;T; 0;!@;4i;"T;5o;6;7F;8i;9i;$@ ;:T;%I"Udef tags @title.scan(/(?<= |\A)@([^\s(]+)/).map { |tag| tag[0] }.sort.uniq end;T;&I" def tags;T;'To; ; F; ;;;;I"Doing::Item#tag_array;F;[;[[@i;T;:tag_array;;;[;{;IC;" 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;T;&I"Edef search(search, distance: nil, negate: false, case_type: nil);T;'To; ; F; ;;;;I"Doing::Item#should_finish?;F;[;[[@ik;T;:should_finish?;;;[;{;IC;"\Test if item is included in never_finish config and thus should not receive a @done tag;T;[o;) ;*I" return;F;+I""item should receive @done tag;T;0;,[I" Boolean;T;!@;[;I" Test if item is included in never_finish config and thus should not receive a @done tag @return [Boolean] item should receive @done tag ;T; 0;!@;4i;"T;5o;6;7F;8ie;9ij;$@ ;:T;%I"5def should_finish? should?('never_finish') end;T;&I"def should_finish?;T;'To; ; F; ;;;;I"Doing::Item#should_time?;F;[;[[@iu;T;:should_time?;;;[;{;IC;"fTest if item is included in never_time config and thus should not receive a date on the @done tag;T;[o;) ;*I" return;F;+I"#item should receive @done date;T;0;,[I" Boolean;T;!@;[;I" Test if item is included in never_time config and thus should not receive a date on the @done tag @return [Boolean] item should receive @done date ;T; 0;!@;4i;"T;5o;6;7F;8io;9it;$@ ;:T;%I"1def should_time? should?('never_time') end;T;&I"def should_time?;T;'To; ; F; ;;;;I"Doing::Item#move_to;F;[[I"new_section;T0[I" label:;TI" true;T[I" log:;TI" true;T;[[@i;T;: move_to;;;[;{;IC;":Move item from current section to destination section;T;[ o;) ;*I" param;F;+I"The destination section;T;I"new_section;T;,[I" String;T;!@o;) ;*I" param;F;+I"$add @from(original section) tag;T;I" label;T;,[I" Boolean;T;!@o;) ;*I" param;F;+I"log this action;T;I"log;T;,[I" Boolean;T;!@o;) ;*I" return;F;+I" nothing;T;0;,0;!@;[;I"/ Move item from current section to destination section @param new_section [String] The destination section @param label [Boolean] add @from(original section) tag @param log [Boolean] log this action @return nothing ;T; 0;!@;4i;"T;5o;6;7F;8iy;9i;$@ ;:T;%I"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;T;&I"5def move_to(new_section, label: true, log: true);T;'To; ; F; ;;;;I"Doing::Item#to_s;F;[;[[@i;T;: to_s;;;[;{;IC;"=outputs item in Doing file format, including leading tab;T;[;[;I"=outputs item in Doing file format, including leading tab;T; 0;!@ ;4i;"F;5o;6;7F;8i;9i;$@ ;:T;%I"kdef to_s "\t- #{@date.strftime('%Y-%m-%d %H:%M')} | #{@title}#{@note.good? ? "\n#{@note}" : ''}" end;T;&I" def to_s;T;'To; ; F; ;;;;I"Doing::Item#to_pretty;F;[[I"elements:;TI"%i[date title section];T;[[@i;T;:to_pretty;;;[;{;IC;"Eoutputs a colored string with relative date and highlighted tags;T;[o;) ;*I" return;F;+I")Pretty representation of the object.;T;0;,0;!@;[;I"y outputs a colored string with relative date and highlighted tags @return Pretty representation of the object. ;T; 0;!@;4i;"T;5o;6;7F;8i;9i;$@ ;:T;%I"`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.highlight_tags('cyan') end end output.join(' ') end;T;&I"4def to_pretty(elements: %i[date title section]);T;'To; ; F; ;;;;I"Doing::Item#inspect;F;[;[[@i;T;: inspect;;;[;{;IC;";T;[o;) ;*I" private;F;+I";T;0;,0;!@+;[;I" @private;T; 0;!@+;4i;"F;5o;6;7F;8i;9i;$@ ;:T;%I"def inspect # %() %() end;T;&I"def inspect;T;'To; ; F; ;;;;I"Doing::Item#clone;F;[;[[@i;F;: clone;;;[;{;IC;" ;T;[;[;@; 0;!@<;4i;$@ ;:T;%I"5def clone Marshal.load(Marshal.dump(self)) end;T;&I"def clone;T;'To; ; F; ;;: private;I"Doing::Item#should?;F;[[I"key;T0;[[@i;F;: should?;;;[;{;IC;" ;T;[o;) ;*I" return;F;+@;0;,[I" Boolean;T;!@H;[;@; 0;!@H;4i;$@ ;:T;%I"def should?(key) config = Doing.config.settings return true unless config[key].is_a?(Array) config[key].each do |tag| if tag =~ /^@/ return false if tags?(tag.sub(/^@/, '').downcase) elsif section.downcase == tag.downcase return false end end true end;T;&I"def should?(key);T;'To; ; F; ;;;W;I"Doing::Item#calc_interval;F;[;[[@i;F;:calc_interval;;;[;{;IC;" ;T;[;[;@; 0;!@Z;4i;$@ ;:T;%I"def calc_interval done = end_date return nil if done.nil? start = @date t = (done - start).to_i t.positive? ? t : nil end;T;&I"def calc_interval;T;'To; ; F; ;;;W;I"Doing::Item#all_searches?;F;[[I" searches;T0[I"case_type:;TI" :smart;T;[[@i;F;:all_searches?;;;[;{;IC;" ;T;[o;) ;*I" return;F;+@;0;,[@V;!@f;[;@; 0;!@f;4i;$@ ;:T;%I"def all_searches?(searches, case_type: :smart) return true unless searches.good? text = @title + @note.to_s searches.each do |s| rx = Regexp.new(s.wildcard_to_rx, ignore_case(s, case_type)) return false unless text =~ rx end true end;T;&I"3def all_searches?(searches, case_type: :smart);T;'To; ; F; ;;;W;I"Doing::Item#no_searches?;F;[[I" searches;T0[I"case_type:;TI" :smart;T;[[@i;F;:no_searches?;;;[;{;IC;" ;T;[o;) ;*I" return;F;+@;0;,[@V;!@z;[;@; 0;!@z;4i;$@ ;:T;%I"def no_searches?(searches, case_type: :smart) return true unless searches.good? text = @title + @note.to_s searches.each do |s| rx = Regexp.new(s.wildcard_to_rx, ignore_case(s, case_type)) return false if text =~ rx end true end;T;&I"2def no_searches?(searches, case_type: :smart);T;'To; ; F; ;;;W;I"Doing::Item#any_searches?;F;[[I" searches;T0[I"case_type:;TI" :smart;T;[[@i;F;:any_searches?;;;[;{;IC;" ;T;[o;) ;*I" return;F;+@;0;,[@V;!@;[;@; 0;!@;4i;$@ ;:T;%I"def any_searches?(searches, case_type: :smart) return true unless searches.good? text = @title + @note.to_s searches.each do |s| rx = Regexp.new(s.wildcard_to_rx, ignore_case(s, case_type)) return true if text =~ rx end false end;T;&I"3def any_searches?(searches, case_type: :smart);T;'To; ; F; ;;;W;I"Doing::Item#all_tags?;F;[[I" tags;T0;[[@i;F;:all_tags?;;;[;{;IC;" ;T;[o;) ;*I" return;F;+@;0;,[@V;!@;[;@; 0;!@;4i;$@ ;:T;%I"def all_tags?(tags) return true unless tags.good? tags.each do |tag| return false unless @title =~ /@#{tag.wildcard_to_rx}(?= |\(|\Z)/i end true end;T;&I"def all_tags?(tags);T;'To; ; F; ;;;W;I"Doing::Item#no_tags?;F;[[I" tags;T0;[[@i;F;: no_tags?;;;[;{;IC;" ;T;[o;) ;*I" return;F;+@;0;,[@V;!@;[;@; 0;!@;4i;$@ ;:T;%I"def no_tags?(tags) return true unless tags.good? tags.each do |tag| return false if @title =~ /@#{tag.wildcard_to_rx}(?= |\(|\Z)/i end true end;T;&I"def no_tags?(tags);T;'To; ; F; ;;;W;I"Doing::Item#any_tags?;F;[[I" tags;T0;[[@i;F;:any_tags?;;;[;{;IC;" ;T;[o;) ;*I" return;F;+@;0;,[@V;!@;[;@; 0;!@;4i;$@ ;:T;%I"def any_tags?(tags) return true unless tags.good? tags.each do |tag| return true if @title =~ /@#{tag.wildcard_to_rx}(?= |\(|\Z)/i end false end;T;&I"def any_tags?(tags);T;'To; ; F; ;;;W;I"Doing::Item#tag_value;F;[[I"tag;T0;[[@i ;F;:tag_value;;;[;{;IC;" ;T;[;[;@; 0;!@;4i;$@ ;:T;%I"vdef tag_value(tag) res = @title.match(/@#{tag.sub(/^@/, '').wildcard_to_rx}\((.*?)\)/) res ? res[1] : nil end;T;&I"def tag_value(tag);T;'To; ; F; ;;;W;I"Doing::Item#number_or_date;F;[[I" value;T0;[[@i;F;:number_or_date;;;[;{;IC;" ;T;[;[;@; 0;!@;4i;$@ ;:T;%I"def number_or_date(value) return nil unless value if value.strip =~ /^[0-9.]+%?$/ value.strip.to_f else value.strip.chronify(guess: :end) end end;T;&I"def number_or_date(value);T;'To; ; F; ;;;W;I""Doing::Item#split_value_query;F;[[I" query;T0;[[@i;F;:split_value_query;;;[;{;IC;" ;T;[;[;@; 0;!@;4i;$@ ;:T;%I"tdef split_value_query(query) val_rx = /^(!)?@?(\S+) +(!?[<>=][=*]?|[$*^]=) +(.*?)$/ query.match(val_rx) end;T;&I"!def split_value_query(query);T;'To; ; F; ;;;W;I"Doing::Item#any_values?;F;[[I" queries;T0;[[@i;F;:any_values?;;;[;{;IC;" ;T;[o;) ;*I" return;F;+@;0;,[@V;!@;[;@; 0;!@;4i;$@ ;:T;%I"def any_values?(queries) return true unless queries.good? queries.each do |q| parts = split_value_query(q) return true if tag_value_matches?(parts[2], parts[3], parts[4], parts[1]) end false end;T;&I"def any_values?(queries);T;'To; ; F; ;;;W;I"Doing::Item#all_values?;F;[[I" queries;T0;[[@i);F;:all_values?;;;[;{;IC;" ;T;[o;) ;*I" return;F;+@;0;,[@V;!@;[;@; 0;!@;4i;$@ ;:T;%I"def all_values?(queries) return true unless queries.good? queries.each do |q| parts = split_value_query(q) return false unless tag_value_matches?(parts[2], parts[3], parts[4], parts[1]) end true end;T;&I"def all_values?(queries);T;'To; ; F; ;;;W;I"Doing::Item#no_values?;F;[[I" queries;T0;[[@i3;F;:no_values?;;;[;{;IC;" ;T;[o;) ;*I" return;F;+@;0;,[@V;!@!;[;@; 0;!@!;4i;$@ ;:T;%I"def no_values?(queries) return true unless queries.good? queries.each do |q| parts = split_value_query(q) return false if tag_value_matches?(parts[2], parts[3], parts[4], parts[1]) end true end;T;&I"def no_values?(queries);T;'To; ; F; ;;;W;I"#Doing::Item#tag_value_matches?;F;[ [I"tag;T0[I" comp;T0[I" value;T0[I" negate;T0;[[@i=;F;:tag_value_matches?;;;[;{;IC;" ;T;[o;) ;*I" return;F;+@;0;,[@V;!@2;[;@; 0;!@2;4i;$@ ;:T;%I"def tag_value_matches?(tag, comp, value, negate) if all_tags?([tag]) tag_val = tag_value(tag) if (value.chronify.nil? && value =~ /[a-z]/i && comp =~ /^!?==?$/) || comp =~ /[$*^]=/ is_match = case comp when /\^=/ tag_val =~ /^#{value.wildcard_to_rx}/i when /\$=/ tag_val =~ /#{value.wildcard_to_rx}$/i when %r{==} tag_val =~ /^#{value.wildcard_to_rx}$/i else tag_val =~ /#{value.wildcard_to_rx}/i end comp =~ /!/ || negate ? !is_match : is_match else tag_val = number_or_date(tag_val) val = number_or_date(value) return false if val.nil? || tag_val.nil? return false unless val.class == tag_val.class matches = case comp when /^<$/ tag_val < val when /^<=$/ tag_val <= val when /^>$/ tag_val > val when /^>=$/ tag_val >= val when /^!=/ tag_val != val when /^=/ tag_val == val end negate.nil? ? matches : !matches end else false end end;T;&I"5def tag_value_matches?(tag, comp, value, negate);T;'To; ; F; ;;;W;I"Doing::Item#tag_pattern?;F;[[I" tags;T0;[[@ik;F;:tag_pattern?;;;[;{;IC;" ;T;[o;) ;*I" return;F;+@;0;,[@V;!@I;[;@; 0;!@I;4i;$@ ;:T;%I"def tag_pattern?(tags) query = tags.to_query no_tags?(query[:must_not]) && all_tags?(query[:must]) && any_tags?(query[:should]) end;T;&I"def tag_pattern?(tags);T;'To; ; F; ;;;W;I"Doing::Item#split_tags;F;[[I" tags;T0;[[@iq;F;:split_tags;;;[;{;IC;" ;T;[;[;@; 0;!@Z;4i;$@ ;:T;%I":def split_tags(tags) tags.to_tags.tags_to_array end;T;&I"def split_tags(tags);T;'T: @owner@ :@class_mixinsIC;[;i@ :@instance_mixinsIC;[o:YARD::CodeObjects::Proxy :@orignamespace0:@origname0: @imethod0;: Color;$@: @objo; ;IC;[o:&YARD::CodeObjects::ConstantObject;[[I"lib/doing/colors.rb;Ti ;T;:ATTRIBUTES;;;;;[;{;IC;":stopdoc: ;T;[;[;I":stopdoc:;T; 0;!@m;"F;5o;6;7F;8i ;9i ;$@k;I"Doing::Color::ATTRIBUTES;F;%I"ATTRIBUTES = [ [:clear, 0], # String#clear is already used to empty string in Ruby 1.9 [:reset, 0], # synonym for :clear [:bold, 1], [:dark, 2], [:italic, 3], # not widely implemented [:underline, 4], [:underscore, 4], # synonym for :underline [:blink, 5], [:rapid_blink, 6], # not widely implemented [:negative, 7], # no reverse because of String#reverse [:concealed, 8], [:strikethrough, 9], # not widely implemented [:strike, 9], # not widely implemented [:black, 30], [:red, 31], [:green, 32], [:yellow, 33], [:blue, 34], [:magenta, 35], [:purple, 35], [:cyan, 36], [:white, 37], [:bgblack, 40], [:bgred, 41], [:bggreen, 42], [:bgyellow, 43], [:bgblue, 44], [:bgmagenta, 45], [:bgpurple, 45], [:bgcyan, 46], [:bgwhite, 47], [:boldblack, 90], [:boldred, 91], [:boldgreen, 92], [:boldyellow, 93], [:boldblue, 94], [:boldmagenta, 95], [:boldpurple, 95], [:boldcyan, 96], [:boldwhite, 97], [:boldbgblack, 100], [:boldbgred, 101], [:boldbggreen, 102], [:boldbgyellow, 103], [:boldbgblue, 104], [:boldbgmagenta, 105], [:boldbgpurple, 105], [:boldbgcyan, 106], [:boldbgwhite, 107], [:softpurple, '0;35;40'], [:hotpants, '7;34;40'], [:knightrider, '7;30;40'], [:flamingo, '7;31;47'], [:yeller, '1;37;43'], [:whiteboard, '1;30;47'], [:chalkboard, '1;37;40'], [:led, '0;32;40'], [:redacted, '0;30;40'], [:alert, '1;31;43'], [:error, '1;37;41'], [:default, '0;39'] ].map(&:freeze).freeze;T: @valueI"[ [:clear, 0], # String#clear is already used to empty string in Ruby 1.9 [:reset, 0], # synonym for :clear [:bold, 1], [:dark, 2], [:italic, 3], # not widely implemented [:underline, 4], [:underscore, 4], # synonym for :underline [:blink, 5], [:rapid_blink, 6], # not widely implemented [:negative, 7], # no reverse because of String#reverse [:concealed, 8], [:strikethrough, 9], # not widely implemented [:strike, 9], # not widely implemented [:black, 30], [:red, 31], [:green, 32], [:yellow, 33], [:blue, 34], [:magenta, 35], [:purple, 35], [:cyan, 36], [:white, 37], [:bgblack, 40], [:bgred, 41], [:bggreen, 42], [:bgyellow, 43], [:bgblue, 44], [:bgmagenta, 45], [:bgpurple, 45], [:bgcyan, 46], [:bgwhite, 47], [:boldblack, 90], [:boldred, 91], [:boldgreen, 92], [:boldyellow, 93], [:boldblue, 94], [:boldmagenta, 95], [:boldpurple, 95], [:boldcyan, 96], [:boldwhite, 97], [:boldbgblack, 100], [:boldbgred, 101], [:boldbggreen, 102], [:boldbgyellow, 103], [:boldbgblue, 104], [:boldbgmagenta, 105], [:boldbgpurple, 105], [:boldbgcyan, 106], [:boldbgwhite, 107], [:softpurple, '0;35;40'], [:hotpants, '7;34;40'], [:knightrider, '7;30;40'], [:flamingo, '7;31;47'], [:yeller, '1;37;43'], [:whiteboard, '1;30;47'], [:chalkboard, '1;37;40'], [:led, '0;32;40'], [:redacted, '0;30;40'], [:alert, '1;31;43'], [:error, '1;37;41'], [:default, '0;39'] ].map(&:freeze).freeze;T;'To;r;[[@piM;F;:ATTRIBUTE_NAMES;;;;;[;{;IC;" ;T;[;[;@; 0;!@{;$@k;I""Doing::Color::ATTRIBUTE_NAMES;F;%I"1ATTRIBUTE_NAMES = ATTRIBUTES.transpose.first;T;tI"ATTRIBUTES.transpose.first;T;'To; ; F; ;;;;I"Doing::Color#support?;F;[[I" feature;T0;[[@piU;T;: support?;;;[;{;IC;"Returns true if Doing::Color supports the +feature+. The feature :clear, that is mixing the clear color attribute into String, is only supported on ruby implementations, that do *not* already implement the String#clear method. It's better to use the reset color attribute instead.;T;[o;) ;*I" return;F;+@;0;,[@V;!@;[;I"Returns true if Doing::Color supports the +feature+. The feature :clear, that is mixing the clear color attribute into String, is only supported on ruby implementations, that do *not* already implement the String#clear method. It's better to use the reset color attribute instead.;T; 0;!@;4i;"F;5o;6;7F;8iO;9iT;$@k;:T;%I"def support?(feature) case feature when :clear !String.instance_methods(false).map(&:to_sym).include?(:clear) end end;T;&I"def support?(feature);T;'To; ; F; : class;;;I"Doing::Color.coloring?;F;[;[[@pi|;T;:coloring?;;;[;{;IC;"[Returns true, if the coloring function of this module is switched on, false otherwise.;T;[o;) ;*I" return;F;+@;0;,[@V;!@;[;I"[Returns true, if the coloring function of this module is switched on, false otherwise.;T; 0;!@;4i;"F;5o;6;7F;8i;9i{;$@k;:T;%I""def coloring? @coloring end;T;&I"def coloring?;T;'To; ; F; ;w;;;I"Doing::Color.coloring=;F;[[@0;[[@pi;T;:coloring=;;;[;{;IC;"zTurns the coloring on or off globally, so you can easily do this for example: Doing::Color::coloring = STDOUT.isatty ;T;[;[;I"zTurns the coloring on or off globally, so you can easily do this for example: Doing::Color::coloring = STDOUT.isatty;T; 0;!@;"F;5o;6;7F;8i;9i;$@k;%I"1def coloring=(value) @coloring = value end;T;&I"def coloring=(value);T;'To; ; F; ;w;;;I"Doing::Color.coloring;F;[;[[@pi;F;: coloring;;;[;{;IC;" ;T;[;[;@; 0;!@;4i;$@k;:T;%I"*def coloring @coloring ||= true end;T;&I"def coloring;T;'To;r;[[@pi;T;:COLORED_REGEXP;;;;;[;{;IC;"YRegular expression that is used to scan for ANSI-sequences while uncoloring strings. ;T;[;[;I"YRegular expression that is used to scan for ANSI-sequences while uncoloring strings.;T; 0;!@;"F;5o;6;7F;8i;9i;$@k;I"!Doing::Color::COLORED_REGEXP;F;%I"9COLORED_REGEXP = /\e\[(?:(?:[349]|10)[0-7]|[0-9])?m/;T;tI"(/\e\[(?:(?:[349]|10)[0-7]|[0-9])?m/;T;'To; ; F; ;;;;I"Doing::Color#uncolor;F;[[I" string;TI"nil;T;[[@pi;T;: uncolor;;;[;{;IC;"iReturns an uncolored version of the string, that is all ANSI-sequences are stripped from the string.;T;[;[;I"iReturns an uncolored version of the string, that is all ANSI-sequences are stripped from the string.;T; 0;!@;4i;"F;5o;6;7F;8i;9i;$@k;:T;%I"def uncolor(string = nil) # :yields: if block_given? yield.to_str.gsub(COLORED_REGEXP, '') elsif string.respond_to?(:to_str) string.to_str.gsub(COLORED_REGEXP, '') elsif respond_to?(:to_str) to_str.gsub(COLORED_REGEXP, '') else '' end end;T;&I"def uncolor(string = nil);T;'To; ; F; ;;;W;I"Doing::Color#attributes;F;[;[[@pi;T;:attributes;;;[;{;IC;"@Returns an array of all Doing::Color attributes as symbols. ;T;[;[;I"@Returns an array of all Doing::Color attributes as symbols.;T; 0;!@;"F;#0;$@k;:T;%I")def attributes ATTRIBUTE_NAMES end;T;&I"def attributes;T;'To; ; T; ;w;;;I"Doing::Color.attributes;F;@;@;T;;};;;@;{;IC;"@Returns an array of all Doing::Color attributes as symbols.;T;[;[;I"@Returns an array of all Doing::Color attributes as symbols.;T; 0;!@;4i;"F;5o;6;7F;8i;9i;$@k;:T;%@;&@;'T;i@k;jIC;[@k;i@k;kIC;[;i@k:@attributesIC:SymbolHash{;wIC;{;zIC;{: read@: write@:@symbolize_valueT;}T;IC;{;}T;}T: @aliases{: @groups[;[[@pi ;T;;p;;;;;[;{;IC;"Terminal color functions;T;[;[;I"Terminal color functions;T; 0;!@k;4i;"F;5o;6;7F;8i ;9i ;$@;I"Doing::Color;F;'T: @type: module;i@ ;~IC;{;wIC;{;}T;IC;{ ;IC;{;{@ ;|@;}T;-IC;{;{@-;|@:;}T;/IC;{;{@L;|@Y;}T;1IC;{;{@k;|@x;}T;}T;}T;~{;[;[[@i ;T;: Item;;;;;[;{;IC;",This class describes a single WWID item;T;[;[;I". This class describes a single WWID item ;T; 0;!@ ;4i;"T;5o;6;7F;8i ;9i ;$@;I"Doing::Item;F:@superclasso;l ;m0;n0;o0;: Object;$@;qo; ;IC;[o; ; F; ;;;;I"Object#good?;F;[;[[I"lib/doing/good.rb;Ti;T;: good?;;;[;{;IC;"$Tests if object is nil or empty;T;[o;) ;*I" return;F;+I".true if object is defined and has content;T;0;,[I" Boolean;T;!@ ;[;I"s Tests if object is nil or empty @return [Boolean] true if object is defined and has content ;T; 0;!@ ;4i;"T;5o;6;7F;8i ;9i;$@;:T;%I"%def good? !nil? && !empty? end;T;&I"def good?;T;'T;i@;jIC;[;i@;kIC;[;i@;~IC;{;wIC;{;}T;IC;{;}T;}T;~{;[;[[@%i ;T;;;;;;;[;{;IC;"Object helpers;T;[;[;I"Object helpers;T; 0;!@;4i;"F;5o;6;7F;8i ;9i ;$@;I" Object;F;o;l ;m0;n0;o0;:BasicObject;$@;q0;;w;'T;0;'To; ;IC;[o; ; F; ;;;;I"Doing::Note#initialize;F;[[I" note;TI"[];T;[[I"lib/doing/note.rb;Ti;T;;3;;;[;{;IC;"Initializes a new note;T;[o;) ;*I" param;F;+I")Initial note, can be string or array;T;I" note;T;,[I" Array;T;!@Ho;) ;*I" return;F;+I"a new instance of Note;T;0;,[I" Note;F;!@H;[;I"o Initializes a new note @param note [Array] Initial note, can be string or array ;T; 0;!@H;4i;"T;5o;6;7F;8i;9i;$@F;:T;%I"Adef initialize(note = []) super() add(note) if note end;T;&I"def initialize(note = []);T;'To; ; F; ;;;;I"Doing::Note#add;F;[[I" note;T0[I" replace:;TI" false;T;[[@Pi";T;:add;;;[;{;IC;":Add note contents, optionally replacing existing note;T;[o;) ;*I" param;F;+I"3The note to add, can be String, Array, or Note;T;I" note;T;,[I" Array;T;!@eo;) ;*I" param;F;+I"replace existing content;T;I" replace;T;,[I" Boolean;T;!@e;[;I" Add note contents, optionally replacing existing note @param note [Array] The note to add, can be String, Array, or Note @param replace [Boolean] replace existing content ;T; 0;!@e;4i;"T;5o;6;7F;8i;9i!;$@F;:T;%I"def add(note, replace: false) clear if replace if note.is_a?(String) append_string(note) elsif note.is_a?(Array) append(note) end end;T;&I""def add(note, replace: false);T;'To; ; F; ;;;;I"Doing::Note#compress;F;[;[[@Pi0;T;: compress;;;[;{;IC;"(Remove blank lines and comments (#);T;[o;) ;*I" return;F;+I"compressed array;T;0;,[I" Array;T;!@;[;I"P Remove blank lines and comments (#) @return [Array] compressed array ;T; 0;!@;4i;"T;5o;6;7F;8i+;9i/;$@F;:T;%I"Cdef compress delete_if { |l| l =~ /^\s*$/ || l =~ /^#/ } end;T;&I"def compress;T;'To; ; F; ;;;;I"Doing::Note#compress!;F;[;[[@Pi4;F;:compress!;;;[;{;IC;" ;T;[;[;@; 0;!@;4i;$@F;:T;%I")def compress! replace compress end;T;&I"def compress!;T;'To; ; F; ;;;;I"Doing::Note#strip_lines;F;[;[[@Pi>;T;:strip_lines;;;[;{;IC;"JRemove leading/trailing whitespace for every line of note;T;[o;) ;*I" return;F;+I"Stripped note;T;0;,[I" Array;T;!@;[;I"o Remove leading/trailing whitespace for every line of note @return [Array] Stripped note ;T; 0;!@;4i;"T;5o;6;7F;8i8;9i=;$@F;:T;%I"'def strip_lines map(&:strip) end;T;&I"def strip_lines;T;'To; ; F; ;;;;I"Doing::Note#strip_lines!;F;[;[[@PiB;F;:strip_lines!;;;[;{;IC;" ;T;[;[;@; 0;!@;4i;$@F;:T;%I"/def strip_lines! replace strip_lines end;T;&I"def strip_lines!;T;'To; ; F; ;;;;I"Doing::Note#to_s;F;[;[[@PiH;T;;S;;;[;{;IC;"Note as multi-line string;T;[;[;I" Note as multi-line string;T; 0;!@;4i;"T;5o;6;7F;8iF;9iG;$@F;:T;%I"Jdef to_s compress.strip_lines.map { |l| "\t\t#{l}" }.join("\n") end;T;&I" def to_s;T;'To; ; F; ;;;;I"Doing::Note#inspect;F;[;[[@PiM;T;;U;;;[;{;IC;";T;[o;) ;*I" private;F;+I";T;0;,0;!@;[;I" @private;T; 0;!@;4i;"F;5o;6;7F;8iL;9iL;$@F;:T;%I"kdef inspect "" end;T;&I"def inspect;T;'To; ; F; ;;;;I"Doing::Note#equal?;F;[[I" other;T0;[[@PiX;T;;@;;;[;{;IC;"ITest if a note is equal (compare string representations);T;[o;) ;*I" param;F;+I"The other Note;T;I" other;T;,[I" Note;T;!@o;) ;*I" return;F;+I"true if equal;T;0;,[I" Boolean;T;!@;[;I" Test if a note is equal (compare string representations) @param other [Note] The other Note @return [Boolean] true if equal;T; 0;!@;4i;"T;5o;6;7F;8iQ;9iW;$@F;:T;%I"Xdef equal?(other) return false unless other.is_a?(Note) to_s == other.to_s end;T;&I"def equal?(other);T;'To; ; F; ;;;W;I"Doing::Note#append;F;[[I" lines;T0;[[@Pie;T;: append;;;[;{;IC;"'Append an array of strings to note;T;[o;) ;*I" param;F;+I"Array of strings;T;I" lines;T;,[I" Array;T;!@;[;I"V Append an array of strings to note @param lines [Array] Array of strings ;T; 0;!@;4i;"T;5o;6;7F;8i`;9id;$@F;:T;%I"=def append(lines) concat(lines) replace compress end;T;&I"def append(lines);T;'To; ; F; ;;;W;I"Doing::Note#append_string;F;[[I" input;T0;[[@Pip;T;:append_string;;;[;{;IC;"(Append a string to the note content;T;[o;) ;*I" param;F;+I"-The input string, newlines will be split;T;I" input;T;,[I" String;T;!@;[;I"~ Append a string to the note content @param input [String] The input string, newlines will be split ;T; 0;!@;4i;"T;5o;6;7F;8ij;9io;$@F;:T;%I"]def append_string(input) concat(input.split(/\n/).map(&:strip)) replace compress end;T;&I"def append_string(input);T;'T;i@F;jIC;[;i@F;kIC;[;i@F;~IC;{;wIC;{;}T;IC;{;}T;}T;~{;[;[[@Pi ;T;: Note;;;;;[;{;IC;"'This class describes an item note.;T;[;[;I") This class describes an item note. ;T; 0;!@F;4i;"T;5o;6;7F;8i ;9i ;$@;I"Doing::Note;F;o;l ;m0;n0;o0;: Array;$@;qo; ;IC;[o; ; F; ;;;;I"Array#good?;F;[;[[@%i);T;;;;;[;{;IC;"$Tests if object is nil or empty;T;[o;) ;*I" return;F;+I".true if object is defined and has content;T;0;,[I" Boolean;T;!@<;[;I"s Tests if object is nil or empty @return [Boolean] true if object is defined and has content ;T; 0;!@<;4i;"T;5o;6;7F;8i#;9i(;$@:;:T;%I"%def good? !nil? && !empty? end;T;&I"def good?;T;'T;i@:;jIC;[;i@:;kIC;[;i@:;~IC;{;wIC;{;}T;IC;{;}T;}T;~{;[;[[@%i";F;;;;;;;[;{;IC;" ;T;[;[;@; 0;!@:;4i;$@;I" Array;F;o;l ;m0;n0;o0;;;$@;q@;0;'T;0;'To; ;IC;[o; ; F; ;;;;I"Doing::Util#user_home;F;[;[[I"lib/doing/util.rb;Ti ;F;:user_home;;;[;{;IC;" ;T;[;[;@; 0;!@a;4i;$@_;:T;%I"gdef user_home if Dir.respond_to?('home') Dir.home else File.expand_path('~') end end;T;&I"def user_home;T;'To; ; F; ;;;;I"Doing::Util#exec_available;F;[[I"cli;T0;[[@fi;T;:exec_available;;;[;{;IC;"+Test if command line tool is available;T;[o;) ;*I" param;F;+I" The name or path of the cli;T;I"cli;T;,[I" String;T;!@n;[;I"e Test if command line tool is available @param cli [String] The name or path of the cli ;T; 0;!@n;4i;"T;5o;6;7F;8i;9i;$@_;:T;%I"_def exec_available(cli) return false unless cli.good? !TTY::Which.which(cli).nil? end;T;&I"def exec_available(cli);T;'To; ; F; ;;;;I"%Doing::Util#first_available_exec;F;[[I"*commands;T0;[[@fi&;T;:first_available_exec;;;[;{;IC;">Return the first valid executable from a list of commands;T;[o;) ;*I" example;F;+I"JDoing::Util.first_available_exec('bat', 'less -Xr', 'more -r', 'cat');T;I";T;,0;!@;[;I" Return the first valid executable from a list of commands @example Doing::Util.first_available_exec('bat', 'less -Xr', 'more -r', 'cat') ;T; 0;!@;4i;"T;5o;6;7F;8i ;9i%;$@_;:T;%I"def first_available_exec(*commands) commands.compact.map(&:strip).reject(&:empty?).uniq .find { |cmd| exec_available(cmd.split.first) } end;T;&I"(def first_available_exec(*commands);T;'To; ; F; ;;;;I"#Doing::Util#merge_default_proc;F;[[I" target;T0[I"overwrite;T0;[[@fi+;F;:merge_default_proc;;;[;{;IC;" ;T;[;[;@; 0;!@;4i;$@_;:T;%I"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;T;&I".def merge_default_proc(target, overwrite);T;'To; ; F; ;;;;I"(Doing::Util#duplicate_frozen_values;F;[[I" target;T0;[[@fi1;F;:duplicate_frozen_values;;;[;{;IC;" ;T;[;[;@; 0;!@;4i;$@_;:T;%I"def duplicate_frozen_values(target) target.each do |key, val| target[key] = val.dup if val.frozen? && duplicable?(val) end end;T;&I"(def duplicate_frozen_values(target);T;'To; ; F; ;;;;I""Doing::Util#deep_merge_hashes;F;[[I"master_hash;T0[I"other_hash;T0;[[@fi?;T;:deep_merge_hashes;;;[;{;IC;"CNon-destructive version of deep_merge_hashes! See that method.;T;[o;) ;*I" return;F;+I"the merged hashes.;T;0;,0;!@o;) ;*I" param;F;+I"The master hash;T;I"master_hash;T;,[I" Hash;T;!@o;) ;*I" param;F;+I"The other hash;T;I"other_hash;T;,[I" Hash;T;!@;[;I"Non-destructive version of deep_merge_hashes! See that method. @return the merged hashes. @param [Hash] master_hash The master hash @param [Hash] other_hash The other hash ;T; 0;!@;4i;"F;5o;6;7F;8i7;9i>;$@_;:T;%I"kdef deep_merge_hashes(master_hash, other_hash) deep_merge_hashes!(master_hash.clone, other_hash) end;T;&I"3def deep_merge_hashes(master_hash, other_hash);T;'To; ; F; ;;;;I"#Doing::Util#deep_merge_hashes!;F;[[I" target;T0[I"overwrite;T0;[[@fiL;T;:deep_merge_hashes!;;;[;{;IC;"IMerges a master hash with another hash, recursively. master_hash - the "parent" hash whose values will be overridden other_hash - the other hash whose values will be persisted after the merge This code was lovingly stolen from some random gem: http://gemjack.com/gems/tartan-0.1.1/classes/Hash.html Thanks to whoever made it.;T;[;[;I"IMerges a master hash with another hash, recursively. master_hash - the "parent" hash whose values will be overridden other_hash - the other hash whose values will be persisted after the merge This code was lovingly stolen from some random gem: http://gemjack.com/gems/tartan-0.1.1/classes/Hash.html Thanks to whoever made it.;T; 0;!@;4i;"F;5o;6;7F;8iC;9iK;$@_;:T;%I"def deep_merge_hashes!(target, overwrite) merge_values(target, overwrite) merge_default_proc(target, overwrite) duplicate_frozen_values(target) target end;T;&I".def deep_merge_hashes!(target, overwrite);T;'To; ; F; ;;;;I"Doing::Util#duplicable?;F;[[I"obj;T0;[[@fiT;F;:duplicable?;;;[;{;IC;" ;T;[o;) ;*I" return;F;+@;0;,[@V;!@;[;@; 0;!@;4i;$@_;:T;%I"qdef duplicable?(obj) case obj when nil, false, true, Symbol, Numeric false else true end end;T;&I"def duplicable?(obj);T;'To; ; F; ;;;;I"Doing::Util#mergable?;F;[[I" value;T0;[[@fi];F;:mergable?;;;[;{;IC;" ;T;[o;) ;*I" return;F;+@;0;,[@V;!@;[;@; 0;!@;4i;$@_;:T;%I"1def mergable?(value) value.is_a?(Hash) end;T;&I"def mergable?(value);T;'To; ; F; ;;;;I"Doing::Util#merge_values;F;[[I" target;T0[I"overwrite;T0;[[@fia;F;:merge_values;;;[;{;IC;" ;T;[;[;@; 0;!@ ;4i;$@_;:T;%I"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;T;&I"(def merge_values(target, overwrite);T;'To; ; F; ;;;;I"Doing::Util#write_to_file;F;[[I" file;T0[I" content;T0[I" backup:;TI" true;T;[[@fit;T;:write_to_file;;;[;{;IC;"Write content to a file;T;[o;) ;*I" param;F;+I"(The path to the file to (over)write;T;I" file;T;,[I" String;T;!@o;) ;*I" param;F;+I"%The content to write to the file;T;I" content;T;,[I" String;T;!@o;) ;*I" param;F;+I"create a ~ backup;T;I" backup;T;,[I" Boolean;T;!@;[;I" Write content to a file @param file [String] The path to the file to (over)write @param content [String] The content to write to the file @param backup [Boolean] create a ~ backup ;T; 0;!@;4i;"T;5o;6;7F;8im;9is;$@_;:T;%I"def write_to_file(file, content, backup: true) unless file puts content return end Doing.logger.benchmark(:write_file, :start) file = File.expand_path(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;T;&I"3def write_to_file(file, content, backup: true);T;'To; ; F; ;;;;I"Doing::Util#safe_load_file;F;[[I" filename;T0;[[@fi;F;:safe_load_file;;;[;{;IC;" ;T;[;[;@; 0;!@B;4i;$@_;:T;%I"Jdef safe_load_file(filename) SafeYAML.load_file(filename) || {} end;T;&I"!def safe_load_file(filename);T;'To; ; F; ;;;;I"Doing::Util#default_editor;F;[;[[@fi;F;:default_editor;;;[;{;IC;" ;T;[;[;@; 0;!@P;4i;$@_;:T;%I"Edef default_editor @default_editor ||= find_default_editor end;T;&I"def default_editor;T;'To; ; F; ;;;;I"!Doing::Util#editor_with_args;F;[;[[@fi;F;:editor_with_args;;;[;{;IC;" ;T;[;[;@; 0;!@\;4i;$@_;:T;%I"?def editor_with_args args_for_editor(default_editor) end;T;&I"def editor_with_args;T;'To; ; F; ;;;;I" Doing::Util#args_for_editor;F;[[I" editor;T0;[[@fi;F;:args_for_editor;;;[;{;IC;" ;T;[;[;@; 0;!@h;4i;$@_;:T;%I"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;T;&I" def args_for_editor(editor);T;'To; ; F; ;;;;I"$Doing::Util#find_default_editor;F;[[I"editor_for;TI"'default';T;[[@fi;F;:find_default_editor;;;[;{;IC;" ;T;[;[;@; 0;!@v;4i;$@_;:T;%I"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.config.settings['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;T;&I"4def find_default_editor(editor_for = 'default');T;'To; ;IC;[o; ; F; ;;;;I"&Doing::Util::Backup#prune_backups;F;[[I" filename;T0[I" limit;TI"10;T;[[I"lib/doing/util_backup.rb;Ti;T;:prune_backups;;;[;{;IC;")Delete all but most recent 5 backups;T;[o;) ;*I" param;F;+I"(Maximum number of backups to retain;T;I" limit;T;,0;!@;[;I"c Delete all but most recent 5 backups @param limit Maximum number of backups to retain ;T; 0;!@;4i;"T;5o;6;7F;8i;9i;$@;:T;%I"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;T;&I",def prune_backups(filename, limit = 10);T;'To; ; F; ;;;;I"#Doing::Util::Backup#clear_redo;F;[[I" filename;T0;[[@i%;T;:clear_redo;;;[;{;IC;"Delete all redo files;T;[o;) ;*I" param;F;+I"(Maximum number of backups to retain;T;I" limit;T;,0;!@;[;I"T Delete all redo files @param limit Maximum number of backups to retain ;T; 0;!@;4i;"T;5o;6;7F;8i ;9i$;$@;:T;%I"def clear_redo(filename) filename ||= Doing.config.settings['doing_file'] backups = Dir.glob("undone*___#{File.basename(filename)}", base: backup_dir).sort.reverse backups.each do |file| FileUtils.rm(File.join(backup_dir, file)) end end;T;&I"def clear_redo(filename);T;'To; ; F; ;;;;I"$Doing::Util::Backup#last_backup;F;[[I" filename;TI"nil;T[I" count:;TI"1;T;[[@i3;T;:last_backup;;;[;{;IC;"$Retrieve the most recent backup;T;[o;) ;*I" param;F;+I"The filename;T;I" filename;T;,0;!@o;) ;*I" return;F;+I" filename;T;0;,[I" String;T;!@;[;I"h Retrieve the most recent backup @param filename The filename @return [String] filename ;T; 0;!@;4i;"T;5o;6;7F;8i-;9i2;$@;:T;%I"def last_backup(filename = nil, count: 1) filename ||= Doing.config.settings['doing_file'] backup = get_backups(filename).slice(count - 1) backup.nil? ? nil : File.join(backup_dir, backup) end;T;&I".def last_backup(filename = nil, count: 1);T;'To; ; F; ;;;;I",Doing::Util::Backup#restore_last_backup;F;[[I" filename;TI"nil;T[I" count:;TI"1;T;[[@iA;T;:restore_last_backup;;;[;{;IC;"kRestore the most recent backup. If a filename is provided, only backups of that filename will be used.;T;[o;) ;*I" param;F;+I"7The filename to restore, if different from default;T;I" filename;T;,0;!@o;) ;*I" raise;F;+@;0;,[I"DoingRuntimeError;T;!@;[;I" Restore the most recent backup. If a filename is provided, only backups of that filename will be used. @param filename The filename to restore, if different from default ;T; 0;!@;4i;"T;5o;6;7F;8i:;9i@;$@;:T;%I"def restore_last_backup(filename = nil, count: 1) Doing.logger.benchmark(:restore_backup, :start) filename ||= Doing.config.settings['doing_file'] backup_file = last_backup(filename, count: count) raise DoingRuntimeError, 'End of undo history' if backup_file.nil? save_undone(filename) FileUtils.mv(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;T;&I"6def restore_last_backup(filename = nil, count: 1);T;'To; ; F; ;;;;I"$Doing::Util::Backup#redo_backup;F;[[I" filename;TI"nil;T[I" count:;TI"1;T;[[@iT;T;:redo_backup;;;[;{;IC;"Undo last undo;T;[o;) ;*I" param;F;+I"The filename;T;I" filename;T;,0;!@o;) ;*I" raise;F;+@;0;,[I"DoingRuntimeError;T;!@;[;I"9 Undo last undo @param filename The filename ;T; 0;!@;4i;"T;5o;6;7F;8iO;9iS;$@;:T;%I"#def redo_backup(filename = nil, count: 1) filename ||= Doing.config.settings['doing_file'] # redo_file = File.join(backup_dir, "undone___#{File.basename(filename)}") 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 DoingRuntimeError, 'End of redo history' if undone.nil? redo_file = File.join(backup_dir, undone) FileUtils.move(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;T;&I".def redo_backup(filename = nil, count: 1);T;'To; ; F; ;;;;I"%Doing::Util::Backup#clear_undone;F;[[I" filename;TI"nil;T;[[@il;F;:clear_undone;;;[;{;IC;" ;T;[;[;@; 0;!@;4i;$@;:T;%I"#def clear_undone(filename = nil) filename ||= Doing.config.settings['doing_file'] # redo_file = File.join(backup_dir, "undone___#{File.basename(filename)}") Dir.glob("undone*#{File.basename(filename)}", base: backup_dir).each do |f| FileUtils.rm(File.join(backup_dir, f)) end end;T;&I"%def clear_undone(filename = nil);T;'To; ; F; ;;;;I"$Doing::Util::Backup#select_redo;F;[[I" filename;TI"nil;T;[[@iz;T;:select_redo;;;[;{;IC;"eSelect from recent undos. If a filename is provided, only backups of that filename will be used.;T;[o;) ;*I" param;F;+I"The filename to restore;T;I" filename;T;,0;!@o;) ;*I" raise;F;+@;0;,[I"DoingRuntimeError;T;!@;[;I" Select from recent undos. If a filename is provided, only backups of that filename will be used. @param filename The filename to restore ;T; 0;!@;4i;"T;5o;6;7F;8it;9iy;$@;:T;%I"def select_redo(filename = nil) filename ||= Doing.config.settings['doing_file'] undones = Dir.glob("undone*#{File.basename(filename)}", base: backup_dir).sort raise DoingRuntimeError, 'End of redo history' if undones.empty? total = undones.count options = 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 DoingRuntimeError, 'No backup files to load' if options.empty? backup_file = show_menu(options, 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) FileUtils.move(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;T;&I"$def select_redo(filename = nil);T;'To; ; F; ;;;;I"&Doing::Util::Backup#select_backup;F;[[I" filename;TI"nil;T;[[@i;T;:select_backup;;;[;{;IC;"gSelect from recent backups. If a filename is provided, only backups of that filename will be used.;T;[o;) ;*I" param;F;+I"The filename to restore;T;I" filename;T;,0;!@0o;) ;*I" raise;F;+@;0;,[I"DoingRuntimeError;T;!@0;[;I" Select from recent backups. If a filename is provided, only backups of that filename will be used. @param filename The filename to restore ;T; 0;!@0;4i;"T;5o;6;7F;8i;9i;$@;:T;%I"def select_backup(filename = nil) filename ||= Doing.config.settings['doing_file'] options = 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 DoingRuntimeError, 'No backup files to load' if options.empty? backup_file = show_menu(options, filename) Util.write_to_file(File.join(backup_dir, "undone___#{File.basename(filename)}"), IO.read(filename), backup: false) FileUtils.mv(backup_file, filename) prune_backups_after(File.basename(backup_file)) Doing.logger.warn('File update:', "restored from #{backup_file}") end;T;&I"&def select_backup(filename = nil);T;'To; ; F; ;;;;I""Doing::Util::Backup#show_menu;F;[[I" options;T0[I" filename;T0;[[@i;F;:show_menu;;;[;{;IC;" ;T;[o;) ;*I" raise;F;+@;0;,[I"UserCancelled;T;!@I;[;@; 0;!@I;4i;$@;:T;%I"odef show_menu(options, filename) if TTY::Which.which('colordiff') preview = 'colordiff -U 1' pipe = '| awk "(NR>2)"' elsif TTY::Which.which('git') preview = 'git --no-pager diff -U1 --color=always --minimal --word-diff' pipe = ' | awk "(NR>4)"' else preview = 'diff -U 1' pipe = if TTY::Which.which('delta') ' | delta --no-gitconfig --syntax-theme=1337' elsif TTY::Which.which('diff-so-fancy') ' | diff-so-fancy' elsif TTY::Which.which('ydiff') ' | ydiff -c always --wrap < /dev/tty' else cmd = 'sed -e "s/^-/`echo -e "\033[31m"`-/;s/^+/`echo -e "\033[32m"`+/;s/^@/`echo -e "\033[34m"`@/;s/\$/`echo -e "\033[0m"`/"' "| bash -c #{Shellwords.escape(cmd)}" end pipe += ' | awk "(NR>2)"' end result = Doing::Prompt.choose_from(options, prompt: 'Select a backup to restore', sorted: false, fzf_args: [ '--delimiter="\t"', '--with-nth=1', %(--preview='#{preview} "#{filename}" {2} #{pipe}'), '--disabled', '--height=10', '--preview-window="right,70%,nowrap,follow"', '--header="Select a revision to restore"' ]) raise UserCancelled unless result result.strip.split(/\t/).last end;T;&I"%def show_menu(options, filename);T;'To; ; F; ;;;;I"%Doing::Util::Backup#write_backup;F;[[I" filename;TI"nil;T;[[@i;T;:write_backup;;;[;{;IC;"NWrites a copy of the content to a dated backup file in a hidden directory;T;[o;) ;*I" param;F;+I"The data to back up;T;I" content;T;,0;!@];[;I"z Writes a copy of the content to a dated backup file in a hidden directory @param content The data to back up ;T; 0;!@];4i;"T;5o;6;7F;8i;9i;$@;:T;%I"def write_backup(filename = nil) Doing.logger.benchmark(:_write_backup, :start) filename ||= Doing.config.settings['doing_file'] unless File.exist?(filename) Doing.logger.debug('Backup:', "original file doesn't exist (#{filename})") return end backup_file = File.join(backup_dir, "#{timestamp_filename}___#{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.config.settings['history_size'].to_i) clear_undone(filename) Doing.logger.benchmark(:_write_backup, :finish) end;T;&I"%def write_backup(filename = nil);T;'To; ; F; ;;;W;I"+Doing::Util::Backup#timestamp_filename;F;[;[[@i;F;:timestamp_filename;;;[;{;IC;" ;T;[;[;@; 0;!@r;4i;$@;:T;%I"Hdef timestamp_filename Time.now.strftime('%Y-%m-%d_%H.%M.%S') end;T;&I"def timestamp_filename;T;'To; ; F; ;;;W;I"$Doing::Util::Backup#get_backups;F;[[I" filename;TI"nil;T[I"include_forward:;TI" false;T;[[@i;F;:get_backups;;;[;{;IC;" ;T;[;[;@; 0;!@~;4i;$@;:T;%I"def get_backups(filename = nil, include_forward: false) filename ||= Doing.config.settings['doing_file'] backups = Dir.glob("*___#{File.basename(filename)}", base: backup_dir).sort.reverse backups.delete_if { |f| f =~ /^undone/ } unless include_forward end;T;&I"\d{4}-\d{2}-\d{2})_(?