Class: Fatty::History

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/fatty/history.rb,
lib/fatty/history/entry.rb

Defined Under Namespace

Classes: Entry

Constant Summary collapse

DEFAULT_HISTORY_FILE =
File.expand_path("~/.fatty_history")
DEFAULT_HISTORY_MAX =
10_000

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path: nil, max: nil) ⇒ History

Returns a new instance of History.



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/fatty/history.rb', line 12

def initialize(path: nil, max: nil)
  @path =
    case path
    when :default
      Config.config.dig(:history, :file) || DEFAULT_HISTORY_FILE
    when nil, false
      nil
    else
      path
    end
  @path = File.expand_path(@path) if @path
  @max = max&.to_i || Config.config.dig(:history, :max)&.to_i || DEFAULT_HISTORY_MAX
  @entries = []
  @cursors = {}

  if @path
    Fatty.info("History loaded from #{@path}")
    load
  else
    Fatty.info("In-memory History only: no path")
  end
end

Instance Attribute Details

#entriesObject (readonly)

Returns the value of attribute entries.



10
11
12
# File 'lib/fatty/history.rb', line 10

def entries
  @entries
end

Class Method Details

.defaultObject

A global default History object available to all sessions. Sets a class instance variable.



37
38
39
# File 'lib/fatty/history.rb', line 37

def self.default
  for_path(:default)
end

.for_path(path = :default) ⇒ Object



41
42
43
44
# File 'lib/fatty/history.rb', line 41

def self.for_path(path = :default)
  @instances ||= {}
  @instances[path] ||= new(path: path)
end

.normalize_ctx(ctx) ⇒ Object



182
183
184
185
186
187
188
# File 'lib/fatty/history.rb', line 182

def self.normalize_ctx(ctx)
  return {} unless ctx.is_a?(Hash)

  ctx.each_with_object({}) { |(key, value), memo|
    memo[key.to_s] = value
  }.sort.to_h
end

.reset_instances!Object



46
47
48
# File 'lib/fatty/history.rb', line 46

def self.reset_instances!
  @instances = {}
end

Instance Method Details

#add(text, kind: :command, ctx: nil, stamp: nil, persist: true) ⇒ Object

Manipulating History



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/fatty/history.rb', line 87

def add(text, kind: :command, ctx: nil, stamp: nil, persist: true)
  text = text.to_s
  return if text.strip.empty?

  kind = kind.to_sym
  ctx = normalize_ctx(ctx)
  entry = Entry.new(text: text, kind: kind, ctx: ctx, stamp: stamp)

  @entries.reject! do |old_entry|
    old_entry.text == text &&
      old_entry.kind == kind &&
      old_entry.ctx == ctx
  end

  @entries << entry
  truncate!
  append_to_file(entry) if persist
  reset_cursor_for(kind, ctx: ctx)
  text
end

#each(&block) ⇒ Object



60
61
62
63
64
# File 'lib/fatty/history.rb', line 60

def each(&block)
  return enum_for(:each) unless block

  @entries.each(&block)
end

#for(kind: nil, ctx: nil) ⇒ Object



66
67
68
69
70
71
72
73
74
75
# File 'lib/fatty/history.rb', line 66

def for(kind: nil, ctx: nil)
  return enum_for(:for, kind: kind, ctx: ctx) unless block_given?

  each do |entry|
    next if kind && entry.kind != kind.to_sym
    next if ctx && !ctx_match?(entry, ctx)

    yield entry
  end
end

#nextObject



112
113
114
# File 'lib/fatty/history.rb', line 112

def next
  next_for(:command)
end

#next_for(*kinds, ctx: nil) ⇒ Object



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/fatty/history.rb', line 143

def next_for(*kinds, ctx: nil)
  ctx = normalize_ctx(ctx)
  cursor = cursor_for(*kinds, ctx: ctx)
  the_entries = entries_for(*kinds, ctx: ctx, prefix: cursor[:prefix])
  return "" if the_entries.empty? || cursor[:index].nil?

  if cursor[:index] < the_entries.length - 1
    cursor[:index] += 1
    the_entries[cursor[:index]].text
  else
    scratch = cursor[:scratch]
    reset_cursor_for(*kinds, ctx: ctx)
    scratch || ""
  end
end

#previous(current) ⇒ Object



108
109
110
# File 'lib/fatty/history.rb', line 108

def previous(current)
  previous_for(:command, current: current)
end

#previous_for(*kinds, current:, ctx: nil) ⇒ Object



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/fatty/history.rb', line 120

def previous_for(*kinds, current:, ctx: nil)
  ctx = normalize_ctx(ctx)
  cursor = cursor_for(*kinds, ctx: ctx)
  prefix = cursor[:prefix]

  if cursor[:index].nil?
    cursor[:scratch] = current.to_s
    cursor[:prefix] = current.to_s
    prefix = cursor[:prefix]
  end

  the_entries = entries_for(*kinds, ctx: ctx, prefix: prefix)
  return current.to_s if the_entries.empty?

  if cursor[:index].nil?
    cursor[:index] = the_entries.length - 1
    return the_entries[cursor[:index]].text
  end

  cursor[:index] -= 1 if cursor[:index].positive?
  the_entries[cursor[:index]].text
end

#recent(kind: nil, ctx: nil, limit: nil) ⇒ Object



77
78
79
80
81
# File 'lib/fatty/history.rb', line 77

def recent(kind: nil, ctx: nil, limit: nil)
  rows = self.for(kind: kind, ctx: ctx).to_a
  rows = rows.last(limit) if limit
  rows.reverse
end

#reset_cursorObject



116
117
118
# File 'lib/fatty/history.rb', line 116

def reset_cursor
  reset_cursor_for(:command)
end

#reset_cursor_for(*kinds, ctx: nil) ⇒ Object



159
160
161
162
163
164
165
# File 'lib/fatty/history.rb', line 159

def reset_cursor_for(*kinds, ctx: nil)
  ctx = normalize_ctx(ctx)
  cursor = cursor_for(*kinds, ctx: ctx)
  cursor[:index] = nil
  cursor[:prefix] = nil
  cursor[:scratch] = nil
end

#suggest_for(*kinds, prefix:, ctx: nil) ⇒ Object



167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/fatty/history.rb', line 167

def suggest_for(*kinds, prefix:, ctx: nil)
  text = prefix.to_s
  return if text.empty?

  wanted_ctx = normalize_ctx(ctx)

  unless wanted_ctx.empty?
    local = entries_for(*kinds, ctx: wanted_ctx, prefix: text)
    return local.last.text unless local.empty?
  end

  global = entries_for(*kinds, prefix: text)
  global.last&.text
end