{x:	rooto:"YARD::CodeObjects::RootObject:@childrenIC:&YARD::CodeObjects::CodeObjectList[
o:$YARD::CodeObjects::ModuleObject;IC;[o:#YARD::CodeObjects::ClassObject;IC;[$o:$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#id;F;[�;[[@iD[@i;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;!@�:@ref_tag_recurse_counti�;"F:@line_rangeo:
Range:	exclF:
beginiA:endiC;$@
:@explicitT;%I"def id
  @id
end;T;&I"def id;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;!@�;4i�;"T;5o;6;7F;8i;9i;$@
;:T;%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;[�;[[@i4;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;8i.;9i3;$@
;: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;8i8;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#equal?;F;[[I"
other;T0;[[@iO;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;8iH;9iN;$@
;: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;[[@i`;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;8iY;9i_;$@
;: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;[[@il;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;!@5o;)
;*I"return;F;+I"overlaps?;T;0;,[I"Boolean;T;!@5;[�;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;!@5;4i�;"T;5o;6;7F;8id;9ik;$@
;:T;%I"�def overlapping_time?(item_b)
  return true if same_time?(item_b)

  start_a = date
  interval = interval
  end_a = interval ? start_a + interval.to_i : start_a
  start_b = item_b.date
  interval = item_b.interval
  end_b = interval ? start_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#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;!@Po;)
;*I"
param;F;+I"Additional options;T;I"**options;T;,0;!@Po: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;!@Po;C;*I"option;F;+0;I"options;T;,0;Do;E
;*I"option;F;+I"Log as a single change?;T;I":single;T;,[I"Boolean;T;F0;!@Po;C;*I"option;F;+0;I"options;T;,0;Do;E
;*I"option;F;+I"&A value to include as @tag(value);T;I":value;T;,[I"String;T;F0;!@Po;C;*I"option;F;+0;I"options;T;,0;Do;E
;*I"option;F;+I"%if true remove instead of adding;T;I":remove;T;,[I"Boolean;T;F0;!@Po;C;*I"option;F;+0;I"options;T;,0;Do;E
;*I"option;F;+I"3if not nil, rename target tag to this tag name;T;I":rename_to;T;,[I"String;T;F0;!@Po;C;*I"option;F;+0;I"options;T;,0;Do;E
;*I"option;F;+I"-treat target tag string as regex pattern;T;I":regex;T;,[I"Boolean;T;F0;!@Po;C;*I"option;F;+0;I"options;T;,0;Do;E
;*I"option;F;+I"0with rename_to, add tag if it doesn't exist;T;I":force;T;,[I"Boolean;T;F0;!@Po;)
;*I"
param;F;+I""a customizable set of options;T;@i;,[I"	Hash;T;!@P;[�;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;!@P;4i�;"T;5o;6;7F;8ix;9i�;$@
;:T;%I"zdef 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.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#tags?;F;[[I"	tags;T0[I"	bool;TI"	:and;T[I"negate:;TI"
false;T;[[@i�;T;:
tags?;;;[�;{�;IC;"!Test if item contains tag(s);T;[	o;)
;*I"
param;F;+I"CThe tags to test. Can be an array or a comma-separated string.;T;I"	tags;T;,[I"Array or String;T;!@�o;)
;*I"
param;F;+I";The boolean to use for multiple tags (:and, :or, :not);T;I"	bool;T;,[I"Symbol;T;!@�o;)
;*I"
param;F;+I"negate the result?;T;I"negate;T;,[I"Boolean;T;!@�o;)
;*I"return;F;+I"(true if tag/bool combination passes;T;0;,[I"Boolean;T;!@�;[�;I"D
Test if item contains tag(s)

@param      tags    (Array or String) The tags to test. Can be an array or a comma-separated string.
@param      bool    (Symbol) The boolean to use for multiple tags (:and, :or, :not)
@param      negate  [Boolean] negate the result?

@return     [Boolean] true if tag/bool combination passes
;T; 0;!@�;4i�;"T;5o;6;7F;8i�;9i�;$@
;:T;%I"Adef tags?(tags, bool = :and, negate: false)
  tags = split_tags(tags)
  bool = bool.normalize_bool

  matches = case bool
            when :and
              all_tags?(tags)
            when :not
              no_tags?(tags)
            else
              any_tags?(tags)
            end
  negate ? !matches : matches
end;T;&I"0def tags?(tags, bool = :and, negate: false);T;'To;;F;
;;;;I"Doing::Item#search;F;[
[I"search;T0[I"distance:;TI"3;T[I"negate:;TI"
false;T[I"case_type:;TI":smart;T[I"fuzzy:;TI"
false;T;[[@i�;T;:search;;;[�;{�;IC;"'Test if item matches search string;T;[	o;)
;*I"
param;F;+I"The search string;T;I"search;T;,[I"String;T;!@�o;)
;*I"
param;F;+I"negate results;T;I"negate;T;,[I"Boolean;T;!@�o;)
;*I"
param;F;+I"<The case-sensitivity
type (:sensitive,
:ignore, :smart);T;I"case_type;T;,[I"Symbol;T;!@�o;)
;*I"return;F;+I"matches search criteria;T;0;,[I"Boolean;T;!@�;[�;I"<
Test if item matches search string

@param      search     [String] The search string
@param      negate     [Boolean] negate results
@param      case_type  (Symbol) The case-sensitivity
                       type (:sensitive,
                       :ignore, :smart)

@return     [Boolean] matches search criteria
;T; 0;!@�;4i�;"T;5o;6;7F;8i�;9i�;$@
;:T;%I"def search(search, distance: 3, negate: false, case_type: :smart, fuzzy: false)
  text = @title + @note.to_s
  matches = text =~ search.to_rx(distance: distance, case_type: case_type)

  # if search.is_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;T;&I"Tdef search(search, distance: 3, negate: false, case_type: :smart, fuzzy: false);T;'To;;F;
;;;;I"Doing::Item#should_finish?;F;[�;[[@i�;F;:should_finish?;;;[�;{�;IC;"�;T;[o;)
;*I"return;F;+I"�;T;0;,[I"Boolean;T;!@#;[�;@.; 0;!@#;4i�;$@
;:T;%I"5def should_finish?
  should?('never_finish')
end;T;&I"def should_finish?;T;'To;;F;
;;;;I"Doing::Item#should_time?;F;[�;[[@i�;F;:should_time?;;;[�;{�;IC;"�;T;[o;)
;*I"return;F;+@.;0;,[@0;!@4;[�;@.; 0;!@4;4i�;$@
;: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;!@Co;)
;*I"
param;F;+I"$add @from(original
section) tag;T;I"
label;T;,[I"Boolean;T;!@Co;)
;*I"
param;F;+I"log this action;T;I"log;T;,[I"Boolean;T;!@Co;)
;*I"return;F;+I"nothing;T;0;,0;!@C;[�;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;!@C;4i�;"T;5o;6;7F;8i�;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.truncate(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;!@n;4i�;"F;5o;6;7F;8i�;9i�;$@
;:T;%I"ldef to_s
  "\t- #{@date.strftime('%Y-%m-%d %H:%M')} | #{@title}#{@note.empty? ? '' : "\n#{@note}"}"
end;T;&I"
def to_s;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
  # %(<Doing::Item @date=#{@date} @title="#{@title}" @section:"#{@section}" @note:#{@note.to_s}>)
  %(<Doing::Item @date=#{@date}>)
end;T;&I"def inspect;T;'To;;F;
;;:private;I"Doing::Item#should?;F;[[I"key;T0;[[@i;F;:should?;;;[�;{�;IC;"�;T;[o;)
;*I"return;F;+@.;0;,[@0;!@�;[�;@.; 0;!@�;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;
;;;O;I"Doing::Item#calc_interval;F;[�;[[@i;F;:calc_interval;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@
;:T;%I"def calc_interval
  done = end_date
  return nil if done.nil?

  start = @date

  t = (done - start).to_i
  t > 0 ? t : nil
end;T;&I"def calc_interval;T;'To;;F;
;;;O;I"Doing::Item#all_tags?;F;[[I"	tags;T0;[[@i$;F;:all_tags?;;;[�;{�;IC;"�;T;[o;)
;*I"return;F;+@.;0;,[@0;!@�;[�;@.; 0;!@�;4i�;$@
;:T;%I"jdef all_tags?(tags)
  tags.each do |tag|
    return false unless @title =~ /@#{tag}/
  end
  true
end;T;&I"def all_tags?(tags);T;'To;;F;
;;;O;I"Doing::Item#no_tags?;F;[[I"	tags;T0;[[@i+;F;:
no_tags?;;;[�;{�;IC;"�;T;[o;)
;*I"return;F;+@.;0;,[@0;!@�;[�;@.; 0;!@�;4i�;$@
;:T;%I"edef no_tags?(tags)
  tags.each do |tag|
    return false if @title =~ /@#{tag}/
  end
  true
end;T;&I"def no_tags?(tags);T;'To;;F;
;;;O;I"Doing::Item#any_tags?;F;[[I"	tags;T0;[[@i2;F;:any_tags?;;;[�;{�;IC;"�;T;[o;)
;*I"return;F;+@.;0;,[@0;!@�;[�;@.; 0;!@�;4i�;$@
;:T;%I"fdef any_tags?(tags)
  tags.each do |tag|
    return true if @title =~ /@#{tag}/
  end
  false
end;T;&I"def any_tags?(tags);T;'To;;F;
;;;O;I"Doing::Item#split_tags;F;[[I"	tags;T0;[[@i9;F;:split_tags;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@
;:T;%I"xdef split_tags(tags)
  tags = tags.split(/ *, */) if tags.is_a? String
  tags.map { |t| t.strip.sub(/^@/, '') }
end;T;&I"def split_tags(tags);T;'T:@owner@
:@class_mixinsIC;[�;V@
:@instance_mixinsIC;[�;V@
:@attributesIC:SymbolHash{:
classIC;Z{�:@symbolize_valueT;IC;Z{
;IC;Z{:	read@:
write@;\T;-IC;Z{;]@-;^@:;\T;/IC;Z{;]@L;^@Y;\T;1IC;Z{;]@k;^@x;\T;3IC;Z{;]@�;^0;\T;\T;\T:
@aliases{�:@groups[�;[[@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:YARD::CodeObjects::Proxy:@orignamespace0:@origname0:
@imethod0;:Object;$@:	@obj0:
@type;[;'To;
;IC;[o;;F;
;;;;I"Doing::Note#initialize;F;[[I"	note;TI"[];T;[[I"lib/doing/note.rb;Ti;T;;;;;;[�;{�;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;!@o;)
;*I"return;F;+I"a new instance of Note;T;0;,[I"	Note;F;!@;[�;I"o
Initializes a new note

@param      note  [Array] Initial note, can be string
                  or array
;T; 0;!@;4i�;"T;5o;6;7F;8i;9i;$@;: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;[[@i";T;:add;;;[�;{�;IC;":Add note contents, optionally replacing existing note;T;[o;)
;*I"
param;F;+I"3The note to add, can be
string or array (Note);T;I"	note;T;,[I"
Array;T;!@!o;)
;*I"
param;F;+I"replace existing
content;T;I"replace;T;,[I"Boolean;T;!@!;[�;I"�
Add note contents, optionally replacing existing note

@param      note     [Array] The note to add, can be
                     string or array (Note)
@param      replace  [Boolean] replace existing
                     content
;T; 0;!@!;4i�;"T;5o;6;7F;8i;9i!;$@;: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#append;F;[[I"
lines;T0;[[@i0;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+;9i/;$@;:T;%I"=def append(lines)
  concat(lines)
  replace compress
end;T;&I"def append(lines);T;'To;;F;
;;;;I"Doing::Note#append_string;F;[[I"
input;T0;[[@i;;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;!@V;[�;I"~
Append a string to the note content

@param      input  [String] The input string,
                   newlines will be split
;T; 0;!@V;4i�;"T;5o;6;7F;8i5;9i:;$@;:T;%I"]def append_string(input)
  concat(input.split(/\n/).map(&:strip))
  replace compress
end;T;&I"def append_string(input);T;'To;;F;
;;;;I"Doing::Note#compress!;F;[�;[[@i@;F;:compress!;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@l;4i�;$@;:T;%I")def compress!
  replace compress
end;T;&I"def compress!;T;'To;;F;
;;;;I"Doing::Note#compress;F;[�;[[@iI;T;:
compress;;;[�;{�;IC;"-Remove blank lines and comment lines (#);T;[o;)
;*I"return;F;+I"compressed array;T;0;,[I"
Array;T;!@x;[�;I"U
Remove blank lines and comment lines (#)

@return     [Array] compressed array
;T; 0;!@x;4i�;"T;5o;6;7F;8iD;9iH;$@;:T;%I"Cdef compress
  delete_if { |l| l =~ /^\s*$/ || l =~ /^#/ }
end;T;&I"def compress;T;'To;;F;
;;;;I"Doing::Note#strip_lines!;F;[�;[[@iM;F;:strip_lines!;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@;:T;%I"/def strip_lines!
  replace strip_lines
end;T;&I"def strip_lines!;T;'To;;F;
;;;;I"Doing::Note#strip_lines;F;[�;[[@iW;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;8iQ;9iV;$@;:T;%I"'def strip_lines
  map(&:strip)
end;T;&I"def strip_lines;T;'To;;F;
;;;;I"Doing::Note#to_s;F;[�;[[@i];T;;M;;;[�;{�;IC;"Note as multi-line string;T;[�;[�;I"
Note as multi-line string;T; 0;!@�;4i�;"T;5o;6;7F;8i[;9i\;$@;: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;[�;[[@ib;T;;N;;;[�;{�;IC;"�;T;[o;)
;*I"private;F;+I"�;T;0;,0;!@�;[�;I"
@private;T; 0;!@�;4i�;"F;5o;6;7F;8ia;9ia;$@;:T;%I"kdef inspect
  "<Doing::Note - characters:#{compress.strip_lines.join(' ').length} lines:#{count}>"
end;T;&I"def inspect;T;'To;;F;
;;;;I"Doing::Note#equal?;F;[[I"
other;T0;[[@in;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;8if;9im;$@;:T;%I"Xdef equal?(other)
  return false unless other.is_a?(Note)

  to_s == other.to_s
end;T;&I"def equal?(other);T;'T;V@;WIC;[�;V@;XIC;[�;V@;YIC;Z{;[IC;Z{�;\T;IC;Z{�;\T;\T;_{�;`[�;[[@i;T;:	Note;;;;;[�;{�;IC;"'This class describes an item note.;T;[�;[�;I")
This class describes an item note.
;T; 0;!@;4i�;"T;5o;6;7F;8i	;9i;$@;I"Doing::Note;F;bo;c;d0;e0;f0;:
Array;$@;ho;
;IC;[
o;;F;
;;;;I"Array#to_tags;F;[�;[[I"lib/doing/array.rb;Ti;T;:to_tags;;;[�;{�;IC;"Convert strings to @tags;T;[o;)
;*I"example;F;+I"�;F;I"'`['one', '@two', 'three'].to_tags`;T;,0;!@�o;)
;*I"example;F;+I"�;F;I"$`=> ['@one', '@two', '@three']`;T;,0;!@�o;)
;*I"return;F;+I"Array of @tags;T;0;,[I"
Array;T;!@�;[�;I"�Convert strings to @tags

@example `['one', '@two', 'three'].to_tags`
@example `=> ['@one', '@two', '@three']`
@return     [Array] Array of @tags
;T; 0;!@�;4i�;"F;5o;6;7F;8i
;9i;$@�;:T;%I"4def to_tags
  map { |t| t.sub(/^@?/, '@') }
end;T;&I"def to_tags;T;'To;;F;
;;;;I"Array#to_tags!;F;[�;[[@�i;F;:
to_tags!;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@;4i�;$@�;:T;%I"'def to_tags!
  replace to_tags
end;T;&I"def to_tags!;T;'To;;F;
;;;;I"Array#highlight_tags;F;[[I"
color;TI"'cyan';T;[[@�i#;T;:highlight_tags;;;[�;{�;IC;"2Hightlight @tags in string for console output;T;[o;)
;*I"
param;F;+I" the color to highlight
with;T;I"
color;T;,[I"String;T;!@ o;)
;*I"return;F;+I""string with @tags highlighted;T;0;,[I"String;T;!@ ;[�;I"�
Hightlight @tags in string for console output

@param      color  [String] the color to highlight
                   with

@return     [String] string with @tags highlighted
;T; 0;!@ ;4i�;"T;5o;6;7F;8i;9i";$@�;:T;%I"{def highlight_tags(color = 'cyan')
  tag_color = Doing::Color.send(color)
  to_tags.map { |t| "#{tag_color}#{t}" }
end;T;&I"'def highlight_tags(color = 'cyan');T;'To;;F;
;;;;I"Array#log_tags;F;[�;[[@�i-;T;:
log_tags;;;[�;{�;IC;"Tag array for logging;T;[o;)
;*I"return;F;+I",Highlighted tag array joined with comma;T;0;,[I"String;T;!@<;[�;I"Z
Tag array for logging

@return     [String] Highlighted tag array joined with comma
;T; 0;!@<;4i�;"T;5o;6;7F;8i(;9i,;$@�;:T;%I"1def log_tags
  highlight_tags.join(', ')
end;T;&I"def log_tags;T;'To;;F;
;;;;I"Array#nested_hash;F;[[I"
value;T0;[[@�i6;T;:nested_hash;;;[�;{�;IC;"<Convert array to nested hash, setting last key to value;T;[o;)
;*I"
param;F;+I"The value to set;T;I"
value;T;,0;!@Oo;)
;*I"
raise;F;+@.;0;,[I"StandardError;T;!@O;[�;I"c
Convert array to nested hash, setting last key to value

@param      value  The value to set
;T; 0;!@O;4i�;"T;5o;6;7F;8i1;9i5;$@�;:T;%I"�def nested_hash(value)
  raise StandardError, 'Value can not be nil' if value.nil?

  hsh = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }
  hsh.dig(*self[0..-2])[self.fetch(-1)] = value
  hsh
end;T;&I"def nested_hash(value);T;'T;V@�;WIC;[�;V@�;XIC;[�;V@�;YIC;Z{;[IC;Z{�;\T;IC;Z{�;\T;\T;_{�;`[�;[[@�i;T;;r;;;;;[�;{�;IC;"Array helpers;T;[�;[�;I"
Array helpers
;T; 0;!@�;4i�;"T;5o;6;7F;8i	;9i;$@;I"
Array;F;bo;c;d0;e0;f0;;g;$@;h0;i;[;'T;i;[;'To;	;IC;[o;;F;
;;;;I"Doing::Util#user_home;F;[�;[[I"lib/doing/util.rb;Ti
;F;:user_home;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@{;4i�;$@y;: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;[[@�i;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;!@�;[�;I"e
Test if command line tool is available

@param      cli   [String] The name or path of the cli
;T; 0;!@�;4i�;"T;5o;6;7F;8i;9i;$@y;:T;%I"Zdef exec_available(cli)
  return false if cli.nil?

  !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;[[@�i%;T;:first_available_exec;;;[�;{�;IC;">Return the first valid executable from a list of commands;T;[o;)
;*I"example;F;+I"�;F;I"L`Doing::Util.first_available_exec('bat', 'less -Xr', 'more -r', 'cat')`;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$;$@y;: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;[[@�i*;F;:merge_default_proc;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@y;: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;[[@�i0;F;:duplicate_frozen_values;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@y;: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;[[@�i>;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;8i6;9i=;$@y;:T;%I"idef deep_merge_hashes(master_hash, other_hash)
  deep_merge_hashes!(master_hash.dup, 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;[[@�iK;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;8iB;9iJ;$@y;: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;[[@�iS;F;:duplicable?;;;[�;{�;IC;"�;T;[o;)
;*I"return;F;+@.;0;,[@0;!@;[�;@.; 0;!@;4i�;$@y;: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;[[@�i\;F;:mergable?;;;[�;{�;IC;"�;T;[o;)
;*I"return;F;+@.;0;,[@0;!@;[�;@.; 0;!@;4i�;$@y;: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;[[@�i`;F;:merge_values;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@%;4i�;$@y;: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;[[@�is;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;!@5o;)
;*I"
param;F;+I"%The content to write to the file;T;I"content;T;,[I"String;T;!@5o;)
;*I"
param;F;+I"create a ~ backup;T;I"backup;T;,[I"Boolean;T;!@5;[�;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;!@5;4i�;"T;5o;6;7F;8il;9ir;$@y;:T;%I"�def write_to_file(file, content, backup: true)
  unless file
    puts content
    return
  end

  file = File.expand_path(file)

  if File.exist?(file) && backup
    # Create a backup copy for the undo command
    FileUtils.cp(file, "#{file}~")
  end

  File.open(file, 'w+') do |f|
    f.puts content
    Doing.logger.debug('Write:', "File written: #{file}")
  end

  Hooks.trigger :post_write, file
end;T;&I"3def write_to_file(file, content, backup: true);T;'To;;F;
;;;;I"Doing::Util#safe_load_file;F;[[I"
filename;T0;[[@�i�;F;:safe_load_file;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@\;4i�;$@y;: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;[�;[[@�i�;F;:default_editor;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@j;4i�;$@y;:T;%I"Cdef default_editor
  @default_editor = find_default_editor
end;T;&I"def default_editor;T;'To;;F;
;;;;I"!Doing::Util#editor_with_args;F;[�;[[@�i�;F;:editor_with_args;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@v;4i�;$@y;: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;[[@�i�;F;:args_for_editor;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@y;: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;[[@�i�;F;:find_default_editor;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@y;:T;%I"�def find_default_editor(editor_for = 'default')
  # return nil unless $stdout.isatty || ENV['DOING_EDITOR_TEST']

  if ENV['DOING_EDITOR_TEST']
    return ENV['EDITOR']
  end

  editor_config = Doing.config.settings['editors']

  if editor_config.is_a?(String)
    Doing.logger.warn('Deprecated:', "Please update your configuration, 'editors' should be a mapping. Delete the key and run `doing config --update`.")
    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}'")
    return editor unless editor.nil? || editor.empty?
  end

  if editor_for != 'editor' && editor_config['default']
    editor = editor_config['default']
    # Doing.logger.debug('Editor:', "Using #{editor} from config: 'editors->default'")
    return editor unless editor.nil? || editor.empty?
  end

  editor ||= ENV['DOING_EDITOR'] || ENV['GIT_EDITOR'] || ENV['EDITOR']

  unless editor.nil? || editor.empty?
    # Doing.logger.debug('Editor:', "Found editor in environment variables: #{editor}")
    return editor
  end

  Doing.logger.debug('ENV:', 'No EDITOR environment variable, testing available editors')
  editors = %w[vim vi code subl mate mvim nano emacs]
  editors.each do |ed|
    return TTY::Which.which(ed) if TTY::Which.which(ed)

    Doing.logger.debug('ENV:', "#{ed} not available")
  end

  nil
end;T;&I"4def find_default_editor(editor_for = 'default');T;'T;V@y;WIC;[@y;V@y;XIC;[�;V@y;YIC;Z{;[IC;Z{�;\T;IC;Z{�;\T;\T;_{�;`[�;[[@�i
;T;:	Util;;;;;[�;{�;IC;"Utilities;T;[�;[�;I"Utilities;T; 0;!@y;4i�;"F;5o;6;7F;8i	;9i	;$@;I"Doing::Util;F;'To;
;IC;[Co;;F;
;;;;I"#Doing::WWID#additional_configs;F;[�;[[I"lib/doing/wwid.rb;Ti;F;:additional_configs;;;[�;{�;IC;"7Returns the value of attribute additional_configs.
;T;[�;[�;I"7Returns the value of attribute additional_configs.;T; 0;!@�;"F;#0;$@�;%I"5def additional_configs
  @additional_configs
end;T;&I"def additional_configs;T;'To;;F;
;;;;I" Doing::WWID#current_section;F;[�;[[@�i;F;:current_section;;;[�;{�;IC;"4Returns the value of attribute current_section.
;T;[�;[�;I"4Returns the value of attribute current_section.;T; 0;!@�;"F;#0;$@�;%I"/def current_section
  @current_section
end;T;&I"def current_section;T;'To;;F;
;;;;I"Doing::WWID#doing_file;F;[�;[[@�i;F;:doing_file;;;[�;{�;IC;"/Returns the value of attribute doing_file.
;T;[�;[�;I"/Returns the value of attribute doing_file.;T; 0;!@�;"F;#0;$@�;%I"%def doing_file
  @doing_file
end;T;&I"def doing_file;T;'To;;F;
;;;;I"Doing::WWID#content;F;[�;[[@�i;F;:content;;;[�;{�;IC;",Returns the value of attribute content.
;T;[�;[�;I",Returns the value of attribute content.;T; 0;!@�;"F;#0;$@�;%I"def content
  @content
end;T;&I"def content;T;'To;;F;
;;;;I"Doing::WWID#config;F;[�;[[@�i;F;:config;;;[�;{�;IC;"+Returns the value of attribute config.
;T;[�;[�;I"+Returns the value of attribute config.;T; 0;!@�;"F;#0;$@�;%I"def config
  @config
end;T;&I"def config;T;'To;;F;
;;;;I"Doing::WWID#config=;F;[[@0;[[@�i;F;:config=;;;[�;{�;IC;"Sets the attribute config
;T;[o;)
;*I"
param;F;+I".the value to set the attribute config to.;T;I"
value;T;,0;!@�;[�;I"USets the attribute config
@param value the value to set the attribute config to.;T; 0;!@�;"F;#0;$@�;%I"-def config=(value)
  @config = value
end;T;&I"def config=(value);T;'To;;F;
;;;;I"Doing::WWID#config_file;F;[�;[[@�i;F;:config_file;;;[�;{�;IC;"0Returns the value of attribute config_file.
;T;[�;[�;I"0Returns the value of attribute config_file.;T; 0;!@;"F;#0;$@�;%I"'def config_file
  @config_file
end;T;&I"def config_file;T;'To;;F;
;;;;I"Doing::WWID#config_file=;F;[[@0;[[@�i;F;:config_file=;;;[�;{�;IC;"#Sets the attribute config_file
;T;[o;)
;*I"
param;F;+I"3the value to set the attribute config_file to.;T;I"
value;T;,0;!@;[�;I"_Sets the attribute config_file
@param value the value to set the attribute config_file to.;T; 0;!@;"F;#0;$@�;%I"7def config_file=(value)
  @config_file = value
end;T;&I"def config_file=(value);T;'To;;F;
;;;;I"Doing::WWID#auto_tag;F;[�;[[@�i;F;:
auto_tag;;;[�;{�;IC;"-Returns the value of attribute auto_tag.
;T;[�;[�;I"-Returns the value of attribute auto_tag.;T; 0;!@%;"F;#0;$@�;%I"!def auto_tag
  @auto_tag
end;T;&I"def auto_tag;T;'To;;F;
;;;;I"Doing::WWID#auto_tag=;F;[[@0;[[@�i;F;:auto_tag=;;;[�;{�;IC;" Sets the attribute auto_tag
;T;[o;)
;*I"
param;F;+I"0the value to set the attribute auto_tag to.;T;I"
value;T;,0;!@2;[�;I"YSets the attribute auto_tag
@param value the value to set the attribute auto_tag to.;T; 0;!@2;"F;#0;$@�;%I"1def auto_tag=(value)
  @auto_tag = value
end;T;&I"def auto_tag=(value);T;'To;;F;
;;;;I"Doing::WWID#default_option;F;[�;[[@�i;F;:default_option;;;[�;{�;IC;"3Returns the value of attribute default_option.
;T;[�;[�;I"3Returns the value of attribute default_option.;T; 0;!@D;"F;#0;$@�;%I"-def default_option
  @default_option
end;T;&I"def default_option;T;'To;;F;
;;;;I" Doing::WWID#default_option=;F;[[@0;[[@�i;F;:default_option=;;;[�;{�;IC;"&Sets the attribute default_option
;T;[o;)
;*I"
param;F;+I"6the value to set the attribute default_option to.;T;I"
value;T;,0;!@Q;[�;I"eSets the attribute default_option
@param value the value to set the attribute default_option to.;T; 0;!@Q;"F;#0;$@�;%I"=def default_option=(value)
  @default_option = value
end;T;&I"def default_option=(value);T;'To;;F;
;;;;I"Doing::WWID#initialize;F;[�;[[@�i;T;;;;;;[�;{�;IC;"Initializes the object.;T;[o;)
;*I"return;F;+I"a new instance of WWID;T;0;,[I"	WWID;F;!@c;[�;I"
Initializes the object.
;T; 0;!@c;4i�;"T;5o;6;7F;8i;9i;$@�;:T;%I"gdef initialize
  @timers = {}
  @recorded_items = []
  @content = Items.new
  @auto_tag = true
end;T;&I"def initialize;T;'To;;F;
;;;;I"Doing::WWID#logger;F;[�;[[@�i.;T;:logger;;;[�;{�;IC;"�Logger

Responds to :debug, :info, :warn, and :error

Each method takes a topic, and a message or block

Example: debug('Hooks', 'Hook 1 triggered');T;[�;[�;I"�
Logger

Responds to :debug, :info, :warn, and :error

Each method takes a topic, and a message or block

Example: debug('Hooks', 'Hook 1 triggered')
;T; 0;!@v;4i�;"T;5o;6;7F;8i%;9i-;$@�;:T;%I".def logger
  @logger ||= Doing.logger
end;T;&I"def logger;T;'To;;F;
;;;;I" Doing::WWID#init_doing_file;F;[[I"	path;TI"nil;T;[[@�i7;T;:init_doing_file;;;[�;{�;IC;" Initializes the doing file.;T;[o;)
;*I"
param;F;+I",Override path to a doing file, optional;T;I"	path;T;,[I"String;T;!@�;[�;I"f
Initializes the doing file.

@param      path  [String] Override path to a doing file, optional
;T; 0;!@�;4i�;"T;5o;6;7F;8i2;9i6;$@�;:T;%I"�def init_doing_file(path = nil)
  @doing_file =  File.expand_path(@config['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.expand_path(path)) && File.file?(File.expand_path(path)) && File.stat(File.expand_path(path)).size.positive?
    @doing_file = File.expand_path(path)
    input = IO.read(File.expand_path(path))
    input = input.force_encoding('utf-8') if input.respond_to? :force_encoding
    logger.debug('Read:', "read file #{File.expand_path(path)}")
  elsif path.length < 256
    @doing_file = File.expand_path(path)
    create(path)
    input = IO.read(File.expand_path(path))
    input = input.force_encoding('utf-8') if input.respond_to? :force_encoding
    logger.debug('Read:', "read file #{File.expand_path(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*(@\S+\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) \| (.*)/
      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
      item = Item.new(date, title, section)
      @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
end;T;&I"$def init_doing_file(path = nil);T;'To;;F;
;;;;I"Doing::WWID#create;F;[[I"
filename;TI"nil;T;[[@�iv;T;:create;;;[�;{�;IC;"Create a new doing file;T;[�;[�;I"
Create a new doing file
;T; 0;!@�;4i�;"T;5o;6;7F;8is;9iu;$@�;:T;%I"�def create(filename = nil)
  filename = @doing_file if filename.nil?
  return if File.exist?(filename) && File.stat(filename).size.positive?

  File.open(filename, 'w+') do |f|
    f.puts "#{@config['current_section']}:"
  end
end;T;&I"def create(filename = nil);T;'To;;F;
;;;;I"Doing::WWID#fork_editor;F;[[I"
input;TI"'';T[I"
message:;TI"
:default;T;[[@�i;T;:fork_editor;;;[�;{�;IC;"JCreate a process for an editor and wait for the file handle to return;T;[o;)
;*I"
param;F;+I"Text input for editor;T;I"
input;T;,[I"String;T;!@�o;)
;*I"
raise;F;+@.;0;,[I"MissingEditor;T;!@�;[�;I"
Create a process for an editor and wait for the file handle to return

@param      input  [String] Text input for editor
;T; 0;!@�;4i�;"T;5o;6;7F;8i;9i~;$@�;:T;%I"�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', '.md'])

  File.open(tmpfile.path, 'w+') do |f|
    f.puts input
    unless message.nil?
      f.puts message == :default ? "# The first line is the entry title, any lines after that are added as a note" : message
    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;T;&I"3def fork_editor(input = '', message: :default);T;'To;;F;
;;;;I"Doing::WWID#format_input;F;[[I"
input;T0;[[@�i�;T;:format_input;;;[�;{�;IC;"9Takes a multi-line string and formats it as an entry;T;[o;)
;*I"
param;F;+I"The string to parse;T;I"
input;T;,[I"String;T;!@�o;)
;*I"return;F;+I" [[String]title, [Note]note];T;0;,[I"
Array;T;!@�o;)
;*I"
raise;F;+@.;0;,[I"EmptyInput;T;!@�;[�;I"�
Takes a multi-line string and formats it as an entry

@param      input  [String] The string to parse

@return     [Array] [[String]title, [Note]note]
;T; 0;!@�;4i�;"T;5o;6;7F;8i�;9i�;$@�;:T;%I"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/
  done_rx = /(?<=^| )@(?<tag>done|finished|completed?)\((?<date>.*?)\)/i
  date_rx = /^(?:\s*- )?(?<date>.*?) \| (?=\S)/

  title.gsub!(done_rx) do
    m = Regexp.last_match
    t = m['tag']
    d = m['date']
    parsed_date = d =~ date_rx ? Time.parse(d) : d.chronify(guess: :begin)
    parsed_date.nil? ? m[0] : "@#{t}(#{parsed_date.strftime('%F %R')})"
  end

  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;T;&I"def format_input(input);T;'To;;F;
;;;;I"Doing::WWID#sections;F;[�;[[@�i�;T;:
sections;;;[�;{�;IC;"List sections;T;[o;)
;*I"return;F;+I"section titles;T;0;,[I"
Array;T;!@�;[�;I"8
List sections

@return     [Array] section titles
;T; 0;!@�;4i�;"T;5o;6;7F;8i�;9i�;$@�;:T;%I"/def sections
  @content.section_titles
end;T;&I"def sections;T;'To;;F;
;;;;I"Doing::WWID#guess_section;F;[[I"	frag;T0[I"
guessed:;TI"
false;T[I"
suggest:;TI"
false;T;[[@�i�;T;:guess_section;;;[�;{�;IC;"7Attempt to match a string with an existing section;T;[o;)
;*I"
param;F;+I"The user-provided string;T;I"	frag;T;,[I"String;T;!@�o;)
;*I"
param;F;+I"already guessed and failed;T;I"guessed;T;,[I"Boolean;T;!@�;[�;I"�
Attempt to match a string with an existing section

@param      frag     [String] The user-provided string
@param      guessed  [Boolean] already guessed and failed
;T; 0;!@�;4i�;"T;5o;6;7F;8i�;9i�;$@�;:T;%I"def guess_section(frag, guessed: false, suggest: false)
  return 'All' if frag =~ /^all$/i
  frag ||= @config['current_section']

  return frag.cap_first if @content.section?(frag)

  section = nil
  re = frag.split('').join('.*?')
  sections.each do |sect|
    next unless sect =~ /#{re}/i

    logger.debug('Match:', %(Assuming "#{sect}" from "#{frag}"))
    section = sect
    break
  end

  return section if suggest

  unless section || guessed
    alt = guess_view(frag, guessed: true, suggest: true)
    if alt
      meant_view = Prompt.yn("#{boldwhite("Did you mean")} `#{yellow("doing view #{alt}")}#{boldwhite}`?", default_response: 'n')

      raise WrongCommand.new("run again with #{"doing view #{alt}".boldwhite}", 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 InvalidSection.new("unknown section #{frag.bold.white}", topic: 'Missing:')
  end
  section ? section.cap_first : guessed
end;T;&I"<def guess_section(frag, guessed: false, suggest: false);T;'To;;F;
;;;;I"Doing::WWID#guess_view;F;[[I"	frag;T0[I"
guessed:;TI"
false;T[I"
suggest:;TI"
false;T;[[@�i;T;:guess_view;;;[�;{�;IC;"4Attempt to match a string with an existing view;T;[o;)
;*I"
param;F;+I"The user-provided string;T;I"	frag;T;,[I"String;T;!@o;)
;*I"
param;F;+I"already guessed;T;I"guessed;T;,[I"Boolean;T;!@;[�;I"�
Attempt to match a string with an existing view

@param      frag     [String] The user-provided string
@param      guessed  [Boolean] already guessed
;T; 0;!@;4i�;"T;5o;6;7F;8i;9i;$@�;:T;%I"def guess_view(frag, guessed: false, suggest: false)
  views.each { |view| return view if frag.downcase == view.downcase }
  view = false
  re = frag.split('').join('.*?')
  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 InvalidView.new(%(unknown view #{frag.bold.white}), topic: 'Missing:') unless alt

    meant_view = Prompt.yn("Did you mean `doing show #{alt}`?", default_response: 'n')

    raise WrongCommand.new("run again with #{"doing show #{alt}".yellow}", topic: 'Try again:') if meant_view

    raise InvalidView.new(%(unknown view #{alt.bold.white}), topic: 'Missing:')
  end
  view
end;T;&I"9def guess_view(frag, guessed: false, suggest: false);T;'To;;F;
;;;;I"Doing::WWID#add_item;F;[[I"
title;T0[I"section;TI"nil;T[I"opt;TI"{};T;[[@�iC;T;:
add_item;;;[�;{�;IC;"Adds an entry;T;[o;)
;*I"
param;F;+I"The entry title;T;I"
title;T;,[I"String;T;!@@o;)
;*I"
param;F;+I"The section to add to;T;I"section;T;,[I"String;T;!@@o;)
;*I"
param;F;+I"Additional Options;T;I"opt;T;,[I"	Hash;T;!@@o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+I"item start date;T;I"
:date;T;,[I"	Date;T;F0;!@@o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+I"5item note (will be converted if value is String);T;I"
:note;T;,[I"
Array;T;F0;!@@o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+I"
backdate;T;I"
:back;T;,[I"	Date;T;F0;!@@o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+I";new item is timed entry, marks previous entry as @done;T;I":timed;T;,[I"Boolean;T;F0;!@@;[�;I"�
Adds an entry

@param      title    [String] The entry title
@param      section  [String] The section to add to
@param      opt      [Hash] Additional Options

@option opt :date [Date] item start date
@option opt :note [Array] item note (will be converted if value is String)
@option opt :back [Date] backdate
@option opt :timed [Boolean] new item is timed entry, marks previous entry as @done
;T; 0;!@@;4i�;"T;5o;6;7F;8i7;9iB;$@�;:T;%I"�def add_item(title, section = nil, opt = {})
  section ||= @config['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 @auto_tag
    title = autotag(title)
    title.add_tags!(@config['default_tags']) unless @config['default_tags'].empty?
  end

  title.compress!
  entry = Item.new(opt[:back], title.strip, section)
  entry.note = note

  items = @content.dup
  if opt[:timed]
    items.reverse!
    items.each_with_index do |i, x|
      next if i.title =~ / @done/

      items[x].title = "#{i.title} @done(#{opt[:back].strftime('%F %R')})"
      break
    end
  end

  @content.push(entry)
  # logger.count(:added, level: :debug)
  logger.info('New entry:', %(added "#{entry.title}" to #{section}))
end;T;&I"1def add_item(title, section = nil, opt = {});T;'To;;F;
;;;;I"Doing::WWID#dedup;F;[[I"
items;T0[I"no_overlap:;TI"
false;T;[[@�io;T;:
dedup;;;[�;{�;IC;"<Remove items from a list that already exist in @content;T;[o;)
;*I"
param;F;+I"The items to deduplicate;T;I"
items;T;,[I"
Array;T;!@�o;)
;*I"
param;F;+I"-Remove items with overlapping time spans;T;I"no_overlap;T;,[I"Boolean;T;!@�;[�;I"�
Remove items from a list that already exist in @content

@param      items       [Array] The items to deduplicate
@param      no_overlap  [Boolean] Remove items with overlapping time spans
;T; 0;!@�;4i�;"T;5o;6;7F;8ii;9in;$@�;:T;%I"�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;T;&I"(def dedup(items, no_overlap: false);T;'To;;F;
;;;;I"Doing::WWID#import;F;[[I"
paths;T0[I"opt;TI"{};T;[[@�i�;T;:import;;;[�;{�;IC;"Imports external entries;T;[o;)
;*I"
param;F;+I"Path to JSON report file;T;I"
paths;T;,[I"String;T;!@�o;)
;*I"
param;F;+I"Additional Options;T;I"opt;T;,[I"	Hash;T;!@�;[�;I"}
Imports external entries

@param      paths  [String] Path to JSON report file
@param      opt    [Hash] Additional Options
;T; 0;!@�;4i�;"T;5o;6;7F;8i|;9i�;$@�;:T;%I"ndef import(paths, opt = {})
  Plugins.plugins[:import].each do |_, options|
    next unless opt[:type] =~ /^(#{options[:trigger].normalize_trigger})$/i

    if paths.count.positive?
      paths.each do |path|
        options[:class].import(self, path, options: opt)
      end
    else
      options[:class].import(self, nil, options: opt)
    end
    break
  end
end;T;&I" def import(paths, opt = {});T;'To;;F;
;;;;I"Doing::WWID#last_note;F;[[I"section;TI"
'All';T;[[@�i�;T;:last_note;;;[�;{�;IC;"<Return the content of the last note for a given section;T;[o;)
;*I"
param;F;+I".The section to retrieve from, default
All;T;I"section;T;,[I"String;T;!@�o;)
;*I"
raise;F;+@.;0;,[I"NoEntryError;T;!@�;[�;I"�
Return the content of the last note for a given section

@param      section  [String] The section to retrieve from, default
                     All
;T; 0;!@�;4i�;"T;5o;6;7F;8i�;9i�;$@�;:T;%I"Pdef 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;T;&I"#def last_note(section = 'All');T;'To;;F;
;;;;I"Doing::WWID#reset_item;F;[[I"	item;T0[I"resume:;TI"
false;T;[[@�i�;T;:reset_item;;;[�;{�;IC;"JReset start date to current time, optionally remove
done tag (resume);T;[o;)
;*I"
param;F;+I"the item to reset/resume;T;I"	item;T;,[I"	Item;T;!@�o;)
;*I"
param;F;+I"removing @done tag if true;T;I"resume;T;,[I"Boolean;T;!@�;[�;I"�Reset start date to current time, optionally remove
done tag (resume)

@param      item    [Item] the item to reset/resume
@param      resume  [Boolean] removing @done tag if true
;T; 0;!@�;4i�;"F;5o;6;7F;8i�;9i�;$@�;:T;%I"�def reset_item(item, resume: false)
  item.date = Time.now
  item.tag('done', remove: true) if resume
  logger.info('Reset:', %(Reset #{resume ? 'and resumed ' : ''} "#{item.title}" in #{item.section}))
  item
end;T;&I"(def reset_item(item, resume: false);T;'To;;F;
;;;;I"Doing::WWID#repeat_item;F;[[I"	item;T0[I"opt;TI"{};T;[[@�i�;T;:repeat_item;;;[�;{�;IC;"/Duplicate an item and add it as a new item;T;[o;)
;*I"
param;F;+I"the item to duplicate;T;I"	item;T;,[I"	Item;T;!@o;)
;*I"
param;F;+I"additional options;T;I"opt;T;,[I"	Hash;T;!@o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+I"open new item in editor;T;I":editor;T;,[I"Boolean;T;F0;!@o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+I"set start date;T;I"
:date;T;,[I"String;T;F0;!@o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+I" add new item to section :in;T;I":in;T;,[I"String;T;F0;!@o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+I"add note to new item;T;I"
:note;T;,[I"	Note;T;F0;!@o;)
;*I"return;F;+I"nothing;T;0;,0;!@;[�;I"lDuplicate an item and add it as a new item

@param      item    [Item] the item to duplicate
@param      opt     [Hash] additional options

@option opt :editor [Boolean] open new item in editor
@option opt :date   [String] set start date
@option opt :in     [String] add new item to section :in
@option opt :note   [Note] add note to new item

@return     nothing
;T; 0;!@;4i�;"F;5o;6;7F;8i�;9i�;$@�;:T;%I"�def repeat_item(item, opt = {})
  if item.should_finish?
    if item.should_time?
      item.title.tag!('done', value: Time.now.strftime('%F %R'))
    else
      item.title.tag!('done')
    end
  end

  # Remove @done tag
  title = item.title.sub(/\s*@done(\(.*?\))?/, '').chomp
  section = opt[:in].nil? ? item.section : guess_section(opt[:in])
  @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: true })
end;T;&I"$def repeat_item(item, opt = {});T;'To;;F;
;;;;I"Doing::WWID#repeat_last;F;[[I"opt;TI"{};T;[[@�i�;T;:repeat_last;;;[�;{�;IC;"Restart the last entry;T;[o;)
;*I"
param;F;+I"Additional Options;T;I"opt;T;,[I"	Hash;T;!@J;[�;I"J
Restart the last entry

@param      opt   [Hash] Additional Options
;T; 0;!@J;4i�;"T;5o;6;7F;8i�;9i�;$@�;:T;%I"Gdef repeat_last(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;T;&I"def repeat_last(opt = {});T;'To;;F;
;;;;I"Doing::WWID#last_entry;F;[[I"opt;TI"{};T;[[@�i�;T;:last_entry;;;[�;{�;IC;"Get the last entry;T;[o;)
;*I"
param;F;+I"Additional Options;T;I"opt;T;,[I"	Hash;T;!@a;[�;I"F
Get the last entry

@param      opt   [Hash] Additional Options
;T; 0;!@a;4i�;"T;5o;6;7F;8i�;9i�;$@�;:T;%I"3def last_entry(opt = {})
  opt[:tag_bool] ||= :and
  opt[:section] ||= @config['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;T;&I"def last_entry(opt = {});T;'To;;F;
;;;;I"Doing::WWID#all_tags;F;[[I"
items;T0[I"	opt:;TI"{};T;[[@�i;F;:
all_tags;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@x;4i�;$@�;:T;%I"~def all_tags(items, opt: {})
  all_tags = []
  items.each { |item| all_tags.concat(item.tags).uniq! }
  all_tags.sort
end;T;&I"!def all_tags(items, opt: {});T;'To;;F;
;;;;I"Doing::WWID#tag_groups;F;[[I"
items;T0[I"	opt:;TI"{};T;[[@�i;F;:tag_groups;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@�;:T;%I"def tag_groups(items, opt: {})
  all_items = filter_items(items, opt: opt)
  tags = all_tags(all_items, opt: {})
  tag_groups = {}
  tags.each do |tag|
    tag_groups[tag] ||= []
    tag_groups[tag] = filter_items(all_items, opt: { tag: tag, tag_bool: :or })
  end

  tag_groups
end;T;&I"#def tag_groups(items, opt: {});T;'To;;F;
;;;;I"#Doing::WWID#fuzzy_filter_items;F;[[I"
items;T0[I"	opt:;TI"{};T;[[@�i%;F;:fuzzy_filter_items;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@�;:T;%I"def fuzzy_filter_items(items, opt: {})
  scannable = items.map.with_index { |item, idx| "#{item.title} #{item.note.join(' ')}".gsub(/[|*?!]/, '') + "|#{idx}"  }.join("\n")

  fzf_args = [
    '--multi',
    %(--filter="#{opt[:search].sub(/^'?/, "'")}"),
    '--no-sort',
    '-d "\|"',
    '--nth=1'
  ]
  if opt[:case]
    fzf_args << case opt[:case].normalize_case
                when :sensitive
                  '+i'
                when :ignore
                  '-i'
                end
  end
  # fzf_args << '-e' if opt[:exact]
  # puts fzf_args.join(' ')
  res = `echo #{Shellwords.escape(scannable)}|#{Prompt.fzf} #{fzf_args.join(' ')}`
  selected = Items.new
  res.split(/\n/).each do |item|
    idx = item.match(/\|(\d+)$/)[1].to_i
    selected.push(items[idx])
  end
  selected
end;T;&I"+def fuzzy_filter_items(items, opt: {});T;'To;;F;
;;;;I"Doing::WWID#filter_items;F;[[I"
items;TI"Items.new;T[I"	opt:;TI"{};T;[[@�iV;T;:filter_items;;;[�;{�;IC;"*Filter items based on search criteria;T;[o;)
;*I"
param;F;+I"6The items to filter (if empty, filters all items);T;I"
items;T;,[I"
Array;T;!@�o;)
;*I"
param;F;+I"The filter parameters;T;I"opt;T;,[I"	Hash;T;!@�o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+0;I"
:section;T;,[I"String;T;F0;!@�o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+0;I":unfinished;T;,[I"Boolean;T;F0;!@�o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+I"�;T;I"	:tag;T;,[I"Array or String;T;F[I"$Array or comma-separated string;T;!@�o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+I"�;T;I":tag_bool;T;,[I"Symbol;T;F[I"	:and;TI":or;TI"	:not;T;!@�o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+I"�;T;I":search;T;,[I"String;T;F[I"string;TI"optional regex with //;T;!@�o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+I"[[Time]start, [Time]end];T;I":date_filter;T;,[I"
Array;T;F0;!@�o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+0;I":only_timed;T;,[I"Boolean;T;F0;!@�o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+I"�;T;I":before;T;,[I"String;T;F[I"Date/Time string;TI"
unparsed;T;!@�o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+I"�;T;I":after;T;,[I"String;T;F[I"Date/Time string;TI"
unparsed;T;!@�o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+0;I":today;T;,[I"Boolean;T;F0;!@�o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+0;I":yesterday;T;,[I"Boolean;T;F0;!@�o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+I"�;T;I":count;T;,[I"Number;T;F[I"Number to return;T;!@�o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+I"�;T;I"	:age;T;,[I"String;T;F[I"'old' or 'new';T;!@�;[�;I"	
Filter items based on search criteria

@param      items  [Array] The items to filter (if empty, filters all items)
@param      opt    [Hash] The filter parameters

@option opt [String] :section
@option opt [Boolean] :unfinished
@option opt [Array or String] :tag  (Array or comma-separated string)
@option opt [Symbol] :tag_bool  (:and, :or, :not)
@option opt [String] :search  (string, optional regex with //)
@option opt [Array] :date_filter  [[Time]start, [Time]end]
@option opt [Boolean] :only_timed
@option opt [String] :before  (Date/Time string, unparsed)
@option opt [String] :after  (Date/Time string, unparsed)
@option opt [Boolean] :today
@option opt [Boolean] :yesterday
@option opt [Number] :count  (Number to return)
@option opt [String] :age  ('old' or 'new')
;T; 0;!@�;4i�;"T;5o;6;7F;8iB;9iU;$@�;:T;%I"�def filter_items(items = Items.new, opt: {})
  time_rx = /^(\d{1,2}+(:\d{1,2}+)?( *(am|pm))?|midnight|noon)$/

  if items.nil? || items.empty?
    section = opt[:section] ? guess_section(opt[:section]) : 'All'
    items = section =~ /^all$/i ? @content.dup : @content.in_section(section)
  end

  opt[:time_filter] = [nil, nil]
  if opt[:from] && !opt[:date_filter]
    date_string = opt[:from]
    case date_string
    when / (to|through|thru|(un)?til|-+) /
      dates = date_string.split(/ (?:to|through|thru|(?:un)?til|-+) /)
      if dates[0].strip =~ time_rx && dates[-1].strip =~ time_rx
        time_start = dates[0].strip
        time_end = dates[-1].strip
      else
        start = dates[0].chronify(guess: :begin)
        finish = dates[-1].chronify(guess: :end)
      end
    when time_rx
      time_start = date_string
      time_end = nil
    else
      start = date_string.chronify(guess: :begin)
      finish = false
    end

    if time_start
      opt[:time_filter] = [time_start, time_end]
      Doing.logger.debug('Parser:', "--from string interpreted as time span, from #{time_start ? time_start : '12am'} to #{time_end ? time_end : '11:59pm'}")
    else
      raise InvalidTimeExpression, 'Unrecognized date string' unless start

      opt[:date_filter] = [start, finish]
      Doing.logger.debug('Parser:', "--from string interpreted as #{start.strftime('%F %R')} -- #{finish ? finish.strftime('%F %R') : 'now'}")
    end
  end

  if opt[:before] =~ time_rx
    opt[:time_filter][1] = opt[:before]
    opt[:before] = nil
  end

  if 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 do |item|
    keep = true
    if opt[:unfinished]
      finished = item.tags?('done', :and)
      finished = opt[:not] ? !finished : finished
      keep = false if finished
    end

    if keep && opt[:tag]
      opt[:tag_bool] ||= :and
      tag_match = opt[:tag].nil? || opt[:tag].empty? ? true : item.tags?(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, fuzzy: opt[:fuzzy])
                     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]
      start_string = if opt[:time_filter][0].nil?
                       "#{item.date.strftime('%Y-%m-%d')} 12am"
                     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.next_day.strftime('%Y-%m-%d')} 12am"
                   else
                     "#{item.date.strftime('%Y-%m-%d')} #{opt[:time_filter][1]}"
                   end
      end_time = end_string.chronify(guess: :end)

      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] && !opt[:tag_filter]['tags'].empty?
      keep = item.tags?(opt[:tag_filter]['tags'], opt[:tag_filter]['bool'])
      keep = opt[:not] ? !keep : keep
    end

    if keep && opt[:before]
      time_string = opt[:before]
      if time_string =~ time_rx
        cutoff = "#{item.date.strftime('%Y-%m-%d')} #{time_string}".chronify(guess: :begin)
      else
        cutoff = time_string.chronify(guess: :begin)
      end
      keep = cutoff && item.date <= cutoff
      keep = opt[:not] ? !keep : keep
    end

    if keep && opt[:after]
      time_string = opt[:after]
      if time_string =~ time_rx
        cutoff = "#{item.date.strftime('%Y-%m-%d')} #{time_string}".chronify(guess: :end)
      else
        cutoff = time_string.chronify(guess: :end)
      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
  count = opt[:count]&.positive? ? opt[:count] : filtered_items.length

  output = Items.new

  if opt[:age] =~ /^o/i
    output.concat(filtered_items.slice(0, count).reverse)
  else
    output.concat(filtered_items.reverse.slice(0, count))
  end

  output
end;T;&I"1def filter_items(items = Items.new, opt: {});T;'To;;F;
;;;;I"Doing::WWID#interactive;F;[[I"opt;TI"{};T;[[@�i�;T;:interactive;;;[�;{�;IC;"bDisplay an interactive menu of entries

Options hash is shared with #filter_items and #act_on;T;[o;)
;*I"
param;F;+I"Additional options;T;I"opt;T;,[I"	Hash;T;!@N	o;)
;*I"
raise;F;+@.;0;,[I"NoResults;T;!@N	;[�;I"�
Display an interactive menu of entries

@param      opt   [Hash] Additional options

Options hash is shared with #filter_items and #act_on
;T; 0;!@N	;4i�;"T;5o;6;7F;8i�;9i�;$@�;:T;%I"def interactive(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[:not]
  opt[:multiple] = true
  opt[:show_if_single] = true
  filter_options = %i[after before case date_filter from fuzzy not search section].each_with_object({}) {
    |k, hsh| hsh[k] = opt[k]
  }
  items = filter_items(Items.new, opt: filter_options)

  selection = Prompt.choose_from_items(items, include_section: opt[:section] =~ /^all$/i, **opt)

  raise NoResults, 'no items selected' if selection.nil? || selection.empty?

  act_on(selection, opt)
end;T;&I"def interactive(opt = {});T;'To;;F;
;;;;I"Doing::WWID#act_on;F;[[I"
items;T0[I"opt;TI"{};T;[[@�i0;T;:act_on;;;[�;{�;IC;"�Perform actions on a set of entries. If
            no valid action is included in the opt
            hash and the terminal is a TTY, a menu
            will be presented;T;[o;)
;*I"
param;F;+I"Array of Items to affect;T;I"
items;T;,[I"
Array;T;!@i	o;)
;*I"
param;F;+I"#Options and actions to perform;T;I"opt;T;,[I"	Hash;T;!@i	o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+0;I":editor;T;,[I"Boolean;T;F0;!@i	o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+0;I":delete;T;,[I"Boolean;T;F0;!@i	o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+0;I"	:tag;T;,[I"String;T;F0;!@i	o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+0;I"
:flag;T;,[I"Boolean;T;F0;!@i	o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+0;I":finish;T;,[I"Boolean;T;F0;!@i	o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+0;I":cancel;T;,[I"Boolean;T;F0;!@i	o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+0;I"
:archive;T;,[I"Boolean;T;F0;!@i	o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+0;I":output;T;,[I"String;T;F0;!@i	o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+0;I"
:save_to;T;,[I"String;T;F0;!@i	o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+0;I":again;T;,[I"Boolean;T;F0;!@i	o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+0;I":resume;T;,[I"Boolean;T;F0;!@i	;[�;I"_
Perform actions on a set of entries. If
            no valid action is included in the opt
            hash and the terminal is a TTY, a menu
            will be presented

@param      items  [Array] Array of Items to affect
@param      opt    [Hash] Options and actions to perform

@option opt [Boolean] :editor
@option opt [Boolean] :delete
@option opt [String] :tag
@option opt [Boolean] :flag
@option opt [Boolean] :finish
@option opt [Boolean] :cancel
@option opt [Boolean] :archive
@option opt [String] :output
@option opt [String] :save_to
@option opt [Boolean] :again
@option opt [Boolean] :resume
;T; 0;!@i	;4i�;"T;5o;6;7F;8i;9i/;$@�;:T;%I"�def act_on(items, 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',
      'cancel',
      'delete',
      'finish',
      'flag',
      'archive',
      'move',
      'edit',
      'output formatted'
    ]

    actions.concat(['resume/repeat', 'begin/reset']) if items.count == 1

    choice = Prompt.choose_from(actions,
                                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/)
    to_do.each do |action|
      case action
      when /resume/
        opt[:resume] = true
      when /reset/
        opt[:reset] = 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]

        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 tag =~ /^ *$/

        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
          next if filename.empty?

          opt[:save_to] = filename
        end
      when /archive/
        opt[:archive] = true
      when /delete/
        opt[:delete] = true
      when /edit/
        opt[:editor] = true
      when /finish/
        opt[:finish] = true
      when /cancel/
        opt[:cancel] = true
      when /move/
        section = choose_section.strip
        opt[:move] = section.strip unless section =~ /^ *$/
      when /flag/
        opt[:flag] = true
      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] })
    elsif opt[:reset]
      res = if item.tags?('done', :and) && !opt[:resume]
              opt[:force] ? true : Prompt.yn('Remove @done tag?', default_response: 'y')
            else
              opt[:resume]
            end
      @content.update_item(item, reset_item(item, resume: res))
    end
    write(@doing_file)

    return
  end

  if opt[:delete]
    res = opt[:force] ? true : Prompt.yn("Delete #{items.size} items?", default_response: 'y')
    if res
      items.each { |i| @content.delete_item(i, single: items.count == 1) }
      write(@doing_file)
    end
    return
  end

  if opt[:flag]
    tag = @config['marker_tag'] || 'flagged'
    items.map! do |i|
      i.tag(tag, date: false, remove: opt[:remove], single: single)
    end
  end

  if opt[:finish] || opt[:cancel]
    tag = 'done'
    items.map! do |i|
      if i.should_finish?
        should_date = !opt[:cancel] && i.should_time?
        i.tag(tag, date: should_date, remove: opt[:remove], single: single)
      end
    end
  end

  if opt[:tag]
    tag = opt[:tag]
    items.map! do |i|
      i.tag(tag, date: false, remove: opt[:remove], single: single)
    end
  end

  if opt[:archive] || opt[:move]
    section = opt[:archive] ? 'Archive' : guess_section(opt[:move])
    items.map! { |i| i.move_to(section, label: true) }
  end

  write(@doing_file)

  if opt[:editor]

    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 = "\n-----------\n"
    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(divider)}\n\n#{notice}"

    new_items = fork_editor(input).split(/#{divider}/)

    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?
        @content.delete_item(items[i], single: new_items.count == 1)
        Doing.logger.count(:deleted)
      else
        date, title, note = format_input(new_item)

        note.map!(&:strip)
        note.delete_if(&:ignore?)
        item = items[i]
        old_item = item.dup
        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)
        end
      end
    end

    write(@doing_file)
  end

  return unless opt[:output]

  items.map! do |i|
    i.title = "#{i.title} @project(#{i.section})"
    i
  end

  @content = Items.new
  @content.concat(items)
  @content.add_section(Section.new('Export'), log: false)
  options = { section: 'Export' }

  if opt[:output] =~ /doing/
    options[:output] = 'template'
    options[:template] = '- %date | %title%note'
  else
    options[:output] = opt[:output]
    options[:template] = opt[:template] || nil
  end

  output = list_section(options)

  if opt[:save_to]
    file = File.expand_path(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;T;&I" def act_on(items, opt = {});T;'To;;F;
;;;;I"Doing::WWID#tag_last;F;[[I"opt;TI"{};T;[[@�i*;T;:
tag_last;;;[�;{�;IC;"$Tag the last entry or X entries;T;[o;)
;*I"
param;F;+I"AAdditional Options (see
#filter_items for filtering
options);T;I"opt;T;,[I"	Hash;T;!@�	o;)
;*I"see;F;+0;I"#filter_items;T;,0;!@�	o;)
;*I"
raise;F;+@.;0;,[I"NoResults;T;!@�	;[�;I"�
Tag the last entry or X entries

@param      opt   [Hash] Additional Options (see
                  #filter_items for filtering
                  options)

@see        #filter_items
;T; 0;!@�	;4i�;"T;5o;6;7F;8i!;9i);$@�;:T;%I"def tag_last(opt = {})
  opt[:count] ||= 1
  opt[:archive] ||= false
  opt[:tags] ||= ['done']
  opt[:sequential] ||= false
  opt[:date] ||= false
  opt[:remove] ||= 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?

  items.each do |item|
    added = []
    removed = []

    if opt[:autotag]
      new_title = autotag(item.title) if @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[:sequential]
        next_entry = next_item(item)

        done_date = if next_entry.nil?
                      Time.now
                    else
                      next_entry.date - 60
                    end
      elsif opt[:took]
        if item.date + opt[:took] > Time.now
          item.date = Time.now - opt[:took]
          done_date = Time.now
        else
          done_date = item.date + opt[:took]
        end
      elsif opt[:back]
        done_date = if opt[:back].is_a? Integer
                      item.date + opt[:back]
                    else
                      item.date + (opt[:back] - item.date)
                    end
      else
        done_date = Time.now
      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 opt[:remove] || opt[:rename]
          rename_to = nil
          if opt[:rename]
            rename_to = tag
            tag = opt[:rename]
          end
          old_title = item.title.dup
          item.title.tag!(tag, remove: opt[:remove], rename_to: rename_to, regex: opt[:regex])
          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
          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
  end

  write(@doing_file)
end;T;&I"def tag_last(opt = {});T;'To;;F;
;;;;I"Doing::WWID#next_item;F;[[I"	item;T0[I"options;TI"{};T;[[@�i�;T;:next_item;;;[�;{�;IC;"Get next item in the index;T;[	o;)
;*I"
param;F;+I"target item;T;I"	item;T;,[I"	Item;T;!@�	o;)
;*I"
param;F;+I"additional options;T;I"options;T;,[I"	Hash;T;!@�	o;)
;*I"see;F;+0;I"#filter_items;T;,0;!@�	o;)
;*I"return;F;+I"-the next chronological item in the index;T;0;,[I"	Item;T;!@�	;[�;I"�
Get next item in the index

@param      item     [Item] target item
@param      options  [Hash] additional options
@see #filter_items

@return     [Item] the next chronological item in the index
;T; 0;!@�	;4i�;"T;5o;6;7F;8i�;9i�;$@�;:T;%I"�def next_item(item, options = {})
  items = filter_items(Items.new, opt: options)

  idx = items.index(item)

  idx.positive? ? items[idx - 1] : nil
end;T;&I"&def next_item(item, options = {});T;'To;;F;
;;;;I"Doing::WWID#edit_last;F;[[I"
section:;TI"
'All';T[I"
options:;TI"{};T;[[@�i�;T;:edit_last;;;[�;{�;IC;"Edit the last entry;T;[o;)
;*I"
param;F;+I"The section, default "All";T;I"section;T;,[I"String;T;!@%
;[�;I"T
Edit the last entry

@param      section  [String] The section, default "All"
;T; 0;!@%
;4i�;"T;5o;6;7F;8i�;9i�;$@�;:T;%I"Ndef edit_last(section: 'All', options: {})
  options[:section] = guess_section(section)

  item = last_entry(options)

  if item.nil?
    logger.debug('Skipped:', 'No entries found')
    return
  end

  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"))
  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)

    write(@doing_file)
  end
end;T;&I"/def edit_last(section: 'All', options: {});T;'To;;F;
;;;;I"Doing::WWID#stop_start;F;[[I"target_tag;T0[I"opt;TI"{};T;[[@�i�;T;:stop_start;;;[�;{�;IC;"Accepts one tag and the raw text of a new item if the
passed tag is on any item, it's replaced with @done.
if new_item is not nil, it's tagged with the passed
tag and inserted. This is for use where only one
instance of a given tag should exist (@meanwhile);T;[o;)
;*I"
param;F;+I"Tag to replace;T;I"target_tag;T;,[I"String;T;!@?
o;)
;*I"
param;F;+I"Additional Options;T;I"opt;T;,[I"	Hash;T;!@?
o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+I"target section;T;I"
:section;T;,[I"String;T;F0;!@?
o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+I"archive old item;T;I"
:archive;T;,[I"Boolean;T;F0;!@?
o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+I"backdate new item;T;I"
:back;T;,[I"	Date;T;F0;!@?
o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+I" content to use for new item;T;I":new_item;T;,[I"String;T;F0;!@?
o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+I"note content for new item;T;I"
:note;T;,[I"
Array;T;F0;!@?
;[�;I"]
Accepts one tag and the raw text of a new item if the
passed tag is on any item, it's replaced with @done.
if new_item is not nil, it's tagged with the passed
tag and inserted. This is for use where only one
instance of a given tag should exist (@meanwhile)

@param      target_tag  [String] Tag to replace
@param      opt         [Hash] Additional Options

@option opt :section [String] target section
@option opt :archive [Boolean] archive old item
@option opt :back [Date] backdate new item
@option opt :new_item [String] content to use for new item
@option opt :note [Array] note content for new item;T; 0;!@?
;4i�;"T;5o;6;7F;8i�;9i�;$@�;:T;%I">def stop_start(target_tag, opt = {})
  tag = target_tag.dup
  opt[:section] ||= @config['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|
    next unless item.section == opt[:section] || opt[:section] =~ /all/i

    next unless item.title =~ /@#{tag}/

    item.title.add_tags!([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
  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;T;&I")def stop_start(target_tag, opt = {});T;'To;;F;
;;;;I"Doing::WWID#write;F;[[I"	file;TI"nil;T[I"backup:;TI"	true;T;[[@�i;T;;^;;;[�;{�;IC;"$Write content to file or STDOUT;T;[o;)
;*I"
param;F;+I"The filepath to write to;T;I"	file;T;,[I"String;T;!@�
;[�;I"[
Write content to file or STDOUT

@param      file  [String] The filepath to write to
;T; 0;!@�
;4i�;"T;5o;6;7F;8i;9i;$@�;:T;%I"�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 @config.key?('run_after')
  end
end;T;&I"(def write(file = nil, backup: true);T;'To;;F;
;;;;I"Doing::WWID#restore_backup;F;[[I"	file;T0;[[@�i%;T;:restore_backup;;;[�;{�;IC;"*Restore a backed up version of a file;T;[o;)
;*I"
param;F;+I"The filepath to restore;T;I"	file;T;,[I"String;T;!@�
;[�;I"`
Restore a backed up version of a file

@param      file  [String] The filepath to restore
;T; 0;!@�
;4i�;"T;5o;6;7F;8i ;9i$;$@�;:T;%I"�def restore_backup(file)
  if File.exist?("#{file}~")
    FileUtils.cp("#{file}~", file)
    logger.warn('File update:', "Restored #{file.sub(/^#{Util.user_home}/, '~')}")
  else
    logger.error('Restore error:', 'No backup file found')
  end
end;T;&I"def restore_backup(file);T;'To;;F;
;;;;I"Doing::WWID#rotate;F;[[I"opt;TI"{};T;[[@�i1;T;:rotate;;;[�;{�;IC;"4Rename doing file with date and start fresh one;T;[�;[�;I"6
Rename doing file with date and start fresh one
;T; 0;!@�
;4i�;"T;5o;6;7F;8i.;9i0;$@�;:T;%I"Adef rotate(opt = {})
  keep = opt[:keep] || 0
  tags = []
  tags.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

  @content.each do |item|
    break if counter >= max
    if opt[:before]
      time_string = opt[:before]
      cutoff = time_string.chronify(guess: :begin)
    end

    unless ((!tags.empty? && !item.tags?(tags, bool)) || (opt[:search] && !item.search(opt[:search].to_s)) || (opt[:before] && item.date >= cutoff))
      new_item = @content.delete(item)
      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;T;&I"def rotate(opt = {});T;'To;;F;
;;;;I"Doing::WWID#choose_section;F;[�;[[@�in;T;:choose_section;;;[�;{�;IC;"9Generate a menu of sections and allow user selection;T;[o;)
;*I"return;F;+I"The selected section name;T;0;,[I"String;T;!@�
;[�;I"k
Generate a menu of sections and allow user selection

@return     [String] The selected section name
;T; 0;!@�
;4i�;"T;5o;6;7F;8ii;9im;$@�;:T;%I"�def choose_section
  choice = Prompt.choose_from(@content.section_titles.sort, prompt: 'Choose a section > ', fzf_args: ['--height=60%'])
  choice ? choice.strip : choice
end;T;&I"def choose_section;T;'To;;F;
;;;;I"Doing::WWID#views;F;[�;[[@�ix;T;:
views;;;[�;{�;IC;"List available views;T;[o;)
;*I"return;F;+I"View names;T;0;,[I"
Array;T;!@�
;[�;I";
List available views

@return     [Array] View names
;T; 0;!@�
;4i�;"T;5o;6;7F;8is;9iw;$@�;:T;%I"Kdef views
  @config.has_key?('views') ? @config['views'].keys : []
end;T;&I"def views;T;'To;;F;
;;;;I"Doing::WWID#choose_view;F;[�;[[@�i�;T;:choose_view;;;[�;{�;IC;"6Generate a menu of views and allow user selection;T;[o;)
;*I"return;F;+I"The selected view name;T;0;,[I"String;T;!@�
;[�;I"e
Generate a menu of views and allow user selection

@return     [String] The selected view name
;T; 0;!@�
;4i�;"T;5o;6;7F;8i|;9i�;$@�;:T;%I"�def choose_view
  choice = Prompt.choose_from(views.sort, prompt: 'Choose a view > ', fzf_args: ['--height=60%'])
  choice ? choice.strip : choice
end;T;&I"def choose_view;T;'To;;F;
;;;;I"Doing::WWID#get_view;F;[[I"
title;T0;[[@�i�;T;:
get_view;;;[�;{�;IC;"#Gets a view from configuration;T;[o;)
;*I"
param;F;+I"&The title of the view to retrieve;T;I"
title;T;,[I"String;T;!@;[�;I"d
Gets a view from configuration

@param      title  [String] The title of the view to retrieve
;T; 0;!@;4i�;"T;5o;6;7F;8i�;9i�;$@�;:T;%I"jdef get_view(title)
  return @config['views'][title] if @config['views'].has_key?(title)

  false
end;T;&I"def get_view(title);T;'To;;F;
;;;;I"Doing::WWID#list_section;F;[[I"opt;TI"{};T;[[@�i�;T;:list_section;;;[�;{�;IC;"3Display contents of a section based on options;T;[o;)
;*I"
param;F;+I"Additional Options;T;I"opt;T;,[I"	Hash;T;!@;[�;I"b
Display contents of a section based on options

@param      opt   [Hash] Additional Options
;T; 0;!@;4i�;"T;5o;6;7F;8i�;9i�;$@�;:T;%I"	def list_section(opt = {})
  opt[:config_template] ||= 'default'
  cfg = @config.dig('templates',
                    opt[:config_template]).deep_merge({
                                                        'wrap_width' => @config['wrap_width'] || 0,
                                                        'date_format' => @config['default_date_format'],
                                                        'order' => @config['order'] || 'asc',
                                                        'tags_color' => @config['tags_color'],
                                                        'duration' => @config['duration'],
                                                        'interval_format' => @config['interval_format']
                                                      })
  opt[:duration] ||= cfg['duration'] || false
  opt[:interval_format] ||= cfg['interval_format'] || 'text'
  opt[:count] ||= 0
  opt[:age] ||= 'newest'
  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[:highlight] ||= true
  title = ''
  is_single = true
  if opt[:section].nil?
    opt[:section] = choose_section
    title = opt[:section]
  elsif opt[:section].instance_of?(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.new, opt: opt).reverse

  items.reverse! if opt[:order] =~ /^d/i

  if opt[:interactive]
    opt[:menu] = !opt[:force]
    opt[:query] = '' # opt[:search]
    opt[:multiple] = true
    selected = Prompt.choose_from_items(items, include_section: opt[:section] =~ /^all$/i, **opt)

    raise NoResults, 'no items selected' if selected.empty?

    act_on(selected, opt)
    return
  end

  opt[:output] ||= 'template'
  opt[:wrap_width] ||= @config['templates']['default']['wrap_width'] || 0
  output(items, title, is_single, opt)
end;T;&I"def list_section(opt = {});T;'To;;F;
;;;;I"Doing::WWID#archive;F;[[I"section;TI"@config['current_section'];T[I"options;TI"{};T;[[@�i�;T;:archive;;;[�;{�;IC;"RMove entries from a section to Archive or other specified
            section;T;[o;)
;*I"
param;F;+I"The source section;T;I"section;T;,[I"String;T;!@2o;)
;*I"
param;F;+I"Options;T;I"options;T;,[I"	Hash;T;!@2;[�;I"�
Move entries from a section to Archive or other specified
            section

@param      section      [String] The source section
@param      options      [Hash] Options
;T; 0;!@2;4i�;"T;5o;6;7F;8i�;9i�;$@�;:T;%I"�def archive(section = @config['current_section'], options = {})
  count       = options[:keep] || 0
  destination = options[:destination] || 'Archive'
  tags        = options[:tags] || []
  bool        = options[:bool] || :and

  section = choose_section if section.nil? || 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: tags, bool: bool, search: options[:search], label: options[:label], before: options[:before] })
    write(doing_file)
  else
    raise InvalidArgument, 'Either source or destination does not exist'
  end
end;T;&I"Ddef archive(section = @config['current_section'], options = {});T;'To;;F;
;;;;I"Doing::WWID#today;F;[[I"
times;TI"	true;T[I"output;TI"nil;T[I"opt;TI"{};T;[[@�i�;T;:
today;;;[�;{�;IC;"*Show all entries from the current day;T;[o;)
;*I"
param;F;+I"show times;T;I"
times;T;,[I"Boolean;T;!@Ro;)
;*I"
param;F;+I"output format;T;I"output;T;,[I"String;T;!@Ro;)
;*I"
param;F;+I"Options;T;I"opt;T;,[I"	Hash;T;!@R;[�;I"�
Show all entries from the current day

@param      times   [Boolean] show times
@param      output  [String] output format
@param      opt     [Hash] Options
;T; 0;!@R;4i�;"T;5o;6;7F;8i�;9i�;$@�;:T;%I"jdef today(times = true, output = nil, opt = {})
  opt[:totals] ||= false
  opt[:sort_tags] ||= false

  cfg = @config['templates']['today'].deep_merge(@config['templates']['default']).deep_merge({
    'wrap_width' => @config['wrap_width'] || 0,
    'date_format' => @config['default_date_format'],
    'order' => @config['order'] || 'asc',
    'tags_color' => @config['tags_color'],
    'duration' => @config['duration'],
    'interval_format' => @config['interval_format']
  })

  opt[:duration] ||= cfg['duration'] || false
  opt[:interval_format] ||= cfg['interval_format'] || 'text'

  options = {
    after: opt[:after],
    before: opt[:before],
    count: 0,
    duration: opt[:duration],
    from: opt[:from],
    format: cfg['date_format'],
    interval_format: opt[:interval_format],
    order: cfg['order'] || 'asc',
    output: output,
    section: opt[:section],
    sort_tags: opt[:sort_tags],
    template: cfg['template'],
    times: times,
    today: true,
    totals: opt[:totals],
    wrap_width: cfg['wrap_width'],
    tags_color: cfg['tags_color'],
    config_template: 'today'
  }
  list_section(options)
end;T;&I"4def today(times = true, output = nil, opt = {});T;'To;;F;
;;;;I"Doing::WWID#list_date;F;[
[I"
dates;T0[I"section;T0[I"
times;TI"nil;T[I"output;TI"nil;T[I"opt;TI"{};T;[[@�i);T;:list_date;;;[�;{�;IC;"(Display entries within a date range;T;[
o;)
;*I"
param;F;+I"[start, end];T;I"
dates;T;,[I"
Array;T;!@{o;)
;*I"
param;F;+I"The section;T;I"section;T;,[I"String;T;!@{o;)
;*I"
param;F;+I"Show times;T;I"
times;T;,[I"	Bool;T;!@{o;)
;*I"
param;F;+I"Output format;T;I"output;T;,[I"String;T;!@{o;)
;*I"
param;F;+I"Additional Options;T;I"opt;T;,[I"	Hash;T;!@{;[�;I"�
Display entries within a date range

@param      dates    [Array] [start, end]
@param      section  [String] The section
@param      times    (Bool) Show times
@param      output   [String] Output format
@param      opt      [Hash] Additional Options
;T; 0;!@{;4i�;"T;5o;6;7F;8i ;9i(;$@�;:T;%I"�def list_date(dates, section, times = nil, output = nil, opt = {})
  opt[:totals] ||= false
  opt[:sort_tags] ||= false
  section = guess_section(section)
  # :date_filter expects an array with start and end date
  dates = [dates, dates] if dates.instance_of?(String)

  list_section({
                 section: section,
                 count: 0,
                 order: 'asc',
                 date_filter: dates,
                 times: times,
                 output: output,
                 totals: opt[:totals],
                 duration: opt[:duration],
                 sort_tags: opt[:sort_tags],
                 config_template: 'default'
               })
end;T;&I"Gdef list_date(dates, section, times = nil, output = nil, opt = {});T;'To;;F;
;;;;I"Doing::WWID#yesterday;F;[	[I"section;T0[I"
times;TI"nil;T[I"output;TI"nil;T[I"opt;TI"{};T;[[@�iF;T;:yesterday;;;[�;{�;IC;"'Show entries from the previous day;T;[	o;)
;*I"
param;F;+I"The section;T;I"section;T;,[I"String;T;!@�o;)
;*I"
param;F;+I"Show times;T;I"
times;T;,[I"	Bool;T;!@�o;)
;*I"
param;F;+I"Output format;T;I"output;T;,[I"String;T;!@�o;)
;*I"
param;F;+I"Additional Options;T;I"opt;T;,[I"	Hash;T;!@�;[�;I"�
Show entries from the previous day

@param      section  [String] The section
@param      times    (Bool) Show times
@param      output   [String] Output format
@param      opt      [Hash] Additional Options
;T; 0;!@�;4i�;"T;5o;6;7F;8i>;9iE;$@�;:T;%I"�def yesterday(section, times = nil, output = nil, opt = {})
  opt[:totals] ||= false
  opt[:sort_tags] ||= false
  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]

  options = {
    after: opt[:after],
    before: opt[:before],
    count: 0,
    duration: opt[:duration],
    from: opt[:from],
    order: opt[:order],
    output: output,
    section: section,
    sort_tags: opt[:sort_tags],
    tag_order: opt[:tag_order],
    times: times,
    totals: opt[:totals],
    yesterday: true,
    config_template: 'today'
  }

  list_section(options)
end;T;&I"@def yesterday(section, times = nil, output = nil, opt = {});T;'To;;F;
;;;;I"Doing::WWID#recent;F;[[I"
count;TI"10;T[I"section;TI"nil;T[I"opt;TI"{};T;[[@�ii;T;:recent;;;[�;{�;IC;"Show recent entries;T;[o;)
;*I"
param;F;+I"The number to show;T;I"
count;T;,[I"Integer;T;!@�o;)
;*I"
param;F;+I"0The section to show from, default Currently;T;I"section;T;,[I"String;T;!@�o;)
;*I"
param;F;+I"Additional Options;T;I"opt;T;,[I"	Hash;T;!@�;[�;I"�
Show recent entries

@param      count    [Integer] The number to show
@param      section  [String] The section to show from, default Currently
@param      opt      [Hash] Additional Options
;T; 0;!@�;4i�;"T;5o;6;7F;8ib;9ih;$@�;:T;%I"�def recent(count = 10, section = nil, opt = {})
  times = opt[:t] || true
  opt[:totals] ||= false
  opt[:sort_tags] ||= false

  cfg = @config['templates']['recent'].deep_merge(@config['templates']['default']).deep_merge({
    'wrap_width' => @config['wrap_width'] || 0,
    'date_format' => @config['default_date_format'],
    'order' => @config['order'] || 'asc',
    'tags_color' => @config['tags_color'],
    'duration' => @config['duration'],
    'interval_format' => @config['interval_format']
  })
  opt[:duration] ||= cfg['duration'] || false
  opt[:interval_format] ||= cfg['interval_format'] || 'text'

  section ||= @config['current_section']
  section = guess_section(section)

  list_section({ section: section, wrap_width: cfg['wrap_width'], count: count,
                 format: cfg['date_format'], template: cfg['template'],
                 order: 'asc', times: times, totals: opt[:totals],
                 sort_tags: opt[:sort_tags], tags_color: opt[:tags_color], config_template: 'recent' })
end;T;&I"4def recent(count = 10, section = nil, opt = {});T;'To;;F;
;;;;I"Doing::WWID#last;F;[[I"times:;TI"	true;T[I"
section:;TI"nil;T[I"
options:;TI"{};T;[[@�i�;T;:	last;;;[�;{�;IC;"Show the last entry;T;[o;)
;*I"
param;F;+I"Show times;T;I"
times;T;,[I"	Bool;T;!@o;)
;*I"
param;F;+I",Section to pull from, default Currently;T;I"section;T;,[I"String;T;!@;[�;I"�
Show the last entry

@param      times    (Bool) Show times
@param      section  [String] Section to pull from, default Currently
;T; 0;!@;4i�;"T;5o;6;7F;8i�;9i�;$@�;:T;%I"�def last(times: true, section: nil, options: {})
  section = section.nil? || section =~ /all/i ? 'All' : guess_section(section)
  cfg = @config['templates']['last'].deep_merge(@config['templates']['default']).deep_merge({
    'wrap_width' => @config['wrap_width'] || 0,
    'date_format' => @config['default_date_format'],
    'order' => @config['order'] || 'asc',
    'tags_color' => @config['tags_color'],
    'duration' => @config['duration'],
    'interval_format' => @config['interval_format']
  })
  options[:duration] ||= cfg['duration'] || false
  options[:interval_format] ||= cfg['interval_format'] || 'text'

  opts = {
    section: section,
    wrap_width: cfg['wrap_width'],
    count: 1,
    format: cfg['date_format'],
    template: cfg['template'],
    times: times,
    duration: options[:duration],
    interval_format: options[:interval_format],
    case: options[:case],
    not: options[:negate],
    config_template: 'last'
  }

  if options[:tag]
    opts[:tag_filter] = {
      'tags' => options[:tag],
      'bool' => options[:tag_bool]
    }
  end

  opts[:search] = options[:search] if options[:search]

  list_section(opts)
end;T;&I"5def last(times: true, section: nil, options: {});T;'To;;F;
;;;;I"Doing::WWID#autotag;F;[[I"	text;T0;[[@�i�;T;:autotag;;;[�;{�;IC;"�Uses 'autotag' configuration to turn keywords into tags for time tracking.
Does not repeat tags in a title, and only converts the first instance of an
untagged keyword;T;[o;)
;*I"
param;F;+I"The text to tag;T;I"	text;T;,[I"String;T;!@1;[�;I"�
Uses 'autotag' configuration to turn keywords into tags for time tracking.
Does not repeat tags in a title, and only converts the first instance of an
untagged keyword

@param      text  [String] The text to tag
;T; 0;!@1;4i�;"T;5o;6;7F;8i�;9i�;$@�;:T;%I"�
def autotag(text)
  return unless text
  return text unless @auto_tag

  original = text.dup

  current_tags = text.scan(/@\w+/).map { |t| t.sub(/^@/, '') }
  tagged = {
    whitelisted: [],
    synonyms: [],
    transformed: [],
    replaced: []
  }

  @config['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

  @config['autotag']['synonyms'].each do |tag, v|
    v.each do |word|
      next unless text =~ /\b#{word}\b/i

      unless current_tags.include?(tag) || tagged[:whitelisted].include?(tag)
        tagged[:synonyms].push(tag)
        tagged[:synonyms] = tagged[:synonyms].uniq
      end
    end
  end

  if @config['autotag'].key? 'transform'
    @config['autotag']['transform'].each do |tag|
      next unless tag =~ /\S+:\S+/

      rx, r = tag.split(/:/)
      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|
          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].log_tags}") unless tagged[:whitelisted].empty?
  logger.debug('Autotag:', "synonyms: #{tagged[:synonyms].log_tags}") unless tagged[:synonyms].empty?
  logger.debug('Autotag:', "transforms: #{tagged[:transformed].log_tags}") unless tagged[:transformed].empty?
  logger.debug('Autotag:', "transform replaced: #{tagged[:replaced].log_tags}") unless tagged[:replaced].empty?

  tail_tags = tagged[:synonyms].concat(tagged[:transformed])
  tail_tags.sort!
  tail_tags.uniq!

  text.add_tags!(tail_tags) unless tail_tags.empty?

  if text == original
    logger.debug('Autotag:', "no change to \"#{text.strip}\"")
  else
    new_tags = tagged[:whitelisted].concat(tail_tags).concat(tagged[:replaced])
    logger.debug('Autotag:', "added #{new_tags.log_tags} to \"#{text.strip}\"")
    logger.count(:autotag, level: :info, count: 1, message: 'autotag updated %count %items')
  end

  text.dedup_tags
end;T;&I"def autotag(text);T;'To;;F;
;;;;I"Doing::WWID#tag_times;F;[[I"format:;TI"
:text;T[I"sort_by_name:;TI"
false;T[I"sort_order:;TI"
'asc';T;[[@�i;T;:tag_times;;;[�;{�;IC;"AGet total elapsed time for all tags in
            selection;T;[o;)
;*I"
param;F;+I"(return format (html,
json, or text);T;I"format;T;,[I"String;T;!@Go;)
;*I"
param;F;+I",Sort by name if true, otherwise by time;T;I"sort_by_name;T;,[I"Boolean;T;!@Go;)
;*I"
param;F;+I"!The sort order (asc or desc);T;I"sort_order;T;,[I"String;T;!@G;[�;I",
Get total elapsed time for all tags in
            selection

@param      format        [String] return format (html,
                          json, or text)
@param      sort_by_name  [Boolean] Sort by name if true, otherwise by time
@param      sort_order    [String] The sort order (asc or desc)
;T; 0;!@G;4i�;"T;5o;6;7F;8i;9i;$@�;:T;%I"�
def tag_times(format: :text, sort_by_name: false, sort_order: 'asc')
  return '' if @timers.empty?

  max = @timers.keys.sort_by { |k| k.length }.reverse[0].length + 1

  total = @timers.delete('All')

  tags_data = @timers.delete_if { |_k, v| v == 0 }
  sorted_tags_data = if sort_by_name
                       tags_data.sort_by { |k, _v| k }
                     else
                       tags_data.sort_by { |_k, v| v }
                     end

  sorted_tags_data.reverse! if sort_order =~ /^asc/i
  case format
  when :html

    output = <<EOS
      <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>
EOS
    sorted_tags_data.reverse.each do |k, v|
      if v > 0
        output += "<tr><td style='text-align:left;'>#{k}</td><td style='text-align:left;'>#{'%02d:%02d:%02d' % format_time(v)}</td></tr>\n"
      end
    end
    tail = <<EOS
    <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;">#{'%02d:%02d:%02d' % format_time(total)}</td>
    </tr>
    </tfoot>
    </table>
EOS
    output + tail
  when :markdown
    pad = sorted_tags_data.map {|k, v| k }.group_by(&:size).max.last[0].length
    output = <<~EOS
  | #{' ' * (pad - 7) }project | time     |
  | #{'-' * (pad - 1)}: | :------- |
    EOS
    sorted_tags_data.reverse.each do |k, v|
      if v > 0
        output += "| #{' ' * (pad - k.length)}#{k} | #{'%02d:%02d:%02d' % format_time(v)} |\n"
      end
    end
    tail = "[Tag Totals]"
    output + tail
  when :json
    output = []
    sorted_tags_data.reverse.each do |k, v|
      d, h, m = format_time(v)
      output << {
        'tag' => k,
        'seconds' => v,
        'formatted' => format('%<d>02d:%<h>02d:%<m>02d', d: d, h: h, m: m)
      }
    end
    output
  when :human
    output = []
    sorted_tags_data.reverse.each do |k, v|
      spacer = ''
      (max - k.length).times do
        spacer += ' '
      end
      _d, h, m = format_time(v, human: true)
      output.push("┃ #{spacer}#{k}:#{format('%<h> 4dh %<m>02dm', h: h, m: m)} ┃")
    end

    header = '┏━━ Tag Totals '
    (max - 2).times { header += '━' }
    header += '┓'
    footer = '┗'
    (max + 12).times { footer += '━' }
    footer += '┛'
    divider = '┣'
    (max + 12).times { divider += '━' }
    divider += '┫'
    output = output.empty? ? '' : "\n#{header}\n#{output.join("\n")}"
    d, h, m = format_time(total, human: true)
    output += "\n#{divider}"
    spacer = ''
    (max - 6).times do
      spacer += ' '
    end
    total = "┃ #{spacer}total: "
    total += format('%<h> 4dh %<m>02dm', h: h, m: m)
    total += ' ┃'
    output += "\n#{total}"
    output += "\n#{footer}"
    output
  else
    output = []
    sorted_tags_data.reverse.each do |k, v|
      spacer = ''
      (max - k.length).times do
        spacer += ' '
      end
      d, h, m = format_time(v)
      output.push("#{k}:#{spacer}#{format('%<d>02d:%<h>02d:%<m>02d', d: d, h: h, m: m)}")
    end

    output = output.empty? ? '' : "\n--- Tag Totals ---\n#{output.join("\n")}"
    d, h, m = format_time(total)
    output += "\n\nTotal tracked: #{format('%<d>02d:%<h>02d:%<m>02d', d: d, h: h, m: m)}\n"
    output
  end
end;T;&I"Idef tag_times(format: :text, sort_by_name: false, sort_order: 'asc');T;'To;;F;
;;;;I"Doing::WWID#get_interval;F;[[I"	item;T0[I"formatted:;TI"	true;T[I"record:;TI"	true;T;[[@�i�;T;:get_interval;;;[�;{�;IC;"LGets the interval between entry's start
            date and @done date;T;[	o;)
;*I"
param;F;+I"The entry;T;I"	item;T;,[I"	Item;T;!@po;)
;*I"
param;F;+I"1Return human readable
time (default seconds);T;I"formatted;T;,[I"Boolean;T;!@po;)
;*I"
param;F;+I"/Add the interval to the
total for each tag;T;I"record;T;,[I"Boolean;T;!@po;)
;*I"return;F;+I"hInterval in seconds, or [d, h, m] array if
formatted is true. False if no end date or
interval is 0;T;0;,0;!@p;[�;I"�
Gets the interval between entry's start
            date and @done date

@param      item       [Item] The entry
@param      formatted  [Boolean] Return human readable
                       time (default seconds)
@param      record     [Boolean] Add the interval to the
                       total for each tag

@return     Interval in seconds, or [d, h, m] array if
            formatted is true. False if no end date or
            interval is 0
;T; 0;!@p;4i�;"T;5o;6;7F;8i�;9i�;$@�;:T;%I"=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? ? format('%02d:%02d:%02d', *format_time(seconds)) : false
  end

  false
end;T;&I":def get_interval(item, formatted: true, record: true);T;'To;;F;
;;;;I"Doing::WWID#format_time;F;[[I"seconds;T0[I"human:;TI"
false;T;[[@�i�;T;:format_time;;;[�;{�;IC;",Format human readable time from seconds;T;[o;)
;*I"
param;F;+I"Seconds;T;I"seconds;T;,[I"Integer;T;!@�;[�;I"V
Format human readable time from seconds

@param      seconds  [Integer] Seconds
;T; 0;!@�;4i�;"T;5o;6;7F;8i�;9i�;$@�;:T;%I"1def format_time(seconds, human: false)
  return [0, 0, 0] if seconds.nil?

  if seconds.instance_of?(String) && seconds =~ /(\d+):(\d+):(\d+)/
    h = Regexp.last_match(1)
    m = Regexp.last_match(2)
    s = Regexp.last_match(3)
    seconds = (h.to_i * 60 * 60) + (m.to_i * 60) + s.to_i
  end
  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;T;&I"+def format_time(seconds, human: false);T;'To;;F;
;;;O;I"!Doing::WWID#combined_content;F;[�;[[@�i�;T;:combined_content;;;[�;{�;IC;"OWraps doing file content with additional
            header/footer content;T;[o;)
;*I"return;F;+I"concatenated content;T;0;,[I"String;T;!@�;[�;I"|
Wraps doing file content with additional
            header/footer content

@return     [String] concatenated content
;T; 0;!@�;4i�;"T;5o;6;7F;8i�;9i�;$@�;:T;%I"~def combined_content
  output = @other_content_top ? "#{@other_content_top.join("\n")}\n" : ''
  was_color = Color.coloring?
  Color.coloring = false
  output += @content.to_s
  output += @other_content_bottom.join("\n") unless @other_content_bottom.nil?
  # Just strip all ANSI colors from the content before writing to doing file
  Color.coloring = was_color

  output.uncolor
end;T;&I"def combined_content;T;'To;;F;
;;;O;I"Doing::WWID#output;F;[	[I"
items;T0[I"
title;T0[I"is_single;T0[I"opt;TI"{};T;[[@�i�;T;:output;;;[�;{�;IC;"3Generate output using available export plugins;T;[o;)
;*I"
param;F;+I"The items;T;I"
items;T;,[I"
Array;T;!@�o;)
;*I"
param;F;+I"Page title;T;I"
title;T;,[I"String;T;!@�o;)
;*I"
param;F;+I" Indicates if single
section;T;I"is_single;T;,[I"Boolean;T;!@�o;)
;*I"
param;F;+I"Additional options;T;I"opt;T;,[I"	Hash;T;!@�o;)
;*I"return;F;+I"<formatted output based on opt[:output]
template trigger;T;0;,[I"String;T;!@�o;)
;*I"
raise;F;+@.;0;,[I"InvalidArgument;T;!@�;[�;I"d
Generate output using available export plugins

@param      items      [Array] The items
@param      title      [String] Page title
@param      is_single  [Boolean] Indicates if single
                       section
@param      opt        [Hash] Additional options

@return     [String] formatted output based on opt[:output]
            template trigger
;T; 0;!@�;4i�;"T;5o;6;7F;8i�;9i�;$@�;:T;%I"�def output(items, title, is_single, opt = {})
  out = nil

  raise InvalidArgument, 'Unknown output format' unless opt[:output] =~ Plugins.plugin_regex(type: :export)

  export_options = { page_title: title, is_single: is_single, options: opt }

  Plugins.plugins[:export].each do |_, options|
    next unless opt[:output] =~ /^(#{options[:trigger].normalize_trigger})$/i

    out = options[:class].render(self, items, variables: export_options)
    break
  end

  out
end;T;&I"2def output(items, title, is_single, opt = {});T;'To;;F;
;;;O;I"!Doing::WWID#record_tag_times;F;[[I"	item;T0[I"seconds;T0;[[@�i;T;:record_tag_times;;;[�;{�;IC;"Record times for item tags;T;[o;)
;*I"
param;F;+I"The item to record;T;I"	item;T;,[I"	Item;T;!@�;[�;I"N
Record times for item tags

@param      item  [Item] The item to record
;T; 0;!@�;4i�;"T;5o;6;7F;8i�;9i;$@�;:T;%I"�def record_tag_times(item, seconds)
  item_hash = "#{item.date.strftime('%s')}#{item.title}#{item.section}"
  return if @recorded_items.include?(item_hash)
  item.title.scan(/(?mi)@(\S+?)(\(.*\))?(?=\s|$)/).each do |m|
    k = m[0] == 'done' ? 'All' : m[0].downcase
    if @timers.key?(k)
      @timers[k] += seconds
    else
      @timers[k] = seconds
    end
    @recorded_items.push(item_hash)
  end
end;T;&I"(def record_tag_times(item, seconds);T;'To;;F;
;;;O;I"Doing::WWID#do_archive;F;[[I"section;T0[I"destination;T0[I"opt;TI"{};T;[[@�i;T;:do_archive;;;[�;{�;IC;"3Helper function, performs the actual archiving;T;[o;)
;*I"
param;F;+I"The source section;T;I"section;T;,[I"String;T;!@
o;)
;*I"
param;F;+I"The destination
section;T;I"destination;T;,[I"String;T;!@
o;)
;*I"
param;F;+I"Additional Options;T;I"opt;T;,[I"	Hash;T;!@
;[�;I"�
Helper function, performs the actual archiving

@param      section      [String] The source section
@param      destination  [String] The destination
                         section
@param      opt          [Hash] Additional Options
;T; 0;!@
;4i�;"T;5o;6;7F;8i;9i;$@�;:T;%I"�def do_archive(section, destination, opt = {})
  count = opt[:count] || 0
  tags  = opt[:tags] || []
  bool  = opt[:bool] || :and
  label = opt[:label] || true

  section = guess_section(section)
  destination = guess_section(destination)

  section_items = @content.in_section(section)
  max = section_items.count - count.to_i

  counter = 0

  @content.map! do |item|
    break if counter >= max
    if opt[:before]
      time_string = opt[:before]
      cutoff = time_string.chronify(guess: :begin)
    end

    if (item.section.downcase != section.downcase && section != /^all$/i) || item.section.downcase == destination.downcase
      item
    elsif ((!tags.empty? && !item.tags?(tags, bool)) || (opt[:search] && !item.search(opt[:search].to_s)) || (opt[:before] && item.date >= cutoff))
      item
    else
      counter += 1
      item.move_to(destination, label: label, log: false)
    end
  end

  if counter.positive?
    logger.count(destination == 'Archive' ? :archived : :moved,
                 level: :info,
                 count: counter,
                 message: "%count %items from #{section} to #{destination}")
  else
    logger.info('Skipped:', 'No items were moved')
  end
end;T;&I"3def do_archive(section, destination, opt = {});T;'To;;F;
;;;O;I"Doing::WWID#run_after;F;[�;[[@�iC;F;:run_after;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@>
;4i�;$@�;:T;%I"*def run_after
  return unless @config.key?('run_after')

  _, stderr, status = Open3.capture3(@config['run_after'])
  return unless status.exitstatus.positive?

  logger.log_now(:error, 'Script error:', "Error running #{@config['run_after']}")
  logger.log_now(:error, 'STDERR output:', stderr)
end;T;&I"def run_after;T;'T;V@�;WIC;[�;V@�;XIC;[o;c;d0;e0;f0;:
Color;$@;ho;	;IC;[o:&YARD::CodeObjects::ConstantObject;[[I"lib/doing/colors.rb;Ti
;T;:ATTRIBUTES;;;;;[�;{�;IC;":stopdoc:
;T;[�;[�;I":stopdoc:;T; 0;!@O
;"F;5o;6;7F;8i;9i;$@M
;I"Doing::Color::ATTRIBUTES;F;%I"ZATTRIBUTES = [
  [: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
  [:black, 30],
  [:red, 31],
  [:green, 32],
  [:yellow, 33],
  [:blue, 34],
  [:magenta, 35],
  [:cyan, 36],
  [:white, 37],
  [:bgblack, 40],
  [:bgred, 41],
  [:bggreen, 42],
  [:bgyellow, 43],
  [:bgblue, 44],
  [:bgmagenta, 45],
  [:bgcyan, 46],
  [:bgwhite, 47],
  [:boldblack, 90], # High intensity, aixterm (works in OS X)
  [:boldred, 91],
  [:boldgreen, 92],
  [:boldyellow, 93],
  [:boldblue, 94],
  [:boldmagenta, 95],
  [:boldcyan, 96],
  [:boldwhite, 97],
  [:boldbgblack, 100], # High intensity background, aixterm (works in OS X)
  [:boldbgred, 101],
  [:boldbggreen, 102],
  [:boldbgyellow, 103],
  [:boldbgblue, 104],
  [:boldbgmagenta, 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'],
  [:default, '0;39']
].map(&:freeze).freeze;T:@valueI"M[
  [: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
  [:black, 30],
  [:red, 31],
  [:green, 32],
  [:yellow, 33],
  [:blue, 34],
  [:magenta, 35],
  [:cyan, 36],
  [:white, 37],
  [:bgblack, 40],
  [:bgred, 41],
  [:bggreen, 42],
  [:bgyellow, 43],
  [:bgblue, 44],
  [:bgmagenta, 45],
  [:bgcyan, 46],
  [:bgwhite, 47],
  [:boldblack, 90], # High intensity, aixterm (works in OS X)
  [:boldred, 91],
  [:boldgreen, 92],
  [:boldyellow, 93],
  [:boldblue, 94],
  [:boldmagenta, 95],
  [:boldcyan, 96],
  [:boldwhite, 97],
  [:boldbgblack, 100], # High intensity background, aixterm (works in OS X)
  [:boldbgred, 101],
  [:boldbggreen, 102],
  [:boldbgyellow, 103],
  [:boldbgblue, 104],
  [:boldbgmagenta, 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'],
  [:default, '0;39']
].map(&:freeze).freeze;T;'To;�;[[@R
iC;F;:ATTRIBUTE_NAMES;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@]
;$@M
;I""Doing::Color::ATTRIBUTE_NAMES;F;%I"1ATTRIBUTE_NAMES = ATTRIBUTES.transpose.first;T;�I"ATTRIBUTES.transpose.first;T;'To;;F;
;;;;I"Doing::Color#support?;F;[[I"feature;T0;[[@R
iK;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;,[@0;!@h
;[�;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;!@h
;4i�;"F;5o;6;7F;8iE;9iJ;$@M
;: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;
;[;;;I"Doing::Color.coloring?;F;[�;[[@R
iU;T;:coloring?;;;[�;{�;IC;"[Returns true, if the coloring function of this module
is switched on, false otherwise.;T;[o;)
;*I"return;F;+@.;0;,[@0;!@{
;[�;I"[Returns true, if the coloring function of this module
is switched on, false otherwise.;T; 0;!@{
;4i�;"F;5o;6;7F;8iS;9iT;$@M
;:T;%I""def coloring?
  @coloring
end;T;&I"def coloring?;T;'To;;F;
;[;;;I"Doing::Color.coloring=;F;[[@0;[[@R
i\;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;8iY;9i[;$@M
;%I"1def coloring=(value)
  @coloring = value
end;T;&I"def coloring=(value);T;'To;;F;
;[;;;I"Doing::Color.coloring;F;[�;[[@R
i^;F;:
coloring;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�
;4i�;$@M
;:T;%I"*def coloring
  @coloring ||= true
end;T;&I"def coloring;T;'To;�;[[@R
iy;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;8iw;9ix;$@M
;I"!Doing::Color::COLORED_REGEXP;F;%I"9COLORED_REGEXP = /\e\[(?:(?:[349]|10)[0-7]|[0-9])?m/;T;�I"(/\e\[(?:(?:[349]|10)[0-7]|[0-9])?m/;T;'To;;F;
;;;;I"Doing::Color#uncolor;F;[[I"string;TI"nil;T;[[@R
i};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|;$@M
;: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;
;;;O;I"Doing::Color#attributes;F;[�;[[@R
i�;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;$@M
;:T;%I")def attributes
  ATTRIBUTE_NAMES
end;T;&I"def attributes;T;'To;;T;
;[;;;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�;$@M
;:T;%@�
;&@�
;'T;V@M
;WIC;[@M
;V@M
;XIC;[�;V@M
;YIC;Z{;[IC;Z{;�IC;Z{;]@�
;^@�
;\T;\T;IC;Z{�;\T;\T;_{�;`[�;[[@R
i;T;;�;;;;;[�;{�;IC;"Terminal color functions;T;[�;[�;I"Terminal color functions;T; 0;!@M
;4i�;"F;5o;6;7F;8i
;9i
;$@;I"Doing::Color;F;'T;i:module;V@�;YIC;Z{;[IC;Z{�;\T;IC;Z{
;�IC;Z{;]@�;^0;\T;�IC;Z{;]@�;^0;\T;�IC;Z{;]@�;^0;\T;�IC;Z{;]@�;^0;\T;�IC;Z{;]@�;^@�;\T;�IC;Z{;]@;^@;\T;�IC;Z{;]@%;^@2;\T;�IC;Z{;]@D;^@Q;\T;\T;\T;_{�;`[�;[[@�i;T;:	WWID;;;;;[�;{�;IC;"$Main "What Was I Doing" methods;T;[�;[�;I"&
Main "What Was I Doing" methods
;T; 0;!@�;4i�;"T;5o;6;7F;8i;9i;$@;I"Doing::WWID;F;bo;c;d0;e0;f0;;g;$@;h0;i;[;'To;	;IC;[o;�;[[I"lib/doing/hooks.rb;Ti;F;:DEFAULT_PRIORITY;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@;$@;I"#Doing::Hooks::DEFAULT_PRIORITY;F;%I"DEFAULT_PRIORITY = 20;T;�I"20;T;'To;;F;
;[;;;I"Doing::Hooks.register;F;[[I"
event;T0[I"priority:;TI"DEFAULT_PRIORITY;T[I"&block;T0;[[@	i;T;:
register;;;[�;{�;IC;"4register hook(s) to be called later, public API;T;[�;[�;I"4register hook(s) to be called later, public API;T; 0;!@;4i�;"F;5o;6;7F;8i;9i;$@;:T;%I"}def self.register(event, priority: DEFAULT_PRIORITY, &block)
  register_one(event, priority_value(priority), &block)
end;T;&I"<def register(event, priority: DEFAULT_PRIORITY, &block);T;'To;;F;
;[;;;I" Doing::Hooks.priority_value;F;[[I"
priority;T0;[[@	i;T;:priority_value;;;[�;{�;IC;"$Ensure the priority is a Fixnum;T;[�;[�;I"$Ensure the priority is a Fixnum;T; 0;!@';4i�;"F;5o;6;7F;8i;9i;$@;:T;%I"�def self.priority_value(priority)
  return priority if priority.is_a?(Integer)

  PRIORITY_MAP[priority] || DEFAULT_PRIORITY
end;T;&I"!def priority_value(priority);T;'To;;F;
;[;;;I"Doing::Hooks.register_one;F;[[I"
event;T0[I"
priority;T0[I"&block;T0;[[@	i%;T;:register_one;;;[�;{�;IC;"<register a single hook to be called later, internal API;T;[o;)
;*I"
raise;F;+@.;0;,[I"$Doing::Errors::PluginUncallable;T;!@7;[�;I"<register a single hook to be called later, internal API;T; 0;!@7;4i�;"F;5o;6;7F;8i$;9i$;$@;:T;%I"�def self.register_one(event, priority, &block)
  unless @registry[event]
    raise Doing::Errors::HookUnavailable, "Invalid hook. Doing only supports #{@registry.keys.inspect}"
  end

  raise Doing::Errors::PluginUncallable, 'Hooks must respond to :call' unless block.respond_to? :call

  Doing.logger.debug('Hook Manager:', "Registered #{event} hook") if ENV['DOING_PLUGIN_DEBUG']

  insert_hook event, priority, &block
end;T;&I".def register_one(event, priority, &block);T;'To;;F;
;[;;;I"Doing::Hooks.insert_hook;F;[[I"
event;T0[I"
priority;T0[I"&block;T0;[[@	i1;F;:insert_hook;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@O;4i�;$@;:T;%I"�def self.insert_hook(event, priority, &block)
  @hook_priority[block] = [-priority, @hook_priority.size]
  @registry[event] << block
end;T;&I"-def insert_hook(event, priority, &block);T;'To;;F;
;[;;;I"Doing::Hooks.trigger;F;[[I"
event;T0[I"
*args;T0;[[@	i6;F;:trigger;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@a;4i�;$@;:T;%I"�def self.trigger(event, *args)
  hooks = @registry[event]
  return if hooks.nil? || hooks.empty?

  # 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;T;&I"def trigger(event, *args);T;'T;V@;WIC;[�;V@;XIC;[�;V@;YIC;Z{;[IC;Z{�;\T;IC;Z{�;\T;\T;_{�;`[�;[[@	i
;T;:
Hooks;;;;;[�;{�;IC;"Hook manager;T;[�;[�;I"Hook manager;T; 0;!@;4i�;"F;5o;6;7F;8i	;9i	;$@;I"Doing::Hooks;F;'To;
;IC;[o;;F;
;;;;I"Doing::Items#sections;F;[�;[[I"lib/doing/items.rb;Ti;F;;�;;;[�;{�;IC;"-Returns the value of attribute sections.
;T;[�;[�;I"-Returns the value of attribute sections.;T; 0;!@�;"F;#0;$@�;%I"!def sections
  @sections
end;T;&I"def sections;T;'To;;F;
;;;;I"Doing::Items#sections=;F;[[@0;[[@�i;F;:sections=;;;[�;{�;IC;" Sets the attribute sections
;T;[o;)
;*I"
param;F;+I"0the value to set the attribute sections to.;T;I"
value;T;,0;!@�;[�;I"YSets the attribute sections
@param value the value to set the attribute sections to.;T; 0;!@�;"F;#0;$@�;%I"1def sections=(value)
  @sections = value
end;T;&I"def sections=(value);T;'To;;F;
;;;;I"Doing::Items#initialize;F;[�;[[@�i
;F;;;;;;[�;{�;IC;"�;T;[o;)
;*I"return;F;+I"a new instance of Items;T;0;,[I"
Items;F;!@�;[�;@.; 0;!@�;4i�;$@�;:T;%I"0def initialize
  super
  @sections = []
end;T;&I"def initialize;T;'To;;F;
;;;;I" Doing::Items#section_titles;F;[�;[[@�i;T;:section_titles;;;[�;{�;IC;"List sections, title only;T;[o;)
;*I"return;F;+I"section titles;T;0;,[I"
Array;T;!@�;[�;I"CList sections, title only

@return     [Array] section titles
;T; 0;!@�;4i�;"F;5o;6;7F;8i;9i;$@�;:T;%I"4def section_titles
  @sections.map(&:title)
end;T;&I"def section_titles;T;'To;;F;
;;;;I"Doing::Items#section?;F;[[I"section;T0;[[@�i ;T;:
section?;;;[�;{�;IC;"#Test if section already exists;T;[o;)
;*I"
param;F;+I"section title;T;I"section;T;,[I"String;T;!@�o;)
;*I"return;F;+I"true if section exists;T;0;,[I"Boolean;T;!@�;[�;I"Test if section already exists

@param      section  [String] section title

@return     [Boolean] true if section exists
;T; 0;!@�;4i�;"F;5o;6;7F;8i;9i;$@�;:T;%I"�def section?(section)
  has_section = false
  section = section.is_a?(Section) ? section.title.downcase : section.downcase
  @sections.each do |s|
    if s.title.downcase == section
      has_section = true
      break
    end
  end
  has_section
end;T;&I"def section?(section);T;'To;;F;
;;;;I"Doing::Items#add_section;F;[[I"section;T0[I"	log:;TI"
false;T;[[@�i9;T;:add_section;;;[�;{�;IC;"�Add a new section to the sections array. Accepts
either a Section object, or a title string that will
be converted into a Section.;T;[o;)
;*I"
param;F;+I"SThe section to add. A
String value will be converted to
Section automatically.;T;I"section;T;,[I"Section;T;!@�o;)
;*I"
param;F;+I"LAdd a log message
notifying the user about the
creation of the section.;T;I"log;T;,[I"Boolean;T;!@�o;)
;*I"return;F;+I"nothing;T;0;,0;!@�;[�;I"�Add a new section to the sections array. Accepts
either a Section object, or a title string that will
be converted into a Section.

@param      section  [Section] The section to add. A
                     String value will be converted to
                     Section automatically.
@param      log      [Boolean] Add a log message
                     notifying the user about the
                     creation of the section.

@return     nothing
;T; 0;!@�;4i�;"F;5o;6;7F;8i,;9i8;$@�;:T;%I"�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;T;&I")def add_section(section, log: false);T;'To;;F;
;;;;I"Doing::Items#in_section;F;[[I"section;T0;[[@�iI;T;:in_section;;;[�;{�;IC;"HGet a new Items object containing only items in a
specified section;T;[o;)
;*I"
param;F;+I"section title;T;I"section;T;,[I"String;T;!@o;)
;*I"return;F;+I"Array of items;T;0;,[I"
Items;T;!@;[�;I"�Get a new Items object containing only items in a
specified section

@param      section  [String] section title

@return     [Items] Array of items
;T; 0;!@;4i�;"F;5o;6;7F;8iB;9iH;$@�;:T;%I"�def in_section(section)
  if section =~ /^all$/i
    dup
  else
    items = Items.new.concat(select { |item| item.section == section })
    items.add_section(section, log: false)
    items
  end
end;T;&I"def in_section(section);T;'To;;F;
;;;;I"Doing::Items#delete_item;F;[[I"	item;T0[I"single:;TI"
false;T;[[@�iX;T;:delete_item;;;[�;{�;IC;""Delete an item from the index;T;[o;)
;*I"
param;F;+I"
The item;T;I"	item;T;,0;!@ ;[�;I"@
Delete an item from the index

@param      item  The item
;T; 0;!@ ;4i�;"T;5o;6;7F;8iS;9iW;$@�;:T;%I"�def delete_item(item, single: false)
  deleted = delete(item)
  Doing.logger.count(:deleted)
  Doing.logger.info('Entry deleted:', deleted.title) if single
end;T;&I")def delete_item(item, single: false);T;'To;;F;
;;;;I"Doing::Items#update_item;F;[[I"
old_item;T0[I"
new_item;T0;[[@�id;T;:update_item;;;[�;{�;IC;"5Update an item in the index with a modified item;T;[o;)
;*I"
param;F;+I"The old item;T;I"
old_item;T;,0;!@7o;)
;*I"
param;F;+I"The new item;T;I"
new_item;T;,0;!@7o;)
;*I"
raise;F;+@.;0;,[I"ItemNotFound;T;!@7;[�;I"~
Update an item in the index with a modified item

@param      old_item  The old item
@param      new_item  The new item
;T; 0;!@7;4i�;"T;5o;6;7F;8i^;9ic;$@�;:T;%I"`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.truncate(60))
  new_item
end;T;&I"(def update_item(old_item, new_item);T;'To;;F;
;;;;I"Doing::Items#to_s;F;[�;[[@�ir;T;;M;;;[�;{�;IC;"3Output sections and items in Doing file format;T;[�;[�;I"3Output sections and items in Doing file format;T; 0;!@U;4i�;"F;5o;6;7F;8iq;9iq;$@�;:T;%I"�def to_s
  out = []
  @sections.each do |section|
    out.push(section.original)
    in_section(section.title).each { |item| out.push(item.to_s)}
  end

  out.join("\n")
end;T;&I"
def to_s;T;'To;;F;
;;;;I"Doing::Items#inspect;F;[�;[[@�i};T;;N;;;[�;{�;IC;"�;T;[o;)
;*I"private;F;+I"�;T;0;,0;!@c;[�;I"
@private;T; 0;!@c;4i�;"F;5o;6;7F;8i|;9i|;$@�;:T;%I"�def inspect
  "#<Doing::Items #{count} items, #{@sections.count} sections: #{@sections.map { |s| "<Section:#{s.title} #{in_section(s.title).count} items>" }.join(', ')}>"
end;T;&I"def inspect;T;'T;V@�;WIC;[�;V@�;XIC;[�;V@�;YIC;Z{;[IC;Z{�;\T;IC;Z{;�IC;Z{;]@�;^@�;\T;\T;\T;_{�;`[�;[[@�i
;T;:
Items;;;;;[�;{�;IC;"Items Array;T;[�;[�;I"Items Array;T; 0;!@�;4i�;"F;5o;6;7F;8i	;9i	;$@;I"Doing::Items;F;bo;c;d0;e0;f0;;r;$@;h@�;i0;'To;	;IC;[o;;F;
;[;;;I"Doing::Pager.paginate;F;[�;[[I"lib/doing/pager.rb;Ti;T;:
paginate;;;[�;{�;IC;"3Boolean determines whether output is paginated;T;[�;[�;I"3Boolean determines whether output is paginated;T; 0;!@�;4i�;"F;5o;6;7F;8i
;9i
;$@�;:T;%I"+def paginate
  @paginate ||= false
end;T;&I"def paginate;T;'To;;F;
;[;;;I"Doing::Pager.paginate=;F;[[I"should_paginate;T0;[[@�i;T;:paginate=;;;[�;{�;IC;"Enable/disable pagination;T;[o;)
;*I"
param;F;+I"true to paginate;T;I"should_paginate;T;,[I"Boolean;T;!@�;[�;I"WEnable/disable pagination

@param      should_paginate  [Boolean] true to paginate;T; 0;!@�;4i�;"F;5o;6;7F;8i;9i;$@�;:T;%I"Edef paginate=(should_paginate)
  @paginate = should_paginate
end;T;&I"#def paginate=(should_paginate);T;'To;;F;
;[;;;I"Doing::Pager.page;F;[[I"	text;T0;[[@�i;T;:	page;;;[�;{�;IC;"<Page output. If @paginate is false, just dump to
STDOUT;T;[o;)
;*I"
param;F;+I"text to paginate;T;I"	text;T;,[I"String;T;!@�;[�;I"jPage output. If @paginate is false, just dump to
STDOUT

@param      text  [String] text to paginate
;T; 0;!@�;4i�;"F;5o;6;7F;8i;9i;$@�;:T;%I"�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;T;&I"def page(text);T;'To;;F;
;[;;O;I" Doing::Pager.command_exist?;F;[[I"command;T0;[[@�iH;F;:command_exist?;;;[�;{�;IC;"�;T;[o;)
;*I"return;F;+@.;0;,[@0;!@�;[�;@.; 0;!@�;4i�;$@�;:T;%I"�def command_exist?(command)
  exts = ENV.fetch("PATHEXT", "").split(::File::PATH_SEPARATOR)
  if Pathname.new(command).absolute?
    ::File.exist?(command) ||
      exts.any? { |ext| ::File.exist?("#{command}#{ext}")}
  else
    ENV.fetch("PATH", "").split(::File::PATH_SEPARATOR).any? do |dir|
      file = ::File.join(dir, command)
      ::File.exist?(file) ||
        exts.any? { |ext| ::File.exist?("#{file}#{ext}") }
    end
  end
end;T;&I" def command_exist?(command);T;'To;;F;
;[;;O;I"Doing::Pager.git_pager;F;[�;[[@�iV;F;:git_pager;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@�;:T;%I"Xdef git_pager
  command_exist?("git") ? `git config --get-all core.pager` : nil
end;T;&I"def git_pager;T;'To;;F;
;[;;O;I"Doing::Pager.pagers;F;[�;[[@�iZ;F;:pagers;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@�;:T;%I"{def pagers
  [ENV['GIT_PAGER'], ENV['PAGER'], git_pager,
   'bat -p --pager="less -Xr"', 'less -Xr', 'more -r'].compact
end;T;&I"def pagers;T;'To;;F;
;[;;O;I"!Doing::Pager.find_executable;F;[[I"*commands;T0;[[@�i_;F;:find_executable;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@�;:T;%I"�def find_executable(*commands)
  execs = commands.empty? ? pagers : commands
  execs
    .compact.map(&:strip).reject(&:empty?).uniq
    .find { |cmd| command_exist?(cmd.split.first) }
end;T;&I"#def find_executable(*commands);T;'To;;F;
;[;;O;I"!Doing::Pager.exec_available?;F;[[I"*commands;T0;[[@�if;F;:exec_available?;;;[�;{�;IC;"�;T;[o;)
;*I"return;F;+@.;0;,[@0;!@�;[�;@.; 0;!@�;4i�;$@�;:T;%I"Jdef exec_available?(*commands)
  !find_executable(*commands).nil?
end;T;&I"#def exec_available?(*commands);T;'To;;F;
;[;;O;I"Doing::Pager.which_pager;F;[�;[[@�ij;F;:which_pager;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@;4i�;$@�;:T;%I"Ddef which_pager
  @which_pager ||= find_executable(*pagers)
end;T;&I"def which_pager;T;'T;V@�;WIC;[�;V@�;XIC;[�;V@�;YIC;Z{;[IC;Z{�;\T;IC;Z{�;\T;\T;_{�;`[�;[[@�i;T;:
Pager;;;;;[�;{�;IC;"Pagination;T;[�;[�;I"Pagination;T; 0;!@�;4i�;"F;5o;6;7F;8i
;9i
;$@;I"Doing::Pager;F;'T@M
o;	;IC;[o;
;IC;[o;;F;
;;;;I",Doing::Errors::UserCancelled#initialize;F;[[I"msg;TI"'Cancelled';T[I"
topic;TI"'Exited:';T;[[I"lib/doing/errors.rb;Ti;F;;;;;;[�;{�;IC;"�;T;[o;)
;*I"return;F;+I"$a new instance of UserCancelled;T;0;,[I"UserCancelled;F;!@-;[�;@.; 0;!@-;4i�;$@+;:T;%I"�def initialize(msg = 'Cancelled', topic = 'Exited:')
  Doing.logger.output_results
  Doing.logger.log_now(:warn, topic, msg)
  Process.exit 1
end;T;&I"9def initialize(msg = 'Cancelled', topic = 'Exited:');T;'T;V@+;WIC;[�;V@+;XIC;[�;V@+;YIC;Z{;[IC;Z{�;\T;IC;Z{�;\T;\T;_{�;`[�;[[@8i
;F;:UserCancelled;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@+;4i�;$@);I"!Doing::Errors::UserCancelled;F;bo;c;d0;e0;f0;:StandardError;$@;h0;i;[;'To;
;IC;[o;;F;
;;;;I")Doing::Errors::EmptyInput#initialize;F;[[I"msg;TI"'No input';T[I"
topic;TI"'Exited:';T;[[@8i;F;;;;;;[�;{�;IC;"�;T;[o;)
;*I"return;F;+I"!a new instance of EmptyInput;T;0;,[I"EmptyInput;F;!@W;[�;@.; 0;!@W;4i�;$@U;:T;%I"�def initialize(msg = 'No input', topic = 'Exited:')
  Doing.logger.output_results
  Doing.logger.log_now(:warn, topic, msg)
  Process.exit 1
end;T;&I"8def initialize(msg = 'No input', topic = 'Exited:');T;'T;V@U;WIC;[�;V@U;XIC;[�;V@U;YIC;Z{;[IC;Z{�;\T;IC;Z{�;\T;\T;_{�;`[�;[[@8i;F;:EmptyInput;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@U;4i�;$@);I"Doing::Errors::EmptyInput;F;bo;c;d0;e0;f0;;�;$@;h0;i;[;'To;
;IC;[o;;F;
;;;;I"1Doing::Errors::DoingStandardError#initialize;F;[[I"msg;TI"'';T;[[@8i;F;;;;;;[�;{�;IC;"�;T;[o;)
;*I"return;F;+I")a new instance of DoingStandardError;T;0;,[I"DoingStandardError;F;!@�;[�;@.; 0;!@�;4i�;$@~;:T;%I"Hdef initialize(msg = '')
  Doing.logger.output_results

  super
end;T;&I"def initialize(msg = '');T;'T;V@~;WIC;[�;V@~;XIC;[�;V@~;YIC;Z{;[IC;Z{�;\T;IC;Z{�;\T;\T;_{�;`[�;[[@8i;F;:DoingStandardError;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@~;4i�;$@);I"&Doing::Errors::DoingStandardError;F;bo;c;d0;e0;f0;;�;$@;h0;i;[;'To;
;IC;[o;;F;
;;;;I"+Doing::Errors::WrongCommand#initialize;F;[[I"msg;TI"'wrong command';T[I"topic:;TI"
'Error:';T;[[@8i#;F;;;;;;[�;{�;IC;"�;T;[o;)
;*I"return;F;+I"#a new instance of WrongCommand;T;0;,[I"WrongCommand;F;!@�;[�;@.; 0;!@�;4i�;$@�;:T;%I"mdef initialize(msg = 'wrong command', topic: 'Error:')
  Doing.logger.warn(topic, msg)

  super(msg)
end;T;&I";def initialize(msg = 'wrong command', topic: 'Error:');T;'T;V@�;WIC;[�;V@�;XIC;[�;V@�;YIC;Z{;[IC;Z{�;\T;IC;Z{�;\T;\T;_{�;`[�;[[@8i";F;:WrongCommand;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@);I" Doing::Errors::WrongCommand;F;bo;c;d0;e0;f0;;�;$@;h0;i;[;'To;
;IC;[o;;F;
;;;;I"0Doing::Errors::DoingRuntimeError#initialize;F;[[I"msg;TI"'Runtime Error';T[I"topic:;TI"
'Error:';T;[[@8i+;F;;;;;;[�;{�;IC;"�;T;[o;)
;*I"return;F;+I"(a new instance of DoingRuntimeError;T;0;,[I"DoingRuntimeError;F;!@�;[�;@.; 0;!@�;4i�;$@�;:T;%I"�def initialize(msg = 'Runtime Error', topic: 'Error:')
  Doing.logger.output_results
  Doing.logger.log_now(:error, topic, msg)
  Process.exit 1
end;T;&I";def initialize(msg = 'Runtime Error', topic: 'Error:');T;'T;V@�;WIC;[�;V@�;XIC;[�;V@�;YIC;Z{;[IC;Z{�;\T;IC;Z{�;\T;\T;_{�;`[�;[[@8i*;F;:DoingRuntimeError;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@);I"%Doing::Errors::DoingRuntimeError;F;bo;c;d0;e0;f0;:RuntimeError;$@;h0;i;[;'To;
;IC;[o;;F;
;;;;I"(Doing::Errors::NoResults#initialize;F;[[I"msg;TI"'No results';T[I"
topic;TI"'Exited:';T;[[@8i3;F;;;;;;[�;{�;IC;"�;T;[o;)
;*I"return;F;+I" a new instance of NoResults;T;0;,[I"NoResults;F;!@�;[�;@.; 0;!@�;4i�;$@�;:T;%I"�def initialize(msg = 'No results', topic = 'Exited:')
  Doing.logger.output_results
  Doing.logger.log_now(:warn, topic, msg)
  Process.exit 0

end;T;&I":def initialize(msg = 'No results', topic = 'Exited:');T;'T;V@�;WIC;[�;V@�;XIC;[�;V@�;YIC;Z{;[IC;Z{�;\T;IC;Z{�;\T;\T;_{�;`[�;[[@8i2;F;:NoResults;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@);I"Doing::Errors::NoResults;F;bo;c;d0;e0;f0;;�;$@;h0;i;[;'To;
;IC;[o;;F;
;;;;I"0Doing::Errors::DoingNoTraceError#initialize;F;[[I"msg;TI"nil;T[I"
level;TI"nil;T[I"
topic;TI"nil;T;[[@8i<;F;;;;;;[�;{�;IC;"�;T;[o;)
;*I"return;F;+I"(a new instance of DoingNoTraceError;T;0;,[I"DoingNoTraceError;F;!@!;[�;@.; 0;!@!;4i�;$@;:T;%I"�def initialize(msg = nil, level = nil, topic = nil)
  level ||= :error
  Doing.logger.output_results
  if msg
    Doing.logger.log_now(level, topic, msg)
  end

  Process.exit 1
end;T;&I"8def initialize(msg = nil, level = nil, topic = nil);T;'T;V@;WIC;[�;V@;XIC;[�;V@;YIC;Z{;[IC;Z{�;\T;IC;Z{�;\T;\T;_{�;`[�;[[@8i;;F;:DoingNoTraceError;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@;4i�;$@);I"%Doing::Errors::DoingNoTraceError;F;bo;c;d0;e0;f0;;�;$@;h0;i;[;'To;
;IC;[o;;F;
;;;;I"*Doing::Errors::PluginException#plugin;F;[�;[[@8iH;F;:plugin;;;[�;{�;IC;"+Returns the value of attribute plugin.
;T;[�;[�;I"+Returns the value of attribute plugin.;T; 0;!@M;"F;#0;$@K;%I"def plugin
  @plugin
end;T;&I"def plugin;T;'To;;F;
;;;;I".Doing::Errors::PluginException#initialize;F;[[I"msg;TI"'Plugin error';T[I"	type;TI"nil;T[I"plugin;TI"nil;T;[[@8iJ;F;;;;;;[�;{�;IC;"�;T;[o;)
;*I"return;F;+I"&a new instance of PluginException;T;0;,[I"PluginException;F;!@Z;[�;@.; 0;!@Z;4i�;$@K;:T;%I"�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'
          else
            type.to_s
          end

  msg = "(#{@type}: #{@plugin}) #{msg}"

  Doing.logger.log_now(:error, 'Plugin:', msg)
  Process.exit 1
end;T;&I"Cdef initialize(msg = 'Plugin error', type = nil, plugin = nil);T;'T;V@K;WIC;[�;V@K;XIC;[�;V@K;YIC;Z{;[IC;Z{�;\T;IC;Z{;�IC;Z{;]@M;^0;\T;\T;\T;_{�;`[�;[[@8iG;F;:PluginException;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@K;4i�;$@);I"#Doing::Errors::PluginException;F;bo;c;d0;e0;f0;;�;$@;h0;i;[;'To;�;[[@8i^;F;:HookUnavailable;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;$@);I"#Doing::Errors::HookUnavailable;F;%I"1HookUnavailable = Class.new(PluginException);T;�I"Class.new(PluginException);T;'To;�;[[@8i_;F;:InvalidPluginType;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;$@);I"%Doing::Errors::InvalidPluginType;F;%I"3InvalidPluginType = Class.new(PluginException);T;�I"Class.new(PluginException);T;'To;�;[[@8i`;F;:PluginUncallable;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;$@);I"$Doing::Errors::PluginUncallable;F;%I"2PluginUncallable = Class.new(PluginException);T;�I"Class.new(PluginException);T;'To;�;[[@8ib;F;:InvalidArgument;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;$@);I"#Doing::Errors::InvalidArgument;F;%I"3InvalidArgument = Class.new(DoingRuntimeError);T;�I"!Class.new(DoingRuntimeError);T;'To;�;[[@8ic;F;:MissingArgument;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;$@);I"#Doing::Errors::MissingArgument;F;%I"3MissingArgument = Class.new(DoingRuntimeError);T;�I"!Class.new(DoingRuntimeError);T;'To;�;[[@8id;F;:MissingFile;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;$@);I"Doing::Errors::MissingFile;F;%I"/MissingFile = Class.new(DoingRuntimeError);T;�I"!Class.new(DoingRuntimeError);T;'To;�;[[@8ie;F;:MissingEditor;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;$@);I"!Doing::Errors::MissingEditor;F;%I"1MissingEditor = Class.new(DoingRuntimeError);T;�I"!Class.new(DoingRuntimeError);T;'To;�;[[@8if;F;:NonInteractive;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;$@);I""Doing::Errors::NonInteractive;F;%I".NonInteractive = Class.new(StandardError);T;�I"Class.new(StandardError);T;'To;�;[[@8ih;F;:NoEntryError;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;$@);I" Doing::Errors::NoEntryError;F;%I"0NoEntryError = Class.new(DoingRuntimeError);T;�I"!Class.new(DoingRuntimeError);T;'To;�;[[@8ij;F;:InvalidTimeExpression;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;$@);I")Doing::Errors::InvalidTimeExpression;F;%I"9InvalidTimeExpression = Class.new(DoingRuntimeError);T;�I"!Class.new(DoingRuntimeError);T;'To;�;[[@8ik;F;:InvalidSection;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;$@);I""Doing::Errors::InvalidSection;F;%I"2InvalidSection = Class.new(DoingRuntimeError);T;�I"!Class.new(DoingRuntimeError);T;'To;�;[[@8il;F;:InvalidView;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;$@);I"Doing::Errors::InvalidView;F;%I"/InvalidView = Class.new(DoingRuntimeError);T;�I"!Class.new(DoingRuntimeError);T;'To;�;[[@8in;F;:ItemNotFound;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@	;$@);I" Doing::Errors::ItemNotFound;F;%I"0ItemNotFound = Class.new(DoingRuntimeError);T;�I"!Class.new(DoingRuntimeError);T;'T;V@);WIC;[�;V@);XIC;[�;V@);YIC;Z{;[IC;Z{�;\T;IC;Z{�;\T;\T;_{�;`[�;[[@8i	;F;:Errors;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@);4i�;$@;I"Doing::Errors;F;'To;	;IC;[o;;F;
;[;;;I" Doing::Prompt.force_answer=;F;[[@0;[[I"lib/doing/prompt.rb;Ti;F;:force_answer=;;;[�;{�;IC;"$Sets the attribute force_answer
;T;[o;)
;*I"
param;F;+I"4the value to set the attribute force_answer to.;T;I"
value;T;,0;!@%;[�;I"aSets the attribute force_answer
@param value the value to set the attribute force_answer to.;T; 0;!@%;"F;#0;$@#;%I"9def force_answer=(value)
  @force_answer = value
end;T;&I"def force_answer=(value);T;'To;;F;
;[;;;I""Doing::Prompt.default_answer=;F;[[@0;[[@+i;F;:default_answer=;;;[�;{�;IC;"&Sets the attribute default_answer
;T;[o;)
;*I"
param;F;+I"6the value to set the attribute default_answer to.;T;I"
value;T;,0;!@8;[�;I"eSets the attribute default_answer
@param value the value to set the attribute default_answer to.;T; 0;!@8;"F;#0;$@#;%I"=def default_answer=(value)
  @default_answer = value
end;T;&I"def default_answer=(value);T;'To;;F;
;[;;;I"Doing::Prompt.force_answer;F;[�;[[@+i;F;:force_answer;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@J;4i�;$@#;:T;%I"1def force_answer
  @force_answer ||= nil
end;T;&I"def force_answer;T;'To;;F;
;[;;;I"!Doing::Prompt.default_answer;F;[�;[[@+i;F;:default_answer;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@V;4i�;$@#;:T;%I"7def default_answer
  @default_answer ||= false
end;T;&I"def default_answer;T;'To;;F;
;[;;;I"Doing::Prompt.yn;F;[[I"
question;T0[I"default_response:;TI"
false;T;[[@+i";T;:yn;;;[�;{�;IC;"-Ask a yes or no question in the terminal;T;[o;)
;*I"
param;F;+I"The question
to ask;T;I"
question;T;,[I"String;T;!@bo;)
;*I"
param;F;+I"!default
response if no input;T;I"default_response;T;,[I"	Bool;T;!@bo;)
;*I"return;F;+I"yes or no;T;0;,[I"	Bool;T;!@b;[�;I"
Ask a yes or no question in the terminal

@param      question          [String] The question
                              to ask
@param      default_response  (Bool)   default
                              response if no input

@return     (Bool) yes or no
;T; 0;!@b;4i�;"T;5o;6;7F;8i;9i!;$@#;:T;%I"5def yn(question, default_response: false)
  unless @force_answer.nil?
    return @force_answer
  end

  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

  options = 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(/\?$/, '')} #{options}#{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;T;&I".def yn(question, default_response: false);T;'To;;F;
;[;;;I"Doing::Prompt.fzf;F;[�;[[@+iR;F;:fzf;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@#;:T;%I"'def fzf
  @fzf ||= install_fzf
end;T;&I"def fzf;T;'To;;F;
;[;;;I"Doing::Prompt.install_fzf;F;[�;[[@+iV;F;:install_fzf;;;[�;{�;IC;"�;T;[o;)
;*I"
raise;F;+@.;0;,[I"RuntimeError;T;!@�;[�;@.; 0;!@�;4i�;$@#;:T;%I"�def install_fzf
  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, 'Compiling and installing fzf -- this will only happen once')
  Doing.logger.log_now(:warn, 'fzf is copyright Junegunn Choi, MIT License <https://github.com/junegunn/fzf/blob/master/LICENSE>')

  system("'#{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)
    Doing.logger.log_now(:warn, 'Error installing, trying again as root')
    system("sudo '#{fzf_dir}/install' --bin --no-key-bindings --no-completion --no-update-rc --no-bash --no-zsh --no-fish &> /dev/null")
  end
  raise RuntimeError.new('Error installing fzf, please report at https://github.com/ttscoff/doing/issues') unless File.exist?(fzf_bin)

  Doing.logger.info("fzf installed to #{fzf}")
  Doing.logger.adjust_verbosity({ log_level: prev_level })
  fzf_bin
end;T;&I"def install_fzf;T;'To;;F;
;[;;;I"Doing::Prompt.choose_from;F;[
[I"options;T0[I"prompt:;TI"'Make a selection: ';T[I"multiple:;TI"
false;T[I"sorted:;TI"	true;T[I"fzf_args:;TI"[];T;[[@+ir;T;:choose_from;;;[�;{�;IC;"8Generate a menu of options and allow user selection;T;[o;)
;*I"return;F;+I"The selected option;T;0;,[I"String;T;!@�;[�;I"d
Generate a menu of options and allow user selection

@return     [String] The selected option
;T; 0;!@�;4i�;"T;5o;6;7F;8im;9iq;$@#;:T;%I"�def choose_from(options, 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 seves as confirmation
  fzf_args << %(--prompt="#{prompt}")
  fzf_args << "--height=#{options.count + 2}"
  fzf_args << '--info=inline'
  fzf_args << '--multi' if multiple
  header = "esc: cancel,#{multiple ? ' tab: multi-select, ctrl-a: select all,' : ''} return: confirm"
  fzf_args << %(--header="#{header}")
  options.sort! if sorted
  res = `echo #{Shellwords.escape(options.join("\n"))}|#{fzf} #{fzf_args.join(' ')}`
  return false if res.strip.size.zero?

  res
end;T;&I"hdef choose_from(options, prompt: 'Make a selection: ', multiple: false, sorted: true, fzf_args: []);T;'To;;F;
;[;;;I"$Doing::Prompt.choose_from_items;F;[[I"
items;T0[I"
**opt;T0;[[@+i�;T;:choose_from_items;;;[�;{�;IC;"=Create an interactive menu to select from a set of Items;T;[o;)
;*I"
param;F;+I"list of items;T;I"
items;T;,[I"
Array;T;!@�o;)
;*I"
param;F;+I"options;T;I"opt;T;,[I"	Hash;T;!@�o;)
;*I"
param;F;+I"include section;T;I"include_section;T;,[I"Boolean;T;!@�o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+0;I":header;T;,[I"String;T;F0;!@�o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+0;I":prompt;T;,[I"String;T;F0;!@�o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+0;I":query;T;,[I"String;T;F0;!@�o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+0;I":show_if_single;T;,[I"Boolean;T;F0;!@�o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+0;I"
:menu;T;,[I"Boolean;T;F0;!@�o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+0;I"
:sort;T;,[I"Boolean;T;F0;!@�o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+0;I":multiple;T;,[I"Boolean;T;F0;!@�o;C;*I"option;F;+0;I"opt;T;,0;Do;E
;*I"option;F;+I"�;T;I"
:case;T;,[I"Symbol;T;F[I":sensitive;TI":ignore;TI":smart;T;!@�;[�;I"�
Create an interactive menu to select from a set of Items

@param      items            [Array] list of items
@param      opt              [Hash] options
@param      include_section  [Boolean] include section

@option opt [String] :header
@option opt [String] :prompt
@option opt [String] :query
@option opt [Boolean] :show_if_single
@option opt [Boolean] :menu
@option opt [Boolean] :sort
@option opt [Boolean] :multiple
@option opt [Symbol] :case (:sensitive, :ignore, :smart)
;T; 0;!@�;4i�;"T;5o;6;7F;8i~;9i�;$@#;:T;%I"�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
  options = items.map.with_index do |item, i|
    out = [
      format("%#{pad}d", i),
      ') ',
      format('%13s', item.date.relative_date),
      ' | ',
      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)
  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(options.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;T;&I"(def choose_from_items(items, **opt);T;'T;V@#;WIC;[o;c;d0;e0;f0;;�;$@;h@M
;i;�;V@#;XIC;[�;V@#;YIC;Z{;[IC;Z{;IC;Z{;]@J;^@%;\T;IC;Z{;]@V;^@8;\T;\T;IC;Z{�;\T;\T;_{�;`[�;[[@+i
;T;:Prompt;;;;;[�;{�;IC;"Terminal Prompt methods;T;[�;[�;I"Terminal Prompt methods;T; 0;!@#;4i�;"F;5o;6;7F;8i	;9i	;$@;I"Doing::Prompt;F;'To;
;IC;[o;;F;
;;;;I"Doing::Section#original;F;[�;[[I"lib/doing/section.rb;Ti;F;:
original;;;[�;{�;IC;"-Returns the value of attribute original.
;T;[�;[�;I"-Returns the value of attribute original.;T; 0;!@B;"F;#0;$@@;%I"!def original
  @original
end;T;&I"def original;T;'To;;F;
;;;;I"Doing::Section#original=;F;[[@0;[[@Gi;F;:original=;;;[�;{�;IC;" Sets the attribute original
;T;[o;)
;*I"
param;F;+I"0the value to set the attribute original to.;T;I"
value;T;,0;!@P;[�;I"YSets the attribute original
@param value the value to set the attribute original to.;T; 0;!@P;"F;#0;$@@;%I"1def original=(value)
  @original = value
end;T;&I"def original=(value);T;'To;;F;
;;;;I"Doing::Section#title;F;[�;[[@Gi;F;;-;;;[�;{�;IC;"*Returns the value of attribute title.
;T;[�;[�;I"*Returns the value of attribute title.;T; 0;!@b;"F;#0;$@@;%I"def title
  @title
end;T;&I"def title;T;'To;;F;
;;;;I"Doing::Section#title=;F;[[@0;[[@Gi;F;;.;;;[�;{�;IC;"Sets the attribute title
;T;[o;)
;*I"
param;F;+I"-the value to set the attribute title to.;T;I"
value;T;,0;!@o;[�;I"SSets the attribute title
@param value the value to set the attribute title to.;T; 0;!@o;"F;#0;$@@;%I"+def title=(value)
  @title = value
end;T;&I"def title=(value);T;'To;;F;
;;;;I"Doing::Section#initialize;F;[[I"
title;T0[I"original:;TI"nil;T;[[@Gi
;F;;;;;;[�;{�;IC;"�;T;[o;)
;*I"return;F;+I"a new instance of Section;T;0;,[I"Section;F;!@�;[�;@.; 0;!@�;4i�;$@@;:T;%I"�def initialize(title, original: nil)
  super()

  @title = title

  @original = if original.nil?
                "#{title}:"
              else
                original =~ /:(\s+@\S+(\(.*?\))?)*$/ ? original : "#{original}:"
              end
end;T;&I")def initialize(title, original: nil);T;'To;;F;
;;;;I"Doing::Section#to_s;F;[�;[[@Gi;T;;M;;;[�;{�;IC;"Outputs section title;T;[�;[�;I"Outputs section title;T; 0;!@�;4i�;"F;5o;6;7F;8i;9i;$@@;:T;%I"def to_s
  @title
end;T;&I"
def to_s;T;'To;;F;
;;;;I"Doing::Section#inspect;F;[�;[[@Gi;T;;N;;;[�;{�;IC;"�;T;[o;)
;*I"private;F;+I"�;T;0;,0;!@�;[�;I"
@private;T; 0;!@�;4i�;"F;5o;6;7F;8i;9i;$@@;:T;%I"Wdef inspect
  %(#<Doing::Section @title="#{@title}" @original="#{@original}">)
end;T;&I"def inspect;T;'T;V@@;WIC;[�;V@@;XIC;[�;V@@;YIC;Z{;[IC;Z{�;\T;IC;Z{;
IC;Z{;]@B;^@P;\T;-IC;Z{;]@b;^@o;\T;\T;\T;_{�;`[�;[[@Gi
;T;:Section;;;;;[�;{�;IC;"Section Object;T;[�;[�;I"Section Object;T; 0;!@@;4i�;"F;5o;6;7F;8i	;9i	;$@;I"Doing::Section;F;bo;c;d0;e0;f0;;g;$@;h0;i;[;'To;�;[[I"lib/doing/version.rb;Ti;F;:VERSION;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;$@;I"Doing::VERSION;F;%I"VERSION = '2.1.2pre';T;�I"'2.1.2pre';T;'To;	;IC;[o;;F;
;[;;;I"*Doing::Completion.generate_completion;F;[[I"
type:;TI"
'zsh';T[I"
file:;TI"
'stdout';T;[[I"lib/doing/completion.rb;Ti;T;:generate_completion;;;[�;{�;IC;">Generate a completion script and output to file or
stdout;T;[o;)
;*I"
param;F;+I"*shell to generate for (zsh|bash|fish);T;I"	type;T;,[I"String;T;!@�o;)
;*I"
param;F;+I"!Path to save to, or 'stdout';T;I"	file;T;,[I"String;T;!@�;[�;I"�Generate a completion script and output to file or
stdout

@param      type  [String] shell to generate for (zsh|bash|fish)
@param      file  [String] Path to save to, or 'stdout'
;T; 0;!@�;4i�;"F;5o;6;7F;8i;9i;$@�;:T;%I"def generate_completion(type: 'zsh', file: 'stdout')

  generator = case type.to_s
              when /^f/
                FishCompletions.new
              when /^b/
                BashCompletions.new
              else
                ZshCompletions.new
              end

  result = generator.generate_completions

  if file =~ /^stdout$/i
    $stdout.puts result
  else
    File.open(File.expand_path(file), 'w') do |f|
      f.puts result
    end
    Doing.logger.warn('File written:', "#{type} completions written to #{file}")
  end
end;T;&I"9def generate_completion(type: 'zsh', file: 'stdout');T;'T;V@�;WIC;[�;V@�;XIC;[�;V@�;YIC;Z{;[IC;Z{�;\T;IC;Z{�;\T;\T;_{�;`[�;[[@�i;T;:Completion;;;;;[�;{�;IC;" Completion script generator;T;[�;[�;I" Completion script generator;T; 0;!@�;4i�;"F;5o;6;7F;8i;9i;$@;I"Doing::Completion;F;'To;
;IC;["o;;F;
;;;;I"Doing::LogAdapter#logdev=;F;[[@0;[[I"lib/doing/log_adapter.rb;Ti;T;:logdev=;;;[�;{�;IC;"Sets the log device
;T;[�;[�;I"Sets the log device;T; 0;!@;"F;5o;6;7F;8i
;9i
;$@
;%I"-def logdev=(value)
  @logdev = value
end;T;&I"def logdev=(value);T;'To;;F;
;;;;I""Doing::LogAdapter#max_length=;F;[[@0;[[@i;T;:max_length=;;;[�;{�;IC;"4Max length of log messages (truncate in middle)
;T;[�;[�;I"4Max length of log messages (truncate in middle);T; 0;!@;"F;5o;6;7F;8i;9i;$@
;%I"5def max_length=(value)
  @max_length = value
end;T;&I"def max_length=(value);T;'To;;F;
;;;;I"Doing::LogAdapter#level;F;[�;[[@i;T;:
level;;;[�;{�;IC;"=Returns the current log level (debug, info, warn, error)
;T;[�;[�;I"=Returns the current log level (debug, info, warn, error);T; 0;!@+;"F;5o;6;7F;8i;9i;$@
;%I"def level
  @level
end;T;&I"def level;T;'To;;F;
;;;;I"Doing::LogAdapter#messages;F;[�;[[@i;F;:
messages;;;[�;{�;IC;"-Returns the value of attribute messages.
;T;[�;[�;I"-Returns the value of attribute messages.;T; 0;!@9;"F;#0;$@
;%I"!def messages
  @messages
end;T;&I"def messages;T;'To;;F;
;;;;I"Doing::LogAdapter#results;F;[�;[[@i;F;:results;;;[�;{�;IC;",Returns the value of attribute results.
;T;[�;[�;I",Returns the value of attribute results.;T; 0;!@F;"F;#0;$@
;%I"def results
  @results
end;T;&I"def results;T;'To;�;[[@i;F;:TOPIC_WIDTH;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@S;$@
;I"#Doing::LogAdapter::TOPIC_WIDTH;F;%I"TOPIC_WIDTH = 12;T;�I"12;T;'To;�;[[@i;F;:LOG_LEVELS;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@^;$@
;I""Doing::LogAdapter::LOG_LEVELS;F;%I"LOG_LEVELS = {
  debug: ::Logger::DEBUG,
  info: ::Logger::INFO,
  warn: ::Logger::WARN,
  error: ::Logger::ERROR
}.freeze;T;�I"r{
  debug: ::Logger::DEBUG,
  info: ::Logger::INFO,
  warn: ::Logger::WARN,
  error: ::Logger::ERROR
}.freeze;T;'To;�;[[@i!;F;:COUNT_KEYS;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@i;$@
;I""Doing::LogAdapter::COUNT_KEYS;F;%I"�COUNT_KEYS = %i[
  added
  added_tags
  archived
  autotag
  completed
  completed_archived
  deleted
  moved
  removed_tags
  rotated
  skipped
  updated
].freeze;T;�I"�%i[
  added
  added_tags
  archived
  autotag
  completed
  completed_archived
  deleted
  moved
  removed_tags
  rotated
  skipped
  updated
].freeze;T;'To;;F;
;;;;I"!Doing::LogAdapter#initialize;F;[[I"
level;TI"
:info;T;[[@i5;T;;;;;;[�;{�;IC;"*Create a new instance of a log writer;T;[o;)
;*I"
param;F;+I"the log level;T;I"
level;T;,[I"
optional;TI"symbol;T;!@to;)
;*I"return;F;+I"!a new instance of LogAdapter;T;0;,[I"LogAdapter;F;!@t;[�;I"b
Create a new instance of a log writer

@param      level   (optional, symbol) the log level
;T; 0;!@t;4i�;"F;5o;6;7F;8i0;9i4;$@
;:T;%I"
def initialize(level = :info)
  @messages = []
  @counters = {}
  COUNT_KEYS.each { |key| @counters[key] = { tag: [], count: 0 } }
  @results = []
  @logdev = $stderr
  @max_length = `tput cols`.strip.to_i - 5 || 85
  self.log_level = level
  @prev_level = level
end;T;&I""def initialize(level = :info);T;'To;;F;
;;;;I"!Doing::LogAdapter#log_level=;F;[[I"
level;TI"'info';T;[[@iG;T;:log_level=;;;[�;{�;IC;"$Set the log level on the writer;T;[o;)
;*I"
param;F;+I"the log level;T;I"
level;T;,[I"symbol;T;!@�o;)
;*I"return;F;+I"nothing;T;0;,0;!@�;[�;I"f
Set the log level on the writer

@param      level  (symbol) the log level

@return     nothing
;T; 0;!@�;4i�;"F;5o;6;7F;8i@;9iF;$@
;:T;%I"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;T;&I"#def log_level=(level = 'info');T;'To;;F;
;;;;I"!Doing::LogAdapter#temp_level;F;[[I"
level;T0;[[@iY;T;:temp_level;;;[�;{�;IC;"Set log level temporarily;T;[�;[�;I"Set log level temporarily;T; 0;!@�;4i�;"F;5o;6;7F;8iX;9iX;$@
;:T;%I"�def temp_level(level)
  return if level.nil? || level.to_sym == @log_level

  @prev_level = log_level.dup
  @log_level = level.to_sym
end;T;&I"def temp_level(level);T;'To;;F;
;;;;I"$Doing::LogAdapter#restore_level;F;[�;[[@ia;T;:restore_level;;;[�;{�;IC;"Restore temporary level;T;[�;[�;I"Restore temporary level;T; 0;!@�;4i�;"F;5o;6;7F;8i`;9i`;$@
;:T;%I"�def restore_level
  return if @prev_level.nil? || @prev_level == @log_level

  self.log_level = @prev_level
  @prev_level = nil
end;T;&I"def restore_level;T;'To;;F;
;;;;I"'Doing::LogAdapter#adjust_verbosity;F;[[I"options;TI"{};T;[[@ih;F;:adjust_verbosity;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@
;:T;%I"Zdef adjust_verbosity(options = {})
  if options[:log_level]
    self.log_level = options[:log_level].to_sym
  elsif options[:quiet]
    self.log_level = :error
  elsif options[:verbose] || options[:debug]
    self.log_level = :debug
  end
  log_now :debug, 'Logging at level:', @level.to_s
  # log_now :debug, 'Doing Version:', Doing::VERSION
end;T;&I"'def adjust_verbosity(options = {});T;'To;;F;
;;;;I"Doing::LogAdapter#count;F;[
[I"key;T0[I"level:;TI"
:info;T[I"count:;TI"1;T[I"	tag:;TI"nil;T[I"
message:;TI"nil;T;[[@it;F;:
count;;;[�;{�;IC;"�;T;[o;)
;*I"
raise;F;+@.;0;,[I"ArgumentError;T;!@�;[�;@.; 0;!@�;4i�;$@
;:T;%I"9def 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] ||= message
end;T;&I"Cdef count(key, level: :info, count: 1, tag: nil, message: nil);T;'To;;F;
;;;;I"Doing::LogAdapter#debug;F;[[I"
topic;T0[I"message;TI"nil;T[I"&block;T0;[[@i�;T;:
debug;;;[�;{�;IC;"Print a debug message;T;[o;)
;*I"
param;F;+I"the topic of the message;T;I"
topic;T;,0;!@�o;)
;*I"
param;F;+I"the message detail;T;I"message;T;,0;!@�o;)
;*I"return;F;+I"nothing;T;0;,0;!@�;[�;I"�
Print a debug message

@param      topic    the topic of the message
@param      message  the message detail

@return     nothing
;T; 0;!@�;4i�;"F;5o;6;7F;8i};9i;$@
;:T;%I"Xdef debug(topic, message = nil, &block)
  write(:debug, topic, message, &block)
end;T;&I",def debug(topic, message = nil, &block);T;'To;;F;
;;;;I"Doing::LogAdapter#info;F;[[I"
topic;T0[I"message;TI"nil;T[I"&block;T0;[[@i�;T;:	info;;;[�;{�;IC;"Print a message;T;[o;)
;*I"
param;F;+I"Mthe topic of the message, e.g.
"Configuration file",
"Deprecation", etc.;T;I"
topic;T;,0;!@o;)
;*I"
param;F;+I"the message detail;T;I"message;T;,0;!@o;)
;*I"return;F;+I"nothing;T;0;,0;!@;[�;I"�
Print a message

@param      topic    the topic of the message, e.g.
                     "Configuration file",
                     "Deprecation", etc.
@param      message  the message detail

@return     nothing
;T; 0;!@;4i�;"F;5o;6;7F;8i�;9i�;$@
;:T;%I"Vdef info(topic, message = nil, &block)
  write(:info, topic, message, &block)
end;T;&I"+def info(topic, message = nil, &block);T;'To;;F;
;;;;I"Doing::LogAdapter#warn;F;[[I"
topic;T0[I"message;TI"nil;T[I"&block;T0;[[@i�;T;:	warn;;;[�;{�;IC;"Print a message;T;[o;)
;*I"
param;F;+I"Mthe topic of the message, e.g.
"Configuration file",
"Deprecation", etc.;T;I"
topic;T;,0;!@6o;)
;*I"
param;F;+I"the message detail;T;I"message;T;,0;!@6o;)
;*I"return;F;+I"nothing;T;0;,0;!@6;[�;I"�
Print a message

@param      topic    the topic of the message, e.g.
                     "Configuration file",
                     "Deprecation", etc.
@param      message  the message detail

@return     nothing
;T; 0;!@6;4i�;"F;5o;6;7F;8i�;9i�;$@
;:T;%I"Vdef warn(topic, message = nil, &block)
  write(:warn, topic, message, &block)
end;T;&I"+def warn(topic, message = nil, &block);T;'To;;F;
;;;;I"Doing::LogAdapter#error;F;[[I"
topic;T0[I"message;TI"nil;T[I"&block;T0;[[@i�;T;:
error;;;[�;{�;IC;"Print an error message;T;[o;)
;*I"
param;F;+I"Mthe topic of the message, e.g.
"Configuration file",
"Deprecation", etc.;T;I"
topic;T;,0;!@Vo;)
;*I"
param;F;+I"the message detail;T;I"message;T;,0;!@Vo;)
;*I"return;F;+I"nothing;T;0;,0;!@V;[�;I"�
Print an error message

@param      topic    the topic of the message, e.g.
                     "Configuration file",
                     "Deprecation", etc.
@param      message  the message detail

@return     nothing
;T; 0;!@V;4i�;"F;5o;6;7F;8i�;9i�;$@
;:T;%I"Xdef error(topic, message = nil, &block)
  write(:error, topic, message, &block)
end;T;&I",def error(topic, message = nil, &block);T;'To;;F;
;;;;I"!Doing::LogAdapter#abort_with;F;[[I"
topic;T0[I"message;TI"nil;T[I"&block;T0;[[@i�;T;:abort_with;;;[�;{�;IC;"IPrint an error message and immediately
            abort the process;T;[o;)
;*I"
param;F;+I"Mthe topic of the message, e.g.
"Configuration file",
"Deprecation", etc.;T;I"
topic;T;,0;!@vo;)
;*I"
param;F;+I"(the message detail (can be
omitted);T;I"message;T;,0;!@vo;)
;*I"return;F;+I"nothing;T;0;,0;!@v;[�;I"2
Print an error message and immediately
            abort the process

@param      topic    the topic of the message, e.g.
                     "Configuration file",
                     "Deprecation", etc.
@param      message  the message detail (can be
                     omitted)

@return     nothing
;T; 0;!@v;4i�;"F;5o;6;7F;8i�;9i�;$@
;:T;%I"]def abort_with(topic, message = nil, &block)
  error(topic, message, &block)
  abort
end;T;&I"1def abort_with(topic, message = nil, &block);T;'To;;F;
;;;;I"&Doing::LogAdapter#formatted_topic;F;[[I"
topic;T0[I"colon:;TI"
false;T;[[@i�;T;:formatted_topic;;;[�;{�;IC;"Format the topic;T;[o;)
;*I"
param;F;+I"Mthe topic of the message, e.g.
"Configuration file",
"Deprecation", etc.;T;I"
topic;T;,0;!@�o;)
;*I"
param;F;+I"Separate with a colon?;T;I"
colon;T;,0;!@�o;)
;*I"return;F;+I""the formatted topic statement;T;0;,0;!@�;[�;I"�
Format the topic

@param      topic  the topic of the message, e.g.
                   "Configuration file",
                   "Deprecation", etc.
@param      colon  Separate with a colon?

@return     the formatted topic statement
;T; 0;!@�;4i�;"F;5o;6;7F;8i�;9i�;$@
;:T;%I"�def formatted_topic(topic, colon: false)
  if colon
    "#{topic}: ".rjust(TOPIC_WIDTH)
  elsif topic =~ /:$/
    "#{topic} ".rjust(TOPIC_WIDTH)
  else
    "#{topic} "
  end
end;T;&I"-def formatted_topic(topic, colon: false);T;'To;;F;
;;;;I"Doing::LogAdapter#write;F;[	[I"level_of_message;T0[I"
topic;T0[I"message;TI"nil;T[I"&block;T0;[[@i�;T;;^;;;[�;{�;IC;"Log a message.;T;[
o;)
;*I"
param;F;+I"Ethe Symbol
level of message, one of
:debug, :info, :warn,
:error;T;I"level_of_message;T;,[I"Symbol;T;!@�o;)
;*I"
param;F;+I"%the String
topic or full message;T;I"
topic;T;,[I"String;T;!@�o;)
;*I"
param;F;+I""the String
message (optional);T;I"message;T;,[I"String;T;!@�o;)
;*I"
param;F;+I".a block containing the
message (optional);T;I"
block;T;,0;!@�o;)
;*I"return;F;+I")false if the message was not written;T;0;,[I"Boolean;T;!@�;[�;I">
Log a message.

@param      level_of_message  [Symbol] the Symbol
                              level of message, one of
                              :debug, :info, :warn,
                              :error
@param      topic             [String] the String
                              topic or full message
@param      message           [String] the String
                              message (optional)
@param      block             a block containing the
                              message (optional)

@return     [Boolean] false if the message was not written
;T; 0;!@�;4i�;"F;5o;6;7F;8i�;9i�;$@
;:T;%I"�def write(level_of_message, topic, message = nil, &block)
  @results << { level: level_of_message, message: message(topic, message, &block) }
  true
end;T;&I">def write(level_of_message, topic, message = nil, &block);T;'To;;F;
;;;;I"Doing::LogAdapter#log_now;F;[	[I"
level;T0[I"
topic;T0[I"message;TI"nil;T[I"&block;T0;[[@i�;T;:log_now;;;[�;{�;IC;"CLog to console immediately instead of writing messages on exit;T;[	o;)
;*I"
param;F;+I"The level;T;I"
level;T;,[I"Symbol;T;!@�o;)
;*I"
param;F;+I"The topic or full message;T;I"
topic;T;,[I"String;T;!@�o;)
;*I"
param;F;+I"The message (optional);T;I"message;T;,[I"String;T;!@�o;)
;*I"
param;F;+I".a block containing the message (optional);T;I"
block;T;,0;!@�;[�;I"
Log to console immediately instead of writing messages on exit

@param      level    [Symbol] The level
@param      topic    [String] The topic or full message
@param      message  [String] The message (optional)
@param      block    a block containing the message (optional)
;T; 0;!@�;4i�;"T;5o;6;7F;8i�;9i�;$@
;:T;%I"�def log_now(level, topic, message = nil, &block)
  return false unless write_message?(level)

  if @logdev == $stdout
    @logdev.puts message(topic, message, &block)
  else
    @logdev.puts color_message(level, topic, message, &block)
  end
end;T;&I"5def log_now(level, topic, message = nil, &block);T;'To;;F;
;;;;I"%Doing::LogAdapter#output_results;F;[�;[[@i�;T;:output_results;;;[�;{�;IC;"(Output registers based on log level;T;[o;)
;*I"return;F;+I"nothing;T;0;,0;!@;[�;I"?
Output registers based on log level

@return     nothing
;T; 0;!@;4i�;"T;5o;6;7F;8i�;9i�;$@
;:T;%I";def output_results
  total_counters
  results = @results.select { |msg| write_message?(msg[:level]) }.uniq

  if @logdev == $stdout
    $stdout.print results.map {|res| res[:message].uncolor }.join("\n")
  else
    results.each do |msg|
      @logdev.puts color_message(msg[:level], msg[:message])
    end
  end
end;T;&I"def output_results;T;'To;;F;
;;;;I"!Doing::LogAdapter#log_change;F;[
[I"tags_added:;TI"[];T[I"tags_removed:;TI"[];T[I"count:;TI"1;T[I"
item:;TI"nil;T[I"single:;TI"
false;T;[[@i;F;:log_change;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@$;4i�;$@
;:T;%I"def log_change(tags_added: [], tags_removed: [], count: 1, item: nil, single: false)
  if tags_added.empty? && tags_removed.empty?
    count(:skipped, level: :debug, message: '%count %items with no change', count: count)
  else
    if tags_added.empty?
      count(:skipped, level: :debug, message: 'no tags added to %count %items')
    elsif single && item
      added = tags_added.log_tags
      info('Tagged:',
           %(added #{tags_added.count == 1 ? 'tag' : 'tags'} #{added} to #{item.title}))
    else
      count(:added_tags, level: :info, tag: tags_added, message: '%tags added to %count %items')
    end

    if tags_removed.empty?
      count(:skipped, level: :debug, message: 'no tags removed from %count %items')
    elsif single && item
      added = tags_added.log_tags
      info('Untagged:',
           %(removed #{tags_removed.count == 1 ? 'tag' : 'tags'} #{added} from #{item.title}))
    else
      count(:removed_tags, level: :info, tag: tags_removed, message: '%tags removed from %count %items')
    end
  end
end;T;&I"Ydef log_change(tags_added: [], tags_removed: [], count: 1, item: nil, single: false);T;'To;;F;
;;;O;I"%Doing::LogAdapter#format_counter;F;[[I"key;T0[I"	data;T0;[[@i(;F;:format_counter;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@?;4i�;$@
;:T;%I"def format_counter(key, data)
  case key
  when :rotated
    ['Rotated:', data[:message] || 'rotated %count %items']
  when :autotag
    ['Autotag:', data[:message] || 'autotagged %count %items']
  when :added_tags
    ['Tagged:', data[:message] || 'added %tags to %count %items']
  when :removed_tags
    ['Untagged:', data[:message] || 'removed %tags from %count %items']
  when :added
    ['Added:', data[:message] || 'added %count new %items']
  when :updated
    ['Updated:', data[:message] || 'updated %count %items']
  when :deleted
    ['Deleted:', data[:message] || 'deleted %count %items']
  when :moved
    ['Moved:', data[:message] || 'moved %count %items']
  when :completed
    ['Completed:', data[:message] || 'completed %count %items']
  when :archived
    ['Archived:',  data[:message] || 'archived %count %items']
  when :completed_archived
    ['Archived:',  data[:message] || 'completed and archived %count %items']
  when :skipped
    ['Skipped:', data[:message] || '%count %items were unchanged']
  end
end;T;&I""def format_counter(key, data);T;'To;;F;
;;;O;I"%Doing::LogAdapter#total_counters;F;[�;[[@iE;F;:total_counters;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@O;4i�;$@
;:T;%I"�def total_counters
  @counters.each do |key, data|
    next if data[:count].zero?

    count = data[:count]
    tags = data[:tag] ? data[:tag].uniq.map { |t| "@#{t}".cyan }.join(', ') : 'tags'
    topic, m = format_counter(key, data)
    message = m.dup
    message.sub!(/%count/, count.to_s)
    message.sub!(/%items/, count == 1 ? 'item' : 'items')
    message.sub!(/%tags/, tags)
    write(data[:level], topic, message)
  end
end;T;&I"def total_counters;T;'To;;F;
;;;O;I"%Doing::LogAdapter#write_message?;F;[[I"level_of_message;T0;[[@i^;T;:write_message?;;;[�;{�;IC;"LCheck if the message should be written
            given the log level.;T;[o;)
;*I"
param;F;+I"Ethe Symbol level of
message, one of :debug,
:info, :warn, :error;T;I"level_of_message;T;,0;!@[o;)
;*I"return;F;+I"+whether the message should be written.;T;0;,[@0;!@[;[�;I"
Check if the message should be written
            given the log level.

@param      level_of_message  the Symbol level of
                              message, one of :debug,
                              :info, :warn, :error

@return     whether the message should be written.
;T; 0;!@[;4i�;"F;5o;6;7F;8iT;9i];$@
;:T;%I"ndef write_message?(level_of_message)
  LOG_LEVELS.fetch(@level) <= LOG_LEVELS.fetch(level_of_message)
end;T;&I")def write_message?(level_of_message);T;'To;;F;
;;;O;I"Doing::LogAdapter#message;F;[[I"
topic;T0[I"message;TI"nil;T;[[@ik;T;:message;;;[�;{�;IC;"#Internal: Build a topic method;T;[	o;)
;*I"
param;F;+I"Mthe topic of the message, e.g.
"Configuration file",
"Deprecation", etc.;T;I"
topic;T;,0;!@so;)
;*I"
param;F;+I"the message detail;T;I"message;T;,0;!@so;)
;*I"return;F;+I"the formatted message;T;0;,0;!@so;)
;*I"
raise;F;+@.;0;,[I"ArgumentError;T;!@s;[�;I"�Internal: Build a topic method

@param      topic    the topic of the message, e.g.
                     "Configuration file",
                     "Deprecation", etc.
@param      message  the message detail

@return     the formatted message
;T; 0;!@s;4i�;"F;5o;6;7F;8ib;9ij;$@
;:T;%I"�def message(topic, message = nil)
  raise ArgumentError, 'block or message, not both' if block_given? && message

  message = yield if block_given?
  message = message.to_s.gsub(/\s+/, ' ')

  return topic.ljust(TOPIC_WIDTH) if topic && message.strip.empty?

  topic = formatted_topic(topic, colon: block_given?)
  message.truncmiddle!(@max_length - TOPIC_WIDTH - 5)
  out = topic + message
  out.truncate!(@max_length) if @max_length.positive?
  messages << out
  out
end;T;&I"&def message(topic, message = nil);T;'To;;F;
;;;O;I"$Doing::LogAdapter#color_message;F;[	[I"
level;T0[I"
topic;T0[I"message;TI"nil;T[I"&block;T0;[[@i{;F;:color_message;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@
;:T;%I"def color_message(level, topic, message = nil, &block)
  colors = Doing::Color
  message = message(topic, message, &block)
  prefix = '  '
  topic_fg = colors.boldcyan
  message_fg = colors.boldwhite

  case level
  when :debug
    prefix = '> '.softpurple
    topic_fg = colors.softpurple
    message_fg = colors.white
  when :warn
    prefix = '> '.boldyellow
    topic_fg = colors.boldyellow
    message_fg = colors.yellow
  when :error
    prefix = '!!'.boldred
    topic_fg = colors.flamingo
    message_fg = colors.red
  end

  message.sub!(/^(\s*\S.*?): (.*?)$/) do
    m = Regexp.last_match
    msg = m[2] =~ /(\e\[[\d;]+m)/ ? msg : "#{message_fg}#{m[2]}"

    "#{topic_fg}#{m[1]}#{colors.reset}: #{message_fg}#{m[2]}"
  end

  "#{prefix} #{message.highlight_tags}#{colors.reset}"
end;T;&I";def color_message(level, topic, message = nil, &block);T;'T;V@
;WIC;[�;V@
;XIC;[�;V@
;YIC;Z{;[IC;Z{�;\T;IC;Z{
:logdevIC;Z{;]0;^@;\T:max_lengthIC;Z{;]0;^@;\T;IC;Z{;]@+;^0;\T;IC;Z{;]@9;^0;\T;IC;Z{;]@F;^0;\T;\T;\T;_{�;`[�;[[@i;T;:LogAdapter;;;;;[�;{�;IC;"Log adapter;T;[�;[�;I"
Log adapter
;T; 0;!@
;4i�;"T;5o;6;7F;8i	;9i;$@;I"Doing::LogAdapter;F;bo;c;d0;e0;f0;;g;$@;h0;i;[;'To;
;IC;[o;;F;
;;;;I""Doing::Configuration#settings;F;[�;[[I"lib/doing/configuration.rb;Ti
;F;:
settings;;;[�;{�;IC;"-Returns the value of attribute settings.
;T;[�;[�;I"-Returns the value of attribute settings.;T; 0;!@�;"F;#0;$@�;%I"!def settings
  @settings
end;T;&I"def settings;T;'To;;F;
;;;;I"'Doing::Configuration#ignore_local=;F;[[@0;[[@�i;F;:ignore_local=;;;[�;{�;IC;"$Sets the attribute ignore_local
;T;[o;)
;*I"
param;F;+I"4the value to set the attribute ignore_local to.;T;I"
value;T;,0;!@�;[�;I"aSets the attribute ignore_local
@param value the value to set the attribute ignore_local to.;T; 0;!@�;"F;#0;$@�;%I"9def ignore_local=(value)
  @ignore_local = value
end;T;&I"def ignore_local=(value);T;'To;�;[[@�i;F;:MissingConfigFile;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;$@�;I",Doing::Configuration::MissingConfigFile;F;%I"0MissingConfigFile = Class.new(RuntimeError);T;�I"Class.new(RuntimeError);T;'To;�;[[@�i;F;:
DEFAULTS;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;$@�;I"#Doing::Configuration::DEFAULTS;F;%I"�DEFAULTS = {
  'autotag' => {
    'whitelist' => [],
    'synonyms' => {}
  },
  'editors' => {
    'default' => ENV['DOING_EDITOR'] || ENV['GIT_EDITOR'] || ENV['EDITOR'],
    'doing_file' => nil,
    'config' => nil
  },
  'plugins' => {
    'plugin_path' => File.join(Util.user_home, '.config', 'doing', 'plugins'),
    'command_path' => File.join(Util.user_home, '.config', 'doing', 'commands')
  },
  'doing_file' => '~/what_was_i_doing.md',
  'current_section' => 'Currently',
  'paginate' => false,
  'never_time' => [],
  'never_finish' => [],

  'timer_format' => 'text',
  'interval_format' => 'text',

  'templates' => {
    'default' => {
      'date_format' => '%Y-%m-%d %H:%M',
      'template' => '%date | %title %interval%duration%note',
      'wrap_width' => 0,
      'order' => 'asc'
    },
    'today' => {
      'date_format' => '%_I:%M%P',
      'template' => '%date: %title %interval%duration%note',
      'wrap_width' => 0,
      'order' => 'asc'
    },
    'last' => {
      'date_format' => '%-I:%M%P on %a',
      'template' => '%title (at %date) %interval%duration%odnote',
      'wrap_width' => 88
    },
    'recent' => {
      'date_format' => '%_I:%M%P',
      'template' => '%shortdate: %title (%section) %interval%duration%note',
      'wrap_width' => 88,
      'count' => 10,
      'order' => 'asc'
    }
  },

  'export_templates' => {},

  'views' => {
    'done' => {
      'date_format' => '%_I:%M%P',
      'template' => '%date | %title%note',
      'wrap_width' => 0,
      'section' => 'All',
      'count' => 0,
      'order' => 'desc',
      'tags' => 'done complete cancelled',
      'tags_bool' => 'OR'
    },
    'color' => {
      'date_format' => '%F %_I:%M%P',
      'template' => '%boldblack%date %boldgreen| %boldwhite%title%default%note',
      'wrap_width' => 0,
      'section' => 'Currently',
      'count' => 10,
      'order' => 'asc'
    }
  },
  'marker_tag' => 'flagged',
  'marker_color' => 'red',
  'default_tags' => [],
  'tag_sort' => 'name',
  'include_notes' => true
};T;�I"�{
  'autotag' => {
    'whitelist' => [],
    'synonyms' => {}
  },
  'editors' => {
    'default' => ENV['DOING_EDITOR'] || ENV['GIT_EDITOR'] || ENV['EDITOR'],
    'doing_file' => nil,
    'config' => nil
  },
  'plugins' => {
    'plugin_path' => File.join(Util.user_home, '.config', 'doing', 'plugins'),
    'command_path' => File.join(Util.user_home, '.config', 'doing', 'commands')
  },
  'doing_file' => '~/what_was_i_doing.md',
  'current_section' => 'Currently',
  'paginate' => false,
  'never_time' => [],
  'never_finish' => [],

  'timer_format' => 'text',
  'interval_format' => 'text',

  'templates' => {
    'default' => {
      'date_format' => '%Y-%m-%d %H:%M',
      'template' => '%date | %title %interval%duration%note',
      'wrap_width' => 0,
      'order' => 'asc'
    },
    'today' => {
      'date_format' => '%_I:%M%P',
      'template' => '%date: %title %interval%duration%note',
      'wrap_width' => 0,
      'order' => 'asc'
    },
    'last' => {
      'date_format' => '%-I:%M%P on %a',
      'template' => '%title (at %date) %interval%duration%odnote',
      'wrap_width' => 88
    },
    'recent' => {
      'date_format' => '%_I:%M%P',
      'template' => '%shortdate: %title (%section) %interval%duration%note',
      'wrap_width' => 88,
      'count' => 10,
      'order' => 'asc'
    }
  },

  'export_templates' => {},

  'views' => {
    'done' => {
      'date_format' => '%_I:%M%P',
      'template' => '%date | %title%note',
      'wrap_width' => 0,
      'section' => 'All',
      'count' => 0,
      'order' => 'desc',
      'tags' => 'done complete cancelled',
      'tags_bool' => 'OR'
    },
    'color' => {
      'date_format' => '%F %_I:%M%P',
      'template' => '%boldblack%date %boldgreen| %boldwhite%title%default%note',
      'wrap_width' => 0,
      'section' => 'Currently',
      'count' => 10,
      'order' => 'asc'
    }
  },
  'marker_tag' => 'flagged',
  'marker_color' => 'red',
  'default_tags' => [],
  'tag_sort' => 'name',
  'include_notes' => true
};T;'To;;F;
;;;;I"$Doing::Configuration#initialize;F;[[I"	file;TI"nil;T[I"
options:;TI"{};T;[[@�ib;F;;;;;;[�;{�;IC;"�;T;[o;)
;*I"return;F;+I"$a new instance of Configuration;T;0;,[I"Configuration;F;!@�;[�;@.; 0;!@�;4i�;$@�;:T;%I"�def initialize(file = nil, options: {})
  @config_file = file.nil? ? default_config_file : File.expand_path(file)

  @settings = configure(options)
end;T;&I",def initialize(file = nil, options: {});T;'To;;F;
;;;;I"%Doing::Configuration#config_file;F;[�;[[@�ih;F;;�;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@;4i�;$@�;:T;%I"?def config_file
  @config_file ||= default_config_file
end;T;&I"def config_file;T;'To;;F;
;;;;I"&Doing::Configuration#config_file=;F;[[I"	file;T0;[[@�il;F;;�;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@;4i�;$@�;:T;%I"5def config_file=(file)
  @config_file = file
end;T;&I"def config_file=(file);T;'To;;F;
;;;;I"$Doing::Configuration#config_dir;F;[�;[[@�ip;F;:config_dir;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@*;4i�;$@�;:T;%I"zdef config_dir
  @config_dir ||= File.join(Util.user_home, '.config', 'doing')
  # @config_dir ||= Util.user_home
end;T;&I"def config_dir;T;'To;;F;
;;;;I"-Doing::Configuration#default_config_file;F;[�;[[@�iu;F;:default_config_file;;;[�;{�;IC;"�;T;[o;)
;*I"
raise;F;+@.;0;,[I"DoingRuntimeError;T;!@6;[�;@.; 0;!@6;4i�;$@�;:T;%I"�def default_config_file
  raise DoingRuntimeError, "#{config_dir} exists but is not a directory" if File.exist?(config_dir) && !File.directory?(config_dir)

  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')
  File.join(config_dir, 'config.yml')
end;T;&I"def default_config_file;T;'To;;F;
;;;;I",Doing::Configuration#additional_configs;F;[�;[[@�i|;F;;�;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@F;4i�;$@�;:T;%I"Kdef additional_configs
  @additional_configs ||= find_local_config
end;T;&I"def additional_configs;T;'To;;F;
;;;;I"'Doing::Configuration#choose_config;F;[�;[[@�i�;T;:choose_config;;;[�;{�;IC;"7Present a menu if there are multiple configs found;T;[o;)
;*I"return;F;+I"file path;T;0;,[I"String;T;!@R;[�;I"Y
Present a menu if there are multiple configs found

@return     [String] file path
;T; 0;!@R;4i�;"T;5o;6;7F;8i�;9i�;$@�;:T;%I"ydef choose_config
  if @additional_configs.count.positive?
    choices = [@config_file]
    choices.concat(@additional_configs)
    res = Doing::Prompt.choose_from(choices.uniq.sort.reverse, sorted: false, prompt: 'Local configs found, select which to update > ')

    raise UserCancelled, 'Cancelled' unless res

    res.strip || @config_file
  else
    @config_file
  end
end;T;&I"def choose_config;T;'To;;F;
;;;;I"*Doing::Configuration#resolve_key_path;F;[[I"keypath;T0[I"create:;TI"
false;T;[[@�i�;T;:resolve_key_path;;;[�;{�;IC;"%Resolve a fuzzy-matched key path;T;[o;)
;*I"
param;F;+I"|A dot-separated key
path, e.g.
"plugins.plugin_path". Will also
work with "plug.path" (fuzzy
matched, first match wins);T;I"keypath;T;,[I"String;T;!@eo;)
;*I"return;F;+I"#ordered array of resolved keys;T;0;,[I"
Array;T;!@e;[�;I"@
Resolve a fuzzy-matched key path

@param      keypath  [String] A dot-separated key
                     path, e.g.
                     "plugins.plugin_path". Will also
                     work with "plug.path" (fuzzy
                     matched, first match wins)
@return     [Array] ordered array of resolved keys
;T; 0;!@e;4i�;"T;5o;6;7F;8i�;9i�;$@�;:T;%I"�def resolve_key_path(keypath, create: false)
  cfg = @settings
  real_path = []
  unless keypath =~ /^[.*]?$/
    paths = keypath.split(/[:.]/)
    while paths.length.positive? && !cfg.nil?
      path = paths.shift
      new_cfg = nil
      cfg.each do |key, val|
        next unless key =~ path.to_rx(distance: 4)

        real_path << key
        new_cfg = val
        break
      end

      if new_cfg.nil?
        return nil unless create

        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].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)
        Doing.logger.debug('Config:', "translated key path #{keypath} to #{real_path.join('.')}")
        return real_path
      end
      cfg = new_cfg
    end
  end
  Doing.logger.debug('Config:', "translated key path #{keypath} to #{real_path.join('.')}")
  real_path
end;T;&I"1def resolve_key_path(keypath, create: false);T;'To;;F;
;;;;I"'Doing::Configuration#value_for_key;F;[[I"keypath;TI"'';T;[[@�i�;T;:value_for_key;;;[�;{�;IC;"/Get the value for a fuzzy-matched key path;T;[o;)
;*I"
param;F;+I"|A dot-separated key
path, e.g.
"plugins.plugin_path". Will also
work with "plug.path" (fuzzy
matched, first match wins);T;I"keypath;T;,[I"String;T;!@�o;)
;*I"return;F;+I"Config value;T;0;,[I"	Hash;T;!@�;[�;I"7
Get the value for a fuzzy-matched key path

@param      keypath  [String] A dot-separated key
                     path, e.g.
                     "plugins.plugin_path". Will also
                     work with "plug.path" (fuzzy
                     matched, first match wins)
@return     [Hash] Config value
;T; 0;!@�;4i�;"T;5o;6;7F;8i�;9i�;$@�;:T;%I")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;T;&I"$def value_for_key(keypath = '');T;'To;;F;
;;;;I"Doing::Configuration#from;F;[[I"user_config;T0;[[@�i�;T;:	from;;;[�;{�;IC;"�It takes the input, fills in the defaults where values do not exist.

user_config - a Hash or Configuration of overrides.

Returns a Configuration filled with defaults.;T;[�;[�;I"�It takes the input, fills in the defaults where values do not exist.

user_config - a Hash or Configuration of overrides.

Returns a Configuration filled with defaults.;T; 0;!@�;4i�;"F;5o;6;7F;8i�;9i�;$@�;:T;%I"ldef from(user_config)
  Util.deep_merge_hashes(DEFAULTS, Configuration[user_config].stringify_keys)
end;T;&I"def from(user_config);T;'To;;F;
;;;;I"2Doing::Configuration#update_deprecated_config;F;[�;[[@�i�;T;:update_deprecated_config;;;[�;{�;IC;"KMethod for transitioning from ~/.doingrc to ~/.config/doing/config.yml;T;[�;[�;I"M
Method for transitioning from ~/.doingrc to ~/.config/doing/config.yml
;T; 0;!@�;4i�;"T;5o;6;7F;8i�;9i�;$@�;:T;%I"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)

  wwid = Doing::WWID.new
  Doing.logger.log_now(:warn, 'Deprecated:', "main config file location has changed to #{config_file}")
  res = wwid.yn("Move #{old_file} to new location, preserving settings?", default_response: true)

  return unless res

  if File.exist?(default_config_file)
    res = wwid.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;T;&I"!def update_deprecated_config;T;'To;;F;
;;;;I"#Doing::Configuration#configure;F;[[I"opt;TI"{};T;[[@�i;T;:configure;;;[�;{�;IC;"4Read user configuration and merge with defaults;T;[o;)
;*I"
param;F;+I"Additional Options;T;I"opt;T;,[I"	Hash;T;!@�;[�;I"c
Read user configuration and merge with defaults

@param      opt   [Hash] Additional Options
;T; 0;!@�;4i�;"T;5o;6;7F;8i;9i;$@�;:T;%I"�def configure(opt = {})
  update_deprecated_config if config_file == default_config_file

  @ignore_local = opt[:ignore_local] if opt[:ignore_local]

  config = read_config.dup

  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] && !plugin[:config].empty?
      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) unless @ignore_local
  config = Util.deep_merge_hashes(config, local_config) unless @ignore_local

  Hooks.trigger :post_local_config, self

  config
end;T;&I"def configure(opt = {});T;'To;;F;
;;;;I"!Doing::Configuration#inspect;F;[�;[[@�i0;T;;N;;;[�;{�;IC;"�;T;[o;)
;*I"private;F;+I"�;T;0;,0;!@�;[�;I"
@private;T; 0;!@�;4i�;"F;5o;6;7F;8i/;9i/;$@�;:T;%I"Bdef inspect
  %(<Doing::Configuration #{@settings.hash}>)
end;T;&I"def inspect;T;'To;;F;
;;;;I"Doing::Configuration#to_s;F;[�;[[@�i5;T;;M;;;[�;{�;IC;"�;T;[o;)
;*I"private;F;+I"�;T;0;,0;!@�;[�;I"
@private;T; 0;!@�;4i�;"F;5o;6;7F;8i4;9i4;$@�;:T;%I"(def to_s
  YAML.dump(@settings)
end;T;&I"
def to_s;T;'To;;F;
;;;O;I"+Doing::Configuration#find_deprecations;F;[[I"config;T0;[[@�i@;T;:find_deprecations;;;[�;{�;IC;"$Test for deprecated config keys;T;[o;)
;*I"
param;F;+I"The configuration;T;I"config;T;,0;!@�;[�;I"M
Test for deprecated config keys

@param      config  The configuration
;T; 0;!@�;4i�;"T;5o;6;7F;8i;;9i?;$@�;:T;%I"Qdef find_deprecations(config)
  deprecated = false
  if config.key?('editor')
    deprecated = true
    config['editors']['default'] ||= config['editor']
    config.delete('editor')
    Doing.logger.debug('Deprecated:', "config key 'editor' is now 'editors->default', please update your config.")
  end

  if config.key?('config_editor_app') && !config['editors']['config']
    deprecated = true
    config['editors']['config'] = config['config_editor_app']
    config.delete('config_editor_app')
    Doing.logger.debug('Deprecated:',
                       "config key 'config_editor_app' is now 'editors->config', please update your config.")
  end

  if config.key?('editor_app') && !config['editors']['doing_file']
    deprecated = true
    config['editors']['doing_file'] = config['editor_app']
    config.delete('editor_app')
    Doing.logger.debug('Deprecated:',
                       "config key 'editor_app' is now 'editors->doing_file', please update your config.")
  end

  Doing.logger.warn('Deprecated:', 'outdated keys found, please run `doing config --update`.') if deprecated
  config
end;T;&I""def find_deprecations(config);T;'To;;F;
;;;O;I"&Doing::Configuration#local_config;F;[�;[[@�ib;T;:local_config;;;[�;{�;IC;"Read local configurations;T;[o;)
;*I"return;F;+I"Hash of config options;T;0;,0;!@
;[�;I"D
Read local configurations

@return     Hash of config options
;T; 0;!@
;4i�;"T;5o;6;7F;8i];9ia;$@�;:T;%I";def local_config
  return {} if @ignore_local

  local_configs = read_local_configs || {}

  if additional_configs&.count
    file_list = additional_configs.map { |p| p.sub(/^#{Util.user_home}/, '~') }.join(', ')
    Doing.logger.debug('Config:', "Local config files found: #{file_list}")
  end

  local_configs
end;T;&I"def local_config;T;'To;;F;
;;;O;I",Doing::Configuration#read_local_configs;F;[�;[[@�io;F;:read_local_configs;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@;4i�;$@�;:T;%I"def read_local_configs
  local_configs = {}

  begin
    additional_configs.each do |cfg|
      local_configs.deep_merge(Util.safe_load_file(cfg))
    end
  rescue StandardError
    Doing.logger.error('Config:', 'Error reading local configuration(s)')
  end

  local_configs
end;T;&I"def read_local_configs;T;'To;;F;
;;;O;I"%Doing::Configuration#read_config;F;[�;[[@�i�;T;:read_config;;;[�;{�;IC;"Reads a configuration.;T;[�;[�;I"
Reads a configuration.
;T; 0;!@';4i�;"T;5o;6;7F;8i};9i;$@�;:T;%I"�def read_config
  unless File.exist?(config_file)
    Doing.logger.info('Config:', 'Config file doesn\'t exist, using default configuration')
    return {}.deep_merge(DEFAULTS)
  end

  begin
    user_config = Util.safe_load_file(config_file)
    if user_config.key?('html_template')
      user_config['export_templates'] ||= {}
      user_config['export_templates'].deep_merge(user_config.delete('html_template'))
    end

    user_config['include_notes'] = user_config.delete(':include_notes') if user_config.key?(':include_notes')

    user_config.deep_merge(DEFAULTS)
  rescue StandardError => e
    Doing.logger.error('Config:', 'Error reading default configuration')
    Doing.logger.error('Error:', e.message)
    user_config = DEFAULTS
  end

  user_config
end;T;&I"def read_config;T;'To;;F;
;;;O;I"+Doing::Configuration#find_local_config;F;[�;[[@�i�;T;:find_local_config;;;[�;{�;IC;"0Finds a project-specific configuration file;T;[o;)
;*I"return;F;+I"A file path;T;0;,[I"String;T;!@5;[�;I"T
Finds a project-specific configuration file

@return     [String] A file path
;T; 0;!@5;4i�;"T;5o;6;7F;8i�;9i�;$@�;:T;%I":def find_local_config
  dir = Dir.pwd

  local_config_files = []

  while dir != '/' && (dir =~ %r{[A-Z]:/}).nil?
    local_config_files.push(File.join(dir, '.doingrc')) if File.exist? File.join(dir, '.doingrc')

    dir = File.dirname(dir)
  end

  local_config_files.delete(config_file)

  local_config_files
end;T;&I"def find_local_config;T;'To;;F;
;;;O;I"&Doing::Configuration#load_plugins;F;[[I"add_dir;TI"nil;T;[[@�i�;F;:load_plugins;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@H;4i�;$@�;:T;%I"�def load_plugins(add_dir = nil)
  FileUtils.mkdir_p(add_dir) if add_dir && !File.exist?(add_dir)

  Plugins.load_plugins(add_dir)
end;T;&I"$def load_plugins(add_dir = nil);T;'T;V@�;WIC;[�;V@�;XIC;[�;V@�;YIC;Z{;[IC;Z{�;\T;IC;Z{;.IC;Z{;]@�;^0;\T:ignore_localIC;Z{;]0;^@�;\T;\T;\T;_{�;`[�;[[@�i;T;:Configuration;;;;;[�;{�;IC;"Configuration object;T;[�;[�;I"
Configuration object
;T; 0;!@�;4i�;"T;5o;6;7F;8i	;9i;$@;I"Doing::Configuration;F;bo;c;d0;e0;f0;;g;$@;h0;i;[;'To;	;IC;[o;;F;
;[;;;I"Doing::Plugins.user_home;F;[�;[[I" lib/doing/plugin_manager.rb;Ti;F;;x;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@m;4i�;$@k;:T;%I"6def user_home
  @user_home ||= Util.user_home
end;T;&I"def user_home;T;'To;;F;
;[;;;I"Doing::Plugins.plugins;F;[�;[[@ri;F;:plugins;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@z;4i�;$@k;:T;%I"Hdef plugins
  @plugins ||= {
    import: {},
    export: {}
  }
end;T;&I"def plugins;T;'To;;F;
;[;;;I" Doing::Plugins.load_plugins;F;[[I"add_dir;TI"nil;T;[[@ri;T;;?;;;[�;{�;IC;"%Load plugins from plugins folder;T;[�;[�;I"'
Load plugins from plugins folder
;T; 0;!@�;4i�;"T;5o;6;7F;8i;9i;$@k;:T;%I"�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

  plugins
end;T;&I"$def load_plugins(add_dir = nil);T;'To;;F;
;[;;;I" Doing::Plugins.plugins_path;F;[[I"add_dir;TI"nil;T;[[@ri';T;:plugins_path;;;[�;{�;IC;"RPublic: Setup the plugin search path

Returns an Array of plugin search paths;T;[�;[�;I"RPublic: Setup the plugin search path

Returns an Array of plugin search paths;T; 0;!@�;4i�;"F;5o;6;7F;8i$;9i&;$@k;:T;%I"�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.expand_path(d) }
end;T;&I"$def plugins_path(add_dir = nil);T;'To;;F;
;[;;;I"Doing::Plugins.register;F;[[I"
title;T0[I"	type;T0[I"
klass;T0;[[@ri9;T;;�;;;[�;{�;IC;"�Register a plugin

param: +[String|Array]+ title  The name of the plugin (can be an array of names)

param: +type+ The plugin type (:import, :export)

param: +klass+ The class responding to :render or :import


returns: Success boolean;T;[�;[�;I"�
Register a plugin

param: +[String|Array]+ title  The name of the plugin (can be an array of names)

param: +type+ The plugin type (:import, :export)

param: +klass+ The class responding to :render or :import


returns: Success boolean
;T; 0;!@�;4i�;"T;5o;6;7F;8i-;9i8;$@k;:T;%I"�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;T;&I"%def register(title, type, klass);T;'To;;F;
;[;;;I"#Doing::Plugins.validate_plugin;F;[[I"
title;T0[I"	type;T0[I"
klass;T0;[[@riU;F;:validate_plugin;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@k;:T;%I"�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: type, plugin: title)
  end

  if type == :export && !klass.respond_to?(:render)
    raise Errors::PluginUncallable.new('Export plugins must respond to :render', type: type, plugin: title)
  end

  type
end;T;&I",def validate_plugin(title, type, klass);T;'To;;F;
;[;;;I"Doing::Plugins.valid_type;F;[[I"	type;T0[I"
default:;TI"nil;T;[[@rib;F;:valid_type;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@k;:T;%I"<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, 'Invalid plugin type'
         end

  type.to_sym
end;T;&I"'def valid_type(type, default: nil);T;'To;;F;
;[;;;I" Doing::Plugins.list_plugins;F;[[I"options;TI"{};T;[[@riw;T;:list_plugins;;;[�;{�;IC;"%List available plugins to stdout;T;[o;)
;*I"
param;F;+I"�;T;I"options;T;,[I"	type;TI"separator;T;!@�;[�;I"Q
List available plugins to stdout

@param      options  { type, separator }
;T; 0;!@�;4i�;"T;5o;6;7F;8ir;9iv;$@k;:T;%I"def list_plugins(options = {})
  separator = options[:column] ? "\n" : "\t"
  type = options[:type].nil? || options[:type] =~ /all/i ? 'all' : valid_type(options[: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;T;&I"#def list_plugins(options = {});T;'To;;F;
;[;;;I"%Doing::Plugins.available_plugins;F;[[I"
type:;TI":export;T;[[@ri�;T;:available_plugins;;;[�;{�;IC;"+Return array of available plugin names;T;[o;)
;*I"
param;F;+I"#Plugin type (:import, :export);T;I"	type;T;,0;!@�o;)
;*I"return;F;+I"plugin names;T;0;,[I"Array<String>;T;!@�;[�;I"�
Return array of available plugin names

@param      type  Plugin type (:import, :export)

@return     [Array<String>] plugin names
;T; 0;!@�;4i�;"T;5o;6;7F;8i�;9i�;$@k;:T;%I"adef available_plugins(type: :export)
  type = valid_type(type)
  plugins[type].keys.sort
end;T;&I")def available_plugins(type: :export);T;'To;;F;
;[;;;I" Doing::Plugins.plugin_names;F;[[I"
type:;TI":export;T[I"separator:;TI"'|';T;[[@ri�;T;:plugin_names;;;[�;{�;IC;"*Return string version of plugin names;T;[o;)
;*I"
param;F;+I"#Plugin type (:import, :export);T;I"	type;T;,0;!@o;)
;*I"
param;F;+I"%The separator to join names with;T;I"separator;T;,0;!@o;)
;*I"return;F;+I"Plugin names;T;0;,[I"String;T;!@;[�;I"�
Return string version of plugin names

@param      type       Plugin type (:import, :export)
@param      separator  The separator to join names with

@return     [String]   Plugin names
;T; 0;!@;4i�;"T;5o;6;7F;8i�;9i�;$@k;:T;%I"}def plugin_names(type: :export, separator: '|')
  type = valid_type(type)
  available_plugins(type: type).join(separator)
end;T;&I"4def plugin_names(type: :export, separator: '|');T;'To;;F;
;[;;;I" Doing::Plugins.plugin_regex;F;[[I"
type:;TI":export;T;[[@ri�;T;:plugin_regex;;;[�;{�;IC;"LReturn a regular expression of all
            plugin triggers for type;T;[o;)
;*I"
param;F;+I" The type :import or :export;T;I"	type;T;,0;!@2;[�;I"}
Return a regular expression of all
            plugin triggers for type

@param      type  The type :import or :export
;T; 0;!@2;4i�;"T;5o;6;7F;8i�;9i�;$@k;:T;%I"�def plugin_regex(type: :export)
  type = valid_type(type)
  pattern = []
  plugins[type].each do |_, options|
    pattern << options[:trigger].normalize_trigger
  end
  Regexp.new("^(?:#{pattern.join('|')})$", true)
end;T;&I"$def plugin_regex(type: :export);T;'To;;F;
;[;;;I"$Doing::Plugins.plugin_templates;F;[[I"
type:;TI":export;T;[[@ri�;F;:plugin_templates;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@G;4i�;$@k;:T;%I"def plugin_templates(type: :export)
  type = valid_type(type)
  templates = []
  plugs = plugins[type].clone
  plugs.delete_if { |_t, o| o[:templates].nil? }.each do |_, options|
    options[:templates].each do |t|
      templates << t[:name]
    end
  end

  templates
end;T;&I"(def plugin_templates(type: :export);T;'To;;F;
;[;;;I""Doing::Plugins.template_regex;F;[[I"
type:;TI":export;T;[[@ri�;F;:template_regex;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@V;4i�;$@k;:T;%I"Ddef template_regex(type: :export)
  type = valid_type(type)
  pattern = []
  plugs = plugins[type].clone
  plugs.delete_if { |_t, o| o[:templates].nil? }.each do |_, options|
    options[:templates].each do |t|
      pattern << t[:trigger].normalize_trigger
    end
  end
  Regexp.new("^(?:#{pattern.join('|')})$", true)
end;T;&I"&def template_regex(type: :export);T;'To;;F;
;[;;;I"(Doing::Plugins.template_for_trigger;F;[[I"trigger;T0[I"
type:;TI":export;T;[[@ri�;F;:template_for_trigger;;;[�;{�;IC;"�;T;[o;)
;*I"
raise;F;+@.;0;,[I"Errors::InvalidArgument;T;!@e;[�;@.; 0;!@e;4i�;$@k;:T;%I"�def template_for_trigger(trigger, type: :export)
  type = valid_type(type)
  plugs = plugins[type].clone
  plugs.delete_if { |_t, o| o[:templates].nil? }.each do |_, options|
    options[:templates].each do |t|
      return options[:class].template(trigger) if trigger =~ /^(?:#{t[:trigger].normalize_trigger})$/
    end
  end
  raise Errors::InvalidArgument, "No template type matched \"#{trigger}\""
end;T;&I"5def template_for_trigger(trigger, type: :export);T;'T;V@k;WIC;[�;V@k;XIC;[�;V@k;YIC;Z{;[IC;Z{�;\T;IC;Z{�;\T;\T;_{�;`[�;[[@ri
;T;:Plugins;;;;;[�;{�;IC;"Plugin handling;T;[�;[�;I"Plugin handling;T; 0;!@k;4i�;"F;5o;6;7F;8i	;9i	;$@;I"Doing::Plugins;F;'T;V@;WIC;[�;V@;XIC;[�;V@;YIC;Z{;[IC;Z{�;\T;IC;Z{�;\T;\T;_{�;`[�;[[@R
i	[I"lib/doing/hash.rb;Ti[@i[@i[I"lib/doing/time.rb;Ti[@�i[@�i[@�i[@	i[@�i[@�i	[@8i[@+i[I"lib/doing/string.rb;Ti[I"lib/doing/symbol.rb;Ti[@Gi[@�i[@�i[@i[@�i[@ri[I"!lib/doing/string_chronify.rb;Ti;T;:
Doing;;;;;[�;{�;IC;";Cribbed from <https://github.com/flori/term-ansicolor>;T;[�;[�;I";Cribbed from <https://github.com/flori/term-ansicolor>;T; 0;!@;4i�;"F;5o;6;7F;8i;9i;$@;I"
Doing;Fo;
;IC;[
o;;F;
;;;;I"Hash#deep_freeze;F;[�;[[@�i;T;:deep_freeze;;;[�;{�;IC;" Freeze all values in a hash;T;[o;)
;*I"return;F;+I"�;T;0;,[I"$description_of_the_return_value;T;!@�;[�;I"S
Freeze all values in a hash

@return     { description_of_the_return_value }
;T; 0;!@�;4i�;"T;5o;6;7F;8i;9i;$@�;:T;%I"Ydef deep_freeze
  map { |k, v| v.is_a?(Hash) ? v.deep_freeze : v.freeze }.freeze
end;T;&I"def deep_freeze;T;'To;;F;
;;;;I"Hash#deep_freeze!;F;[�;[[@�i;F;:deep_freeze!;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@�;:T;%I"/def deep_freeze!
  replace deep_freeze
end;T;&I"def deep_freeze!;T;'To;;F;
;;;;I"Hash#stringify_keys;F;[�;[[@�i;T;:stringify_keys;;;[�;{�;IC;"XTurn all keys into string

Return a copy of the hash where all its keys are strings;T;[�;[�;I"XTurn all keys into string

Return a copy of the hash where all its keys are strings;T; 0;!@�;4i�;"F;5o;6;7F;8i;9i;$@�;:T;%I"wdef stringify_keys
  each_with_object({}) { |(k, v), hsh| hsh[k.to_s] = v.is_a?(Hash) ? v.stringify_keys : v }
end;T;&I"def stringify_keys;T;'To;;F;
;;;;I"Hash#symbolize_keys;F;[�;[[@�i ;T;:symbolize_keys;;;[�;{�;IC;"Turn all keys into symbols;T;[�;[�;I"Turn all keys into symbols;T; 0;!@�;4i�;"F;5o;6;7F;8i;9i;$@�;:T;%I"ydef symbolize_keys
  each_with_object({}) { |(k, v), hsh| hsh[k.to_sym] = v.is_a?(Hash) ? v.symbolize_keys : v }
end;T;&I"def symbolize_keys;T;'To;;F;
;;;;I"Hash#deep_set;F;[[I"	path;T0[I"
value;T0;[[@�i,;T;:
deep_set;;;[�;{�;IC;"+Set a nested hash value using an array;T;[	o;)
;*I"example;F;+I"�;F;I"+`{}.deep_set(['one', 'two'], 'value')`;T;,0;!@�o;)
;*I"example;F;+I"�;F;I"*`=> { 'one' => { 'two' => 'value' } };T;,0;!@�o;)
;*I"
param;F;+I"
key path;T;I"	path;T;,[I"
Array;T;!@�o;)
;*I"
param;F;+I"The value;T;I"
value;T;,0;!@�;[�;I"�Set a nested hash value using an array

@example `{}.deep_set(['one', 'two'], 'value')`
@example `=> { 'one' => { 'two' => 'value' } }

@param      path   [Array] key path
@param      value  The value
;T; 0;!@�;4i�;"F;5o;6;7F;8i$;9i+;$@�;:T;%I"Rdef deep_set(path, value)
  if path.count == 1
    if value
      self[path[0]] = value
    else
      delete(path[0])
    end
  else
    if 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
end;T;&I"def deep_set(path, value);T;'T;V@�;WIC;[�;V@�;XIC;[�;V@�;YIC;Z{;[IC;Z{�;\T;IC;Z{�;\T;\T;_{�;`[�;[[@�i
;T;:	Hash;;;;;[�;{�;IC;"Hash helpers;T;[�;[�;I"Hash helpers;T; 0;!@�;4i�;"F;5o;6;7F;8i	;9i	;$@;I"	Hash;F;bo;c;d0;e0;f0;;g;$@;h0;i;[;'To;
;IC;[o;;F;
;;;;I"Time#relative_date;F;[�;[[@�i;F;:relative_date;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@+;4i�;$@);:T;%I"def relative_date
  if self > Date.today.to_time
    strftime('%_I:%M%P')
  elsif self > (Date.today - 6).to_time
    strftime('%a %_I:%M%P')
  elsif self.year == Date.today.year
    strftime('%m/%d %_I:%M%P')
  else
    strftime('%m/%d/%Y %_I:%M%P')
  end
end;T;&I"def relative_date;T;'T;V@);WIC;[�;V@);XIC;[�;V@);YIC;Z{;[IC;Z{�;\T;IC;Z{�;\T;\T;_{�;`[�;[[@�i
;T;:	Time;;;;;[�;{�;IC;"Date helpers;T;[�;[�;I"
Date helpers
;T; 0;!@);4i�;"T;5o;6;7F;8i;9i	;$@;I"	Time;F;bo;c;d0;e0;f0;;g;$@;h0;i;[;'T@�o;
;IC;[-o;;F;
;;;;I"String#is_rx?;F;[�;[[@�i;T;:is_rx?;;;[�;{�;IC;"PDetermines if receiver is surrounded by slashes or starts with single quote;T;[o;)
;*I"return;F;+I"$True if regex, False otherwise.;T;0;,[@0;!@K;[�;I"
Determines if receiver is surrounded by slashes or starts with single quote

@return     True if regex, False otherwise.
;T; 0;!@K;4i�;"T;5o;6;7F;8i;9i;$@I;:T;%I".def is_rx?
  self =~ %r{(^/.*?/$|^')}
end;T;&I"def is_rx?;T;'To;;F;
;;;;I"String#to_rx;F;[[I"distance:;TI"3;T[I"case_type:;TI":smart;T;[[@�i$;T;:
to_rx;;;[�;{�;IC;"�Convert string to fuzzy regex. Characters in words
can be separated by up to *distance* characters in
haystack, spaces indicate unlimited distance.;T;[	o;)
;*I"example;F;+I"�;F;I"V`"this word".to_rx(2) => /t.{0,3}h.{0,3}i.{0,3}s.{0,3}.*?w.{0,3}o.{0,3}r.{0,3}d/`;T;,0;!@]o;)
;*I"
param;F;+I"(Allowed distance
between characters;T;I"
distance;T;,[I"Integer;T;!@]o;)
;*I"
param;F;+I"The case type;T;I"case_type;T;,0;!@]o;)
;*I"return;F;+I"Regex pattern;T;0;,[I"Regexp;T;!@];[�;I"�
Convert string to fuzzy regex. Characters in words
can be separated by up to *distance* characters in
haystack, spaces indicate unlimited distance.

@example    `"this word".to_rx(2) => /t.{0,3}h.{0,3}i.{0,3}s.{0,3}.*?w.{0,3}o.{0,3}r.{0,3}d/`

@param      distance   [Integer] Allowed distance
                       between characters
@param      case_type  The case type

@return     [Regexp] Regex pattern
;T; 0;!@];4i�;"T;5o;6;7F;8i;9i#;$@I;:T;%I"bdef to_rx(distance: 3, case_type: :smart)
  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 { |w| w.split('').join(".{0,#{distance}}") }.join('.*?')
            end
  Regexp.new(pattern, !case_sensitive)
end;T;&I".def to_rx(distance: 3, case_type: :smart);T;'To;;F;
;;;;I"String#truthy?;F;[�;[[@�i>;T;:truthy?;;;[�;{�;IC;"oTest string for truthiness (0, "f", "false", "n", "no" all return false, case insensitive, otherwise true);T;[o;)
;*I"return;F;+I"String is truthy;T;0;,[I"Boolean;T;!@�;[�;I"�
Test string for truthiness (0, "f", "false", "n", "no" all return false, case insensitive, otherwise true)

@return     [Boolean] String is truthy
;T; 0;!@�;4i�;"T;5o;6;7F;8i9;9i=;$@I;:T;%I"Ydef truthy?
  if self =~ /^(0|f(alse)?|n(o)?)$/i
    false
  else
    true
  end
end;T;&I"def truthy?;T;'To;;F;
;;;;I"String#compress;F;[�;[[@�iG;T;;n;;;[�;{�;IC;"-Compress multiple spaces to single space;T;[�;[�;I"-Compress multiple spaces to single space;T; 0;!@�;4i�;"F;5o;6;7F;8iF;9iF;$@I;:T;%I"-def compress
  gsub(/ +/, ' ').strip
end;T;&I"def compress;T;'To;;F;
;;;;I"String#compress!;F;[�;[[@�iK;F;;m;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@I;:T;%I")def compress!
  replace compress
end;T;&I"def compress!;T;'To;;F;
;;;;I"String#highlight_tags!;F;[[I"
color;TI"
'yellow';T;[[@�iP;T;:highlight_tags!;;;[�;{�;IC;"�;T;[�;[o:YARD::Tags::RefTagList;Vo;c;d@I;eI"#highlight_tags;T;fT;;u;$@I;ho;;F;
;;;;I"String#highlight_tags;F;[[I"
color;TI"
'yellow';T;[[@�i[;T;;u;;;[�;{�;IC;"%Colorize @tags with ANSI escapes;T;[o;)
;*I"
param;F;+I"color (see #Color);T;I"
color;T;,[I"String;T;!@�o;)
;*I"return;F;+I""string with @tags highlighted;T;0;,[I"String;T;!@�;[�;I"�
Colorize @tags with ANSI escapes

@param      color  [String] color (see #Color)

@return     [String] string with @tags highlighted
;T; 0;!@�;4i�;"T;5o;6;7F;8iT;9iZ;$@I;:T;%I"�def highlight_tags(color = 'yellow')
  escapes = scan(/(\e\[[\d;]+m)[^\e]+@/)
  color = color.split(' ') unless color.is_a?(Array)
  tag_color = ''
  color.each { |c| tag_color += Doing::Color.send(c) }
  last_color = if !escapes.empty?
                 escapes[-1][0]
               else
                 Doing::Color.default
               end
  gsub(/(\s|m)(@[^ ("']+)/, "\\1#{tag_color}\\2#{Doing::Color.reset}#{last_color}")
end;T;&I")def highlight_tags(color = 'yellow');T;'T;i0;*I"
param;T;0;I"!@param (see #highlight_tags);T; 0;!@�;4i�;"T;5o;6;7F;8iO;9iO;$@I;:T;%I"Ndef highlight_tags!(color = 'yellow')
  replace highlight_tags(color)
end;T;&I"*def highlight_tags!(color = 'yellow');T;'T@�o;;F;
;;;;I"String#ignore?;F;[�;[[@�im;T;:ignore?;;;[�;{�;IC;"#Test if line should be ignored;T;[o;)
;*I"return;F;+I"line is empty or comment;T;0;,[I"Boolean;T;!@�;[�;I"U
Test if line should be ignored

@return     [Boolean] line is empty or comment
;T; 0;!@�;4i�;"T;5o;6;7F;8ih;9il;$@I;:T;%I"Ddef ignore?
  line = self
  line =~ /^#/ || line =~ /^\s*$/
end;T;&I"def ignore?;T;'To;;F;
;;;;I"String#truncate;F;[[I"len;T0[I"ellipsis:;TI"
'...';T;[[@�iw;T;:
truncate;;;[�;{�;IC;"Truncate to nearest word;T;[o;)
;*I"
param;F;+I"The length;T;I"len;T;,0;!@�;[�;I"=
Truncate to nearest word

@param      len   The length
;T; 0;!@�;4i�;"T;5o;6;7F;8ir;9iv;$@I;:T;%I"�def truncate(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;T;&I"'def truncate(len, ellipsis: '...');T;'To;;F;
;;;;I"String#truncate!;F;[[I"len;T0[I"ellipsis:;TI"
'...';T;[[@�i�;F;:truncate!;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@;4i�;$@I;:T;%I"Xdef truncate!(len, ellipsis: '...')
  replace truncate(len, ellipsis: ellipsis)
end;T;&I"(def truncate!(len, ellipsis: '...');T;'To;;F;
;;;;I"String#truncmiddle;F;[[I"len;T0[I"ellipsis:;TI"
'...';T;[[@�i�;T;:truncmiddle;;;[�;{�;IC;""Truncate string in the middle;T;[o;)
;*I"
param;F;+I"The length;T;I"len;T;,0;!@o;)
;*I"
param;F;+I"The ellipsis;T;I"
ellipsis;T;,0;!@;[�;I"i
Truncate string in the middle

@param      len       The length
@param      ellipsis  The ellipsis
;T; 0;!@;4i�;"T;5o;6;7F;8i�;9i�;$@I;:T;%I"�def truncmiddle(len, ellipsis: '...')
  return self if length <= len
  len -= (ellipsis.length / 2).to_i
  total = length
  half = total / 2
  cut = (total - len) / 2
  sub(/(.{#{half - cut}}).*?(.{#{half - cut}})$/, "\\1#{ellipsis}\\2")
end;T;&I"*def truncmiddle(len, ellipsis: '...');T;'To;;F;
;;;;I"String#truncmiddle!;F;[[I"len;T0[I"ellipsis:;TI"
'...';T;[[@�i�;F;:truncmiddle!;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@8;4i�;$@I;:T;%I"^def truncmiddle!(len, ellipsis: '...')
  replace truncmiddle(len, ellipsis: ellipsis)
end;T;&I"+def truncmiddle!(len, ellipsis: '...');T;'To;;F;
;;;;I"String#uncolor;F;[�;[[@�i�;T;;�;;;[�;{�;IC;"Remove color escape codes;T;[o;)
;*I"return;F;+I"clean string;T;0;,0;!@I;[�;I":
Remove color escape codes

@return     clean string
;T; 0;!@I;4i�;"T;5o;6;7F;8i�;9i�;$@I;:T;%I"-def uncolor
  gsub(/\e\[[\d;]+m/,'')
end;T;&I"def uncolor;T;'To;;F;
;;;;I"String#uncolor!;F;[�;[[@�i�;F;:
uncolor!;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@Z;4i�;$@I;:T;%I"'def uncolor!
  replace uncolor
end;T;&I"def uncolor!;T;'To;;F;
;;;;I"String#wrap;F;[
[I"len;T0[I"	pad:;TI"0;T[I"indent:;TI"	'  ';T[I"offset:;TI"0;T[I"prefix:;TI"'';T[I"color:;TI"'';T[I"after:;TI"'';T[I"reset:;TI"'';T;[[@�i�;T;:	wrap;;;[�;{�;IC;"0Wrap string at word breaks, respecting tags;T;[o;)
;*I"
param;F;+I"The length;T;I"len;T;,[I"Integer;T;!@fo;)
;*I"
param;F;+I"5(Optional) The width to pad each subsequent line;T;I"offset;T;,[I"Integer;T;!@fo;)
;*I"
param;F;+I",(Optional) A prefix to add to each line;T;I"prefix;T;,[I"String;T;!@f;[�;I"�
Wrap string at word breaks, respecting tags

@param      len     [Integer] The length
@param      offset  [Integer] (Optional) The width to pad each subsequent line
@param      prefix  [String] (Optional) A prefix to add to each line
;T; 0;!@f;4i�;"T;5o;6;7F;8i�;9i�;$@I;:T;%I"�def wrap(len, pad: 0, indent: '  ', offset: 0, prefix: '', color: '', after: '', reset: '')
  last_color = color.empty? ? '' : after.last_color
  note_rx = /(?i-m)(%(?:[io]d|(?:\^[\s\S])?(?:(?:[ _t]|[^a-z0-9])?\d+)?(?:[\s\S][ _t]?)?)?note)/
  # Don't break inside of tag values
  str = gsub(/@\S+\(.*?\)/) { |tag| tag.gsub(/\s/, '%%%%') }
  words = str.split(/ /).map { |word| word.gsub(/%%%%/, ' ') }
  out = []
  line = []
  words.each do |word|
    if line.join(' ').uncolor.length + word.uncolor.length + 1 > len
      out.push(line.join(' '))
      line.clear
    end

    line << word.uncolor
  end
  out.push(line.join(' '))
  note = ''
  after.sub!(note_rx) do
    note = Regexp.last_match(0)
    ''
  end

  out[0] = format("%-#{pad}s%s%s", out[0], last_color, after)

  left_pad = ' ' * offset
  left_pad += indent
  out.map { |l| "#{left_pad}#{color}#{l}#{last_color}" }.join("\n").strip + last_color + " #{note}".chomp
end;T;&I"_def wrap(len, pad: 0, indent: ' ', offset: 0, prefix: '', color: '', after: '', reset: '');T;'To;;F;
;;;;I"String#cap_first;F;[�;[[@�i�;T;:cap_first;;;[�;{�;IC;"0Capitalize on the first character on string;T;[o;)
;*I"return;F;+I"Capitalized string;T;0;,0;!@�;[�;I"R
Capitalize on the first character on string

@return     Capitalized string
;T; 0;!@�;4i�;"T;5o;6;7F;8i�;9i�;$@I;:T;%I"=def cap_first
  sub(/^\w/) do |m|
    m.upcase
  end
end;T;&I"def cap_first;T;'To;;F;
;;;;I"String#normalize_order!;F;[[I"default;TI"
'asc';T;[[@�i�;T;:normalize_order!;;;[�;{�;IC;"4Convert a sort order string to a qualified type;T;[o;)
;*I"return;F;+I"'asc' or 'desc';T;0;,[I"String;T;!@�;[�;I"\
Convert a sort order string to a qualified type

@return     [String] 'asc' or 'desc'
;T; 0;!@�;4i�;"T;5o;6;7F;8i�;9i�;$@I;:T;%I"Qdef normalize_order!(default = 'asc')
  replace normalize_order(default)
end;T;&I"*def normalize_order!(default = 'asc');T;'To;;F;
;;;;I"String#normalize_order;F;[[I"default;TI"
'asc';T;[[@�i�;F;:normalize_order;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@I;:T;%I"|def normalize_order(default = 'asc')
  case self
  when /^a/i
    'asc'
  when /^d/i
    'desc'
  else
    default
  end
end;T;&I")def normalize_order(default = 'asc');T;'To;;F;
;;;;I"String#normalize_case!;F;[�;[[@�i�;T;:normalize_case!;;;[�;{�;IC;"2Convert a case sensitivity string to a symbol;T;[o;)
;*I"return;F;+I"'Symbol :smart, :sensitive, :ignore;T;0;,0;!@�;[�;I"d
Convert a case sensitivity string to a symbol

@return     Symbol :smart, :sensitive, :ignore
;T; 0;!@�;4i�;"T;5o;6;7F;8i�;9i�;$@I;:T;%I"5def normalize_case!
  replace normalize_case
end;T;&I"def normalize_case!;T;'To;;F;
;;;;I"String#normalize_case;F;[[I"default;TI":smart;T;[[@�i�;F;:normalize_case;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@I;:T;%I"�def normalize_case(default = :smart)
  case self
  when /^c/i
    :sensitive
  when /^i/i
    :ignore
  when /^s/i
    :smart
  else
    default.is_a?(Symbol) ? default : default.normalize_case
  end
end;T;&I")def normalize_case(default = :smart);T;'To;;F;
;;;;I"String#normalize_bool!;F;[[I"default;TI"	:and;T;[[@�i;T;:normalize_bool!;;;[�;{�;IC;")Convert a boolean string to a symbol;T;[o;)
;*I"return;F;+I"Symbol :and, :or, or :not;T;0;,0;!@�;[�;I"R
Convert a boolean string to a symbol

@return     Symbol :and, :or, or :not
;T; 0;!@�;4i�;"T;5o;6;7F;8i�;9i;$@I;:T;%I"Ndef normalize_bool!(default = :and)
  replace normalize_bool(default)
end;T;&I"(def normalize_bool!(default = :and);T;'To;;F;
;;;;I"String#normalize_bool;F;[[I"default;TI"	:and;T;[[@�i;F;:normalize_bool;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@;4i�;$@I;:T;%I"�def normalize_bool(default = :and)
  case self
  when /(and|all)/i
    :and
  when /(any|or)/i
    :or
  when /(not|none)/i
    :not
  else
    default.is_a?(Symbol) ? default : default.normalize_bool
  end
end;T;&I"'def normalize_bool(default = :and);T;'To;;F;
;;;;I"String#normalize_trigger!;F;[�;[[@�i;F;:normalize_trigger!;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@;4i�;$@I;:T;%I";def normalize_trigger!
  replace normalize_trigger
end;T;&I"def normalize_trigger!;T;'To;;F;
;;;;I"String#normalize_trigger;F;[�;[[@�i;F;:normalize_trigger;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@";4i�;$@I;:T;%I"Bdef normalize_trigger
  gsub(/\((?!\?:)/, '(?:').downcase
end;T;&I"def normalize_trigger;T;'To;;F;
;;;;I"String#to_tags;F;[�;[[@�i;F;;s;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@.;4i�;$@I;:T;%I"pdef to_tags
  gsub(/ *, */, ' ').gsub(/ +/, ' ').split(/ /).sort.uniq.map { |t| t.strip.sub(/^@/, '') }
end;T;&I"def to_tags;T;'To;;F;
;;;;I"String#add_tags!;F;[[I"	tags;T0[I"remove:;TI"
false;T;[[@�i!;F;:add_tags!;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@:;4i�;$@I;:T;%I"Tdef add_tags!(tags, remove: false)
  replace add_tags(tags, remove: remove)
end;T;&I"'def add_tags!(tags, remove: false);T;'To;;F;
;;;;I"String#add_tags;F;[[I"	tags;T0[I"remove:;TI"
false;T;[[@�i%;F;:
add_tags;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@K;4i�;$@I;:T;%I"�def add_tags(tags, remove: false)
  title = self.dup
  tags = tags.to_tags
  tags.each { |tag| title.tag!(tag, remove: remove) }
  title
end;T;&I"&def add_tags(tags, remove: false);T;'To;;F;
;;;;I"String#tag!;F;[[I"tag;T0[I"**options;T0;[[@�i1;T;:	tag!;;;[�;{�;IC;"*Add, rename, or remove a tag in place;T;[o;)
;*I"see;F;+0;I"	#tag;T;,0;!@\;[�;I"7
Add, rename, or remove a tag in place

@see #tag
;T; 0;!@\;4i�;"T;5o;6;7F;8i,;9i0;$@I;:T;%I"?def tag!(tag, **options)
  replace tag(tag, **options)
end;T;&I"def tag!(tag, **options);T;'To;;F;
;;;;I"String#tag;F;[[I"tag;T0[I"value:;TI"nil;T[I"remove:;TI"
false;T[I"rename_to:;TI"nil;T[I"regex:;TI"
false;T[I"single:;TI"
false;T[I"force:;TI"
false;T;[[@�iB;T;;B;;;[�;{�;IC;"!Add, rename, or remove a tag;T;[
o;)
;*I"
param;F;+I"The tag;T;I"tag;T;,0;!@qo;)
;*I"
param;F;+I" Value for tag (@tag(value));T;I"
value;T;,[I"String;T;!@qo;)
;*I"
param;F;+I"%Remove the tag instead of adding;T;I"remove;T;,[I"Boolean;T;!@qo;)
;*I"
param;F;+I"Replace tag with this tag;T;I"rename_to;T;,[I"String;T;!@qo;)
;*I"
param;F;+I"Tag is regular expression;T;I"
regex;T;,[I"Boolean;T;!@qo;)
;*I"
param;F;+I"-Operating on a single item (for logging);T;I"single;T;,[I"Boolean;T;!@qo;)
;*I"
param;F;+I"0With rename_to, add tag if it doesn't exist;T;I"
force;T;,[I"Boolean;T;!@qo;)
;*I"return;F;+I""The string with modified tags;T;0;,[I"String;T;!@q;[�;I"�
Add, rename, or remove a tag

@param      tag        The tag
@param      value      [String] Value for tag (@tag(value))
@param      remove     [Boolean] Remove the tag instead of adding
@param      rename_to  [String] Replace tag with this tag
@param      regex      [Boolean] Tag is regular expression
@param      single     [Boolean] Operating on a single item (for logging)
@param      force      [Boolean] With rename_to, add tag if it doesn't exist

@return     [String] The string with modified tags
;T; 0;!@q;4i�;"T;5o;6;7F;8i5;9iA;$@I;:T;%I"
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.dedup_tags!
      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}(?=[ (]|$)/
    Doing.logger.debug('Skipped:', "already tagged #{"@#{tag}".cyan}")
    return title
  else
    add = tag
    add += "(#{value})" unless value.nil?
    title.chomp!
    title += " @#{add}"

    title.dedup_tags!
    title.chomp!
    Doing.logger.write(log_level, 'Tag:', %(added #{('@' + tag).cyan} to "#{title}"))
  end

  title.gsub(/ +/, ' ')
end;T;&I"gdef tag(tag, value: nil, remove: false, rename_to: nil, regex: false, single: false, force: false);T;'To;;F;
;;;;I"String#dedup_tags!;F;[�;[[@�i~;T;:dedup_tags!;;;[�;{�;IC;"9Remove duplicate tags, leaving only first occurrence;T;[o;)
;*I"return;F;+I"Deduplicated string;T;0;,0;!@�;[�;I"\
Remove duplicate tags, leaving only first occurrence

@return     Deduplicated string
;T; 0;!@�;4i�;"T;5o;6;7F;8iy;9i};$@I;:T;%I"-def dedup_tags!
  replace dedup_tags
end;T;&I"def dedup_tags!;T;'To;;F;
;;;;I"String#dedup_tags;F;[�;[[@�i�;F;:dedup_tags;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@I;:T;%I"*def dedup_tags
  title = dup
  tags = title.scan(/(?<=\A| )(@(\S+?)(\([^)]+\))?)(?= |\Z)/).uniq
  tags.each do |tag|
    found = false
    title.gsub!(/( |^)#{tag[1]}(\([^)]+\))?(?= |$)/) do |m|
      if found
        ''
      else
        found = true
        m
      end
    end
  end
  title
end;T;&I"def dedup_tags;T;'To;;F;
;;;;I"String#last_color;F;[�;[[@�i�;T;:last_color;;;[�;{�;IC;"PReturns the last escape sequence from a string.

Actually returns all escape codes, with the assumption
that the result of inserting them will generate the
same color as was set at the end of the string.
Because you can send modifiers like dark and bold
separate from color codes, only using the last code
may not render the same style.;T;[o;)
;*I"return;F;+I"All escape codes in string;T;0;,[I"String;T;!@�;[�;I"�Returns the last escape sequence from a string.

Actually returns all escape codes, with the assumption
that the result of inserting them will generate the
same color as was set at the end of the string.
Because you can send modifiers like dark and bold
separate from color codes, only using the last code
may not render the same style.

@return     [String]  All escape codes in string
;T; 0;!@�;4i�;"F;5o;6;7F;8i�;9i�;$@I;:T;%I"6def last_color
  scan(/\e\[[\d;]+m/).join('')
end;T;&I"def last_color;T;'To;;F;
;;;;I"String#link_urls!;F;[[I"
**opt;T0;[[@�i�;T;:link_urls!;;;[�;{�;IC;""Turn raw urls into HTML links;T;[o;)
;*I"
param;F;+I"Additional Options;T;I"opt;T;,[I"	Hash;T;!@�;[�;I"Q
Turn raw urls into HTML links

@param      opt   [Hash] Additional Options
;T; 0;!@�;4i�;"T;5o;6;7F;8i�;9i�;$@I;:T;%I"adef link_urls!(**opt)
  fmt = opt.fetch(:format, :html)
  replace link_urls(format: fmt)
end;T;&I"def link_urls!(**opt);T;'To;;F;
;;;;I"String#link_urls;F;[[I"
**opt;T0;[[@�i�;F;:link_urls;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@;4i�;$@I;:T;%I"�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;T;&I"def link_urls(**opt);T;'To;;F;
;;;;I"String#remove_self_links;F;[�;[[@�i�;T;:remove_self_links;;;[�;{�;IC;"$Remove <self-linked> formatting;T;[�;[�;I"$Remove <self-linked> formatting;T; 0;!@;4i�;"F;5o;6;7F;8i�;9i�;$@I;:T;%I"�def remove_self_links
  gsub(/<(.*?)>/) do |match|
    m = Regexp.last_match
    if m[1] =~ /^https?:/
      m[1]
    else
      match
    end
  end
end;T;&I"def remove_self_links;T;'To;;F;
;;;;I""String#replace_qualified_urls;F;[[I"**options;T0;[[@�i�;T;:replace_qualified_urls;;;[�;{�;IC;"Replace qualified urls;T;[�;[�;I"Replace qualified urls;T; 0;!@";4i�;"F;5o;6;7F;8i�;9i�;$@I;:T;%I"�def replace_qualified_urls(**options)
  fmt = options.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;T;&I"*def replace_qualified_urls(**options);T;'To;;F;
;;;;I"String#clean_unlinked_urls;F;[�;[[@�i�;T;:clean_unlinked_urls;;;[�;{�;IC;"Clean up unlinked <urls>;T;[�;[�;I"Clean up unlinked <urls>;T; 0;!@2;4i�;"F;5o;6;7F;8i�;9i�;$@I;:T;%I"�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;T;&I"def clean_unlinked_urls;T;'To;;F;
;;;;I"String#set_type;F;[[I"	kind;TI"nil;T;[[@�i�;F;:
set_type;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@@;4i�;$@I;:T;%I"[def set_type(kind = nil)
  if kind
    case kind.to_s
    when /^a/i
      gsub(/^\[ *| *\]$/, '').split(/ *, */)
    when /^i/i
      to_i
    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;T;&I"def set_type(kind = nil);T;'To;;F;
;;;;I"String#chronify;F;[[I"**options;T0;[[@�i ;T;:
chronify;;;[�;{�;IC;"&Converts input string into a Time object when input
takes on the following formats:
            - interval format e.g. '1d2h30m', '45m'
              etc.
            - a semantic phrase e.g. 'yesterday
              5:30pm'
            - a strftime e.g. '2016-03-15 15:32:04
              PDT';T;[
o;)
;*I"
param;F;+I"Additional options;T;I"options;T;,0;!@Oo;C;*I"option;F;+0;I"options;T;,0;Do;E
;*I"option;F;+I"(assume future date
(default: false);T;I":future;T;,[I"Boolean;T;F0;!@Oo;C;*I"option;F;+0;I"options;T;,0;Do;E
;*I"option;F;+I"F:begin or :end to
assume beginning or end of
arbitrary time range;T;I":guess;T;,[I"Symbol;T;F0;!@Oo;)
;*I"return;F;+I"result;T;0;,[I"
DateTime;T;!@Oo;)
;*I"
raise;F;+@.;0;,[I"InvalidTimeExpression;T;!@O;[�;I"�
Converts input string into a Time object when input
takes on the following formats:
            - interval format e.g. '1d2h30m', '45m'
              etc.
            - a semantic phrase e.g. 'yesterday
              5:30pm'
            - a strftime e.g. '2016-03-15 15:32:04
              PDT'

@param      options  Additional options

@option options :future [Boolean] assume future date
                                  (default: false)

@option options :guess  [Symbol] :begin or :end to
                                  assume beginning or end of
                                  arbitrary time range

@return     [DateTime] result
;T; 0;!@O;4i�;"T;5o;6;7F;8i;9i;$@I;:T;%I"def chronify(**options)
  now = Time.now
  raise 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
    now - secs_ago
  else
    Chronic.parse(self, { guess: options.fetch(:guess, :begin), context: options.fetch(:future, false) ? :future : :past, ambiguous_time_range: 8 })
  end
end;T;&I"def chronify(**options);T;'To;;F;
;;;;I"String#chronify_qty;F;[�;[[@�i>;T;:chronify_qty;;;[�;{�;IC;"�Converts simple strings into seconds that can be
added to a Time object

Input string can be HH:MM or XX[dhm][[XXhm][XXm]]
(1d2h30m, 45m, 1.5d, 1h20m, etc.);T;[o;)
;*I"return;F;+I"seconds;T;0;,[I"Integer;T;!@~;[�;I"�
Converts simple strings into seconds that can be
added to a Time object

Input string can be HH:MM or XX[dhm][[XXhm][XXm]]
(1d2h30m, 45m, 1.5d, 1h20m, etc.)

@return     [Integer] seconds
;T; 0;!@~;4i�;"T;5o;6;7F;8i5;9i=;$@I;:T;%I"Jdef 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])?$/
    amt = Regexp.last_match(1)
    type = Regexp.last_match(2).nil? ? 'm' : Regexp.last_match(2)

    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
                minutes
              end
  end
  minutes * 60
end;T;&I"def chronify_qty;T;'T;V@I;WIC;[�;V@I;XIC;[o;c;d@I;eI"Doing::Color;T;f0;;�;$@;h@M
;i;�;V@I;YIC;Z{;[IC;Z{�;\T;IC;Z{�;\T;\T;_{�;`[�;[[@�i[@�i
;T;:String;;;;;[�;{�;IC;"!Chronify methods for strings;T;[�;[�;I"!Chronify methods for strings;T; 0;!@I;4i�;"F;5o;6;7F;8i	;9i	;$@;I"String;F;bo;c;d0;e0;f0;;g;$@;h0;i;[;'To;
;IC;[o;;F;
;;;;I"Symbol#normalize_bool;F;[[I"default;TI"	:and;T;[[@�i
;F;;i;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@�;:T;%I"Jdef normalize_bool(default = :and)
  to_s.normalize_bool(default)
end;T;&I"'def normalize_bool(default = :and);T;'To;;F;
;;;;I"Symbol#normalize_order;F;[[I"default;TI"
'asc';T;[[@�i;F;;e;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@�;:T;%I"Mdef normalize_order(default = 'asc')
  to_s.normalize_order(default)
end;T;&I")def normalize_order(default = 'asc');T;'To;;F;
;;;;I"Symbol#normalize_case;F;[�;[[@�i;F;;g;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@�;:T;%I""def normalize_case
  self
end;T;&I"def normalize_case;T;'T;V@�;WIC;[�;V@�;XIC;[�;V@�;YIC;Z{;[IC;Z{�;\T;IC;Z{�;\T;\T;_{�;`[�;[[@�i;T;:Symbol;;;;;[�;{�;IC;"Symbol helpers;T;[�;[�;I"
Symbol helpers
;T; 0;!@�;4i�;"T;5o;6;7F;8i	;9i;$@;I"Symbol;F;bo;c;d0;e0;f0;;g;$@;h0;i;[;'To;	;IC;[o;;F;
;;;;I"Status#cols;F;[�;[[I"lib/doing/cli_status.rb;Ti;F;:	cols;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@�;:T;%I"4def cols
  @cols ||= `tput cols`.strip.to_i
end;T;&I"
def cols;T;'To;;F;
;;;;I"Status#progress;F;[	[I"msg;T0[I"idx;T0[I"
total;T0[I"	tail;TI"[];T;[[@�i;F;:
progress;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@�;:T;%I"Adef 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;T;&I"-def progress(msg, idx, total, tail = []);T;'To;;F;
;;;;I"Status#status;F;[[I"msg;T0[I"reset:;TI"	true;T[I"end_char:;TI"	"\n";T;[[@�i;F;:status;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@;4i�;$@�;:T;%I"�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;T;&I"1def status(msg, reset: true, end_char: "\n");T;'To;;F;
;;;;I"Status#msg;F;[	[I"msg;T0[I"reset:;TI"	true;T[I"color:;TI"'green';T[I"end_char:;TI"	"\n";T;[[@�i;F;:msg;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@;4i�;$@�;:T;%I"�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;T;&I">def msg(msg, reset: true, color: 'green', end_char: "\n");T;'To;;F;
;;;;I"Status#clear;F;[�;[[@�i;F;:
clear;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@3;4i�;$@�;:T;%I"=def clear
  $stderr.print format("\r#{esc['kill']}")
end;T;&I"def clear;T;'To;;F;
;;;;I"Status#esc;F;[�;[[@�i";F;:esc;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@?;4i�;$@�;:T;%I"&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;T;&I"def esc;T;'T;V@�;WIC;[�;V@�;XIC;[�;V@�;YIC;Z{;[IC;Z{�;\T;IC;Z{�;\T;\T;_{�;`[�;[[@�i;F;:Status;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@;I"Status;Fo;	;IC;[o;	;IC;[o;
;IC;[o;;F;
;;;;I"7GLI::Commands::MarkdownDocumentListener#initialize;F;[	[I"_global_options;T0[I"
_options;T0[I"_arguments;T0[I"app;T0;[[I",lib/doing/markdown_document_listener.rb;Ti;F;;;;;;[�;{�;IC;"�;T;[o;)
;*I"return;F;+I"/a new instance of MarkdownDocumentListener;T;0;,[I"MarkdownDocumentListener;F;!@`;[�;@.; 0;!@`;4i�;$@^;:T;%I"�def initialize(_global_options, _options, _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;T;&I"?def initialize(_global_options, _options, _arguments, app);T;'To;;F;
;;;;I"6GLI::Commands::MarkdownDocumentListener#beginning;F;[�;[[@mi;F;:beginning;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@z;4i�;$@^;:T;%I"def beginning
end;T;&I"def beginning;T;'To;;F;
;;;;I"3GLI::Commands::MarkdownDocumentListener#ending;F;[�;[[@mi;T;:ending;;;[�;{�;IC;")Called when processing has completed;T;[�;[�;I")Called when processing has completed;T; 0;!@�;4i�;"F;5o;6;7F;8i;9i;$@^;:T;%I"}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;T;&I"def ending;T;'To;;F;
;;;;I"9GLI::Commands::MarkdownDocumentListener#program_desc;F;[[I"	desc;T0;[[@mi4;T;:program_desc;;;[�;{�;IC;"&Gives you the program description;T;[�;[�;I"&Gives you the program description;T; 0;!@�;4i�;"F;5o;6;7F;8i3;9i3;$@^;:T;%I"`def program_desc(desc)
  @io.puts "# #{@exe} CLI"
  @io.puts
  @io.puts desc
  @io.puts
end;T;&I"def program_desc(desc);T;'To;;F;
;;;;I">GLI::Commands::MarkdownDocumentListener#program_long_desc;F;[[I"	desc;T0;[[@mi;;F;:program_long_desc;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@^;:T;%I"Fdef program_long_desc(desc)
  @io.puts "> #{desc}"
  @io.puts
end;T;&I" def program_long_desc(desc);T;'To;;F;
;;;;I"4GLI::Commands::MarkdownDocumentListener#version;F;[[I"version;T0;[[@miA;T;:version;;;[�;{�;IC;""Gives you the program version;T;[�;[�;I""Gives you the program version;T; 0;!@�;4i�;"F;5o;6;7F;8i@;9i@;$@^;:T;%I"�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;T;&I"def version(version);T;'To;;F;
;;;;I"4GLI::Commands::MarkdownDocumentListener#options;F;[�;[[@miK;F;:options;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@^;:T;%I"def options
  if @nest.size == 1
    @io.puts "## Global Options"
  else
    @io.puts header("Options", 1)
  end
  @io.puts
end;T;&I"def options;T;'To;;F;
;;;;I"1GLI::Commands::MarkdownDocumentListener#flag;F;[
[I"	name;T0[I"aliases;T0[I"	desc;T0[I"long_desc;T0[I"default_value;T0[I"
arg_name;T0[I"must_match;T0[I"
_type;T0;[[@miU;T;:	flag;;;[�;{�;IC;",Gives you a flag in the current context;T;[�;[�;I",Gives you a flag in the current context;T; 0;!@�;4i�;"F;5o;6;7F;8iT;9iT;$@^;:T;%I"6def 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;T;&I"Ydef flag(name, aliases, desc, long_desc, default_value, arg_name, must_match, _type);T;'To;;F;
;;;;I"3GLI::Commands::MarkdownDocumentListener#switch;F;[
[I"	name;T0[I"aliases;T0[I"	desc;T0[I"long_desc;T0[I"negatable;T0;[[@mic;T;:switch;;;[�;{�;IC;".Gives you a switch in the current context;T;[�;[�;I".Gives you a switch in the current context;T; 0;!@�;4i�;"F;5o;6;7F;8ib;9ib;$@^;:T;%I"�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;T;&I":def switch(name, aliases, desc, long_desc, negatable);T;'To;;F;
;;;;I"8GLI::Commands::MarkdownDocumentListener#end_options;F;[�;[[@miq;F;:end_options;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@;4i�;$@^;:T;%I"def end_options
end;T;&I"def end_options;T;'To;;F;
;;;;I"5GLI::Commands::MarkdownDocumentListener#commands;F;[�;[[@mit;F;:
commands;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@;4i�;$@^;:T;%I"Rdef commands
  @io.puts header("Commands", 1)
  @io.puts
  increment_nest
end;T;&I"def commands;T;'To;;F;
;;;;I"4GLI::Commands::MarkdownDocumentListener#command;F;[[I"	name;T0[I"aliases;T0[I"	desc;T0[I"long_desc;T0[I"
arg_name;T0[I"arg_options;T0;[[@mi{;T;:command;;;[�;{�;IC;"YGives you a command in the current context and creates a new context of this command;T;[�;[�;I"YGives you a command in the current context and creates a new context of this command;T; 0;!@;4i�;"F;5o;6;7F;8iz;9iz;$@^;:T;%I"2def command(name, aliases, desc, long_desc, arg_name, arg_options)
  @parent_command.push ([name] + aliases).join('|')
  arg_name_fmt = @arg_name_formatter.format(arg_name, arg_options, [])
  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;T;&I"Gdef command(name, aliases, desc, long_desc, arg_name, arg_options);T;'To;;F;
;;;;I"8GLI::Commands::MarkdownDocumentListener#end_command;F;[[I"
_name;T0;[[@mi�;T;:end_command;;;[�;{�;IC;"7Ends a command, and "pops" you back up one context;T;[�;[�;I"7Ends a command, and "pops" you back up one context;T; 0;!@6;4i�;"F;5o;6;7F;8i�;9i�;$@^;:T;%I"ydef end_command(_name)
  @parent_command.pop
  decrement_nest
  @io.puts "* * * * * *\n\n" unless @nest.size > 2
end;T;&I"def end_command(_name);T;'To;;F;
;;;;I"<GLI::Commands::MarkdownDocumentListener#default_command;F;[[I"	name;T0;[[@mi�;T;:default_command;;;[�;{�;IC;"EGives you the name of the current command in the current context;T;[�;[�;I"EGives you the name of the current command in the current context;T; 0;!@F;4i�;"F;5o;6;7F;8i�;9i�;$@^;:T;%I"_def default_command(name)
  @io.puts "#### [Default Command] #{name}" unless name.nil?
end;T;&I"def default_command(name);T;'To;;F;
;;;;I"9GLI::Commands::MarkdownDocumentListener#end_commands;F;[�;[[@mi�;F;:end_commands;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@V;4i�;$@^;:T;%I"*def end_commands
  decrement_nest
end;T;&I"def end_commands;T;'To;;F;
;;;O;I"7GLI::Commands::MarkdownDocumentListener#add_dashes;F;[[I"	name;T0;[[@mi�;F;:add_dashes;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@b;4i�;$@^;:T;%I"_def add_dashes(name)
  name = "-#{name}"
  name = "-#{name}" if name.length > 2
  name
end;T;&I"def add_dashes(name);T;'To;;F;
;;;O;I"3GLI::Commands::MarkdownDocumentListener#header;F;[[I"content;T0[I"increment;T0;[[@mi�;F;:header;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@p;4i�;$@^;:T;%I"�def header(content, increment)
  if @nest.size + increment > 6
    "**#{content}**"
  else
    "#{@nest}#{'#'*increment} #{content}"
  end
end;T;&I"#def header(content, increment);T;'To;;F;
;;;O;I";GLI::Commands::MarkdownDocumentListener#increment_nest;F;[[I"increment;TI"1;T;[[@mi�;F;:increment_nest;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@^;:T;%I"Mdef increment_nest(increment=1)
  @nest = "#{@nest}#{'#'*increment}"
end;T;&I"$def increment_nest(increment=1);T;'To;;F;
;;;O;I";GLI::Commands::MarkdownDocumentListener#decrement_nest;F;[[I"increment;TI"1;T;[[@mi�;F;:decrement_nest;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@�;4i�;$@^;:T;%I"Odef decrement_nest(increment=1)
  @nest.gsub!(/#{'#'*increment}$/, '')
end;T;&I"$def decrement_nest(increment=1);T;'T;V@^;WIC;[�;V@^;XIC;[�;V@^;YIC;Z{;[IC;Z{�;\T;IC;Z{�;\T;\T;_{�;`[�;[[@mi
;T;:MarkdownDocumentListener;;;;;[�;{�;IC;";DocumentListener class for GLI documentation generator;T;[�;[�;I";DocumentListener class for GLI documentation generator;T; 0;!@^;4i�;"F;5o;6;7F;8i;9i;$@\;I",GLI::Commands::MarkdownDocumentListener;F;bo;c;d0;e0;f0;;g;$@;h0;i;[;'T;V@\;WIC;[�;V@\;XIC;[�;V@\;YIC;Z{;[IC;Z{�;\T;IC;Z{�;\T;\T;_{�;`[�;[[@mi;F;:
Commands;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@\;4i�;$@Z;I"GLI::Commands;F;'T;V@Z;WIC;[�;V@Z;XIC;[�;V@Z;YIC;Z{;[IC;Z{�;\T;IC;Z{�;\T;\T;_{�;`[�;[[@mi
;F;:GLI;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@Z;4i�;$@;I"GLI;F;V@;WIC;[�;V@;XIC;[�;V@;YIC;Z{;[IC;Z{�;\T;IC;Z{�;\T;\T;_{�;`[�;[�;F;;�;;;;;[�;{�;IC;"�;T;[�;[�;@.; 0;!@;4i�;$0;@.;N@;T@�:Hash#deep_freeze@�:Hash#deep_freeze!@�:Hash#stringify_keys@�:Hash#symbolize_keys@�:Hash#deep_set@�:Doing::Item@
:Doing::Item#date@:Doing::Item#date=@:Doing::Item#title@-:Doing::Item#title=@::Doing::Item#section@L:Doing::Item#section=@Y:Doing::Item#note@k:Doing::Item#note=@x:Doing::Item#id@�:Doing::Item#initialize@�:Doing::Item#duration@�:Doing::Item#interval@�:Doing::Item#end_date@�:Doing::Item#equal?@�:Doing::Item#same_time?@:"Doing::Item#overlapping_time?@5:Doing::Item#tag@P:Doing::Item#tags@�:Doing::Item#tags?@�:Doing::Item#search@�:Doing::Item#should_finish?@#:Doing::Item#should_time?@4:Doing::Item#move_to@C:Doing::Item#to_s@n:Doing::Item#inspect@|:Doing::Item#should?@�:Doing::Item#calc_interval@�:Doing::Item#all_tags?@�:Doing::Item#no_tags?@�:Doing::Item#any_tags?@�:Doing::Item#split_tags@�:Doing::Note@:Doing::Note#initialize@:Doing::Note#add@!:Doing::Note#append@@:Doing::Note#append_string@V:Doing::Note#compress!@l:Doing::Note#compress@x:Doing::Note#strip_lines!@�:Doing::Note#strip_lines@�:Doing::Note#to_s@�:Doing::Note#inspect@�:Doing::Note#equal?@�;V@):Time#relative_date@+:Doing::Util@y:Doing::Util#user_home@{:Doing::Util#exec_available@�:%Doing::Util#first_available_exec@�:#Doing::Util#merge_default_proc@�:(Doing::Util#duplicate_frozen_values@�:"Doing::Util#deep_merge_hashes@�:#Doing::Util#deep_merge_hashes!@�:Doing::Util#duplicable?@:Doing::Util#mergable?@:Doing::Util#merge_values@%:Doing::Util#write_to_file@5:Doing::Util#safe_load_file@\:Doing::Util#default_editor@j:!Doing::Util#editor_with_args@v: Doing::Util#args_for_editor@�:$Doing::Util#find_default_editor@�:Doing::WWID@�:#Doing::WWID#additional_configs@�: Doing::WWID#current_section@�:Doing::WWID#doing_file@�:Doing::WWID#content@�:Doing::WWID#config@�:Doing::WWID#config=@�:Doing::WWID#config_file@:Doing::WWID#config_file=@:Doing::WWID#auto_tag@%:Doing::WWID#auto_tag=@2:Doing::WWID#default_option@D: Doing::WWID#default_option=@Q:Doing::WWID#initialize@c:Doing::WWID#logger@v: Doing::WWID#init_doing_file@�:Doing::WWID#create@�:Doing::WWID#fork_editor@�:Doing::WWID#format_input@�:Doing::WWID#sections@�:Doing::WWID#guess_section@�:Doing::WWID#guess_view@:Doing::WWID#add_item@@:Doing::WWID#dedup@�:Doing::WWID#import@�:Doing::WWID#last_note@�:Doing::WWID#reset_item@�:Doing::WWID#repeat_item@:Doing::WWID#repeat_last@J:Doing::WWID#last_entry@a:Doing::WWID#all_tags@x:Doing::WWID#tag_groups@�:#Doing::WWID#fuzzy_filter_items@�:Doing::WWID#filter_items@�:Doing::WWID#interactive@N	:Doing::WWID#act_on@i	:Doing::WWID#tag_last@�	:Doing::WWID#next_item@�	:Doing::WWID#edit_last@%
:Doing::WWID#stop_start@?
:Doing::WWID#write@�
:Doing::WWID#restore_backup@�
:Doing::WWID#rotate@�
:Doing::WWID#choose_section@�
:Doing::WWID#views@�
:Doing::WWID#choose_view@�
:Doing::WWID#get_view@:Doing::WWID#list_section@:Doing::WWID#archive@2:Doing::WWID#today@R:Doing::WWID#list_date@{:Doing::WWID#yesterday@�:Doing::WWID#recent@�:Doing::WWID#last@:Doing::WWID#autotag@1:Doing::WWID#tag_times@G:Doing::WWID#get_interval@p:Doing::WWID#format_time@�:!Doing::WWID#combined_content@�:Doing::WWID#output@�:!Doing::WWID#record_tag_times@�:Doing::WWID#do_archive@
:Doing::WWID#run_after@>
;r@�:Array#to_tags@�:Array#to_tags!@:Array#highlight_tags@ :Array#log_tags@<:Array#nested_hash@O:Doing::Hooks@:#Doing::Hooks::DEFAULT_PRIORITY@:Doing::Hooks.register@: Doing::Hooks.priority_value@':Doing::Hooks.register_one@7:Doing::Hooks.insert_hook@O:Doing::Hooks.trigger@a:Doing::Items@�:Doing::Items#sections@�:Doing::Items#sections=@�:Doing::Items#initialize@�: Doing::Items#section_titles@�:Doing::Items#section?@�:Doing::Items#add_section@�:Doing::Items#in_section@:Doing::Items#delete_item@ :Doing::Items#update_item@7:Doing::Items#to_s@U:Doing::Items#inspect@c:Doing::Pager@�:Doing::Pager.paginate@�:Doing::Pager.paginate=@�:Doing::Pager.page@�: Doing::Pager.command_exist?@�:Doing::Pager.git_pager@�:Doing::Pager.pagers@�:!Doing::Pager.find_executable@�:!Doing::Pager.exec_available?@�:Doing::Pager.which_pager@:Doing::Color@M
:Doing::Color::ATTRIBUTES@O
:"Doing::Color::ATTRIBUTE_NAMES@]
:Doing::Color#support?@h
:Doing::Color.coloring?@{
:Doing::Color.coloring=@�
:Doing::Color.coloring@�
:!Doing::Color::COLORED_REGEXP@�
:Doing::Color#uncolor@�
:Doing::Color#attributes@�
:Doing::Color.attributes@�
:Doing::Errors@):!Doing::Errors::UserCancelled@+:,Doing::Errors::UserCancelled#initialize@-:Doing::Errors::EmptyInput@U:)Doing::Errors::EmptyInput#initialize@W:&Doing::Errors::DoingStandardError@~:1Doing::Errors::DoingStandardError#initialize@�: Doing::Errors::WrongCommand@�:+Doing::Errors::WrongCommand#initialize@�:%Doing::Errors::DoingRuntimeError@�:0Doing::Errors::DoingRuntimeError#initialize@�:Doing::Errors::NoResults@�:(Doing::Errors::NoResults#initialize@�:%Doing::Errors::DoingNoTraceError@:0Doing::Errors::DoingNoTraceError#initialize@!:#Doing::Errors::PluginException@K:*Doing::Errors::PluginException#plugin@M:.Doing::Errors::PluginException#initialize@Z:#Doing::Errors::HookUnavailable@�:%Doing::Errors::InvalidPluginType@�:$Doing::Errors::PluginUncallable@�:#Doing::Errors::InvalidArgument@�:#Doing::Errors::MissingArgument@�:Doing::Errors::MissingFile@�:!Doing::Errors::MissingEditor@�:"Doing::Errors::NonInteractive@�: Doing::Errors::NoEntryError@�:)Doing::Errors::InvalidTimeExpression@�:"Doing::Errors::InvalidSection@�:Doing::Errors::InvalidView@�: Doing::Errors::ItemNotFound@	:Doing::Prompt@#: Doing::Prompt.force_answer=@%:"Doing::Prompt.default_answer=@8:Doing::Prompt.force_answer@J:!Doing::Prompt.default_answer@V:Doing::Prompt.yn@b:Doing::Prompt.fzf@�:Doing::Prompt.install_fzf@�:Doing::Prompt.choose_from@�:$Doing::Prompt.choose_from_items@�;z@I:String#is_rx?@K:String#to_rx@]:String#truthy?@�:String#compress@�:String#compress!@�:String#highlight_tags!@�:String#highlight_tags@�:String#ignore?@�:String#truncate@�:String#truncate!@:String#truncmiddle@:String#truncmiddle!@8:String#uncolor@I:String#uncolor!@Z:String#wrap@f:String#cap_first@�:String#normalize_order!@�:String#normalize_order@�:String#normalize_case!@�:String#normalize_case@�:String#normalize_bool!@�:String#normalize_bool@:String#normalize_trigger!@:String#normalize_trigger@":String#to_tags@.:String#add_tags!@::String#add_tags@K:String#tag!@\:String#tag@q:String#dedup_tags!@�:String#dedup_tags@�:String#last_color@�:String#link_urls!@�:String#link_urls@:String#remove_self_links@:"String#replace_qualified_urls@":String#clean_unlinked_urls@2:String#set_type@@;{@�:Symbol#normalize_bool@�:Symbol#normalize_order@�:Symbol#normalize_case@�:Doing::Section@@:Doing::Section#original@B:Doing::Section#original=@P:Doing::Section#title@b:Doing::Section#title=@o:Doing::Section#initialize@�:Doing::Section#to_s@�:Doing::Section#inspect@�:Doing::VERSION@�;�@�:Status#cols@�:Status#progress@�:Status#status@:Status#msg@:Status#clear@3:Status#esc@?:Doing::Completion@�:*Doing::Completion.generate_completion@�:Doing::LogAdapter@
:Doing::LogAdapter#logdev=@:"Doing::LogAdapter#max_length=@:Doing::LogAdapter#level@+:Doing::LogAdapter#messages@9:Doing::LogAdapter#results@F:#Doing::LogAdapter::TOPIC_WIDTH@S:"Doing::LogAdapter::LOG_LEVELS@^:"Doing::LogAdapter::COUNT_KEYS@i:!Doing::LogAdapter#initialize@t:!Doing::LogAdapter#log_level=@�:!Doing::LogAdapter#temp_level@�:$Doing::LogAdapter#restore_level@�:'Doing::LogAdapter#adjust_verbosity@�:Doing::LogAdapter#count@�:Doing::LogAdapter#debug@�:Doing::LogAdapter#info@:Doing::LogAdapter#warn@6:Doing::LogAdapter#error@V:!Doing::LogAdapter#abort_with@v:&Doing::LogAdapter#formatted_topic@�:Doing::LogAdapter#write@�:Doing::LogAdapter#log_now@�:%Doing::LogAdapter#output_results@:!Doing::LogAdapter#log_change@$:%Doing::LogAdapter#format_counter@?:%Doing::LogAdapter#total_counters@O:%Doing::LogAdapter#write_message?@[:Doing::LogAdapter#message@s:$Doing::LogAdapter#color_message@�:Doing::Configuration@�:"Doing::Configuration#settings@�:'Doing::Configuration#ignore_local=@�:,Doing::Configuration::MissingConfigFile@�:#Doing::Configuration::DEFAULTS@�:$Doing::Configuration#initialize@�:%Doing::Configuration#config_file@:&Doing::Configuration#config_file=@:$Doing::Configuration#config_dir@*:-Doing::Configuration#default_config_file@6:,Doing::Configuration#additional_configs@F:'Doing::Configuration#choose_config@R:*Doing::Configuration#resolve_key_path@e:'Doing::Configuration#value_for_key@�:Doing::Configuration#from@�:2Doing::Configuration#update_deprecated_config@�:#Doing::Configuration#configure@�:!Doing::Configuration#inspect@�:Doing::Configuration#to_s@�:+Doing::Configuration#find_deprecations@�:&Doing::Configuration#local_config@
:,Doing::Configuration#read_local_configs@:%Doing::Configuration#read_config@':+Doing::Configuration#find_local_config@5:&Doing::Configuration#load_plugins@H:Doing::Plugins@k:Doing::Plugins.user_home@m:Doing::Plugins.plugins@z: Doing::Plugins.load_plugins@�: Doing::Plugins.plugins_path@�:Doing::Plugins.register@�:#Doing::Plugins.validate_plugin@�:Doing::Plugins.valid_type@�: Doing::Plugins.list_plugins@�:%Doing::Plugins.available_plugins@�: Doing::Plugins.plugin_names@: Doing::Plugins.plugin_regex@2:$Doing::Plugins.plugin_templates@G:"Doing::Plugins.template_regex@V:(Doing::Plugins.template_for_trigger@e:String#chronify@O:String#chronify_qty@~;�@Z:GLI::Commands@\:,GLI::Commands::MarkdownDocumentListener@^:7GLI::Commands::MarkdownDocumentListener#initialize@`:6GLI::Commands::MarkdownDocumentListener#beginning@z:3GLI::Commands::MarkdownDocumentListener#ending@�:9GLI::Commands::MarkdownDocumentListener#program_desc@�:>GLI::Commands::MarkdownDocumentListener#program_long_desc@�:4GLI::Commands::MarkdownDocumentListener#version@�:4GLI::Commands::MarkdownDocumentListener#options@�:1GLI::Commands::MarkdownDocumentListener#flag@�:3GLI::Commands::MarkdownDocumentListener#switch@�:8GLI::Commands::MarkdownDocumentListener#end_options@:5GLI::Commands::MarkdownDocumentListener#commands@:4GLI::Commands::MarkdownDocumentListener#command@:8GLI::Commands::MarkdownDocumentListener#end_command@6:<GLI::Commands::MarkdownDocumentListener#default_command@F:9GLI::Commands::MarkdownDocumentListener#end_commands@V:7GLI::Commands::MarkdownDocumentListener#add_dashes@b:3GLI::Commands::MarkdownDocumentListener#header@p:;GLI::Commands::MarkdownDocumentListener#increment_nest@�:;GLI::Commands::MarkdownDocumentListener#decrement_nest@�