mars_base_10 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d66886ffab2da6f395505f68090aa88ea77f7e9bb17669c79a562d9298338227
4
- data.tar.gz: '084f5515249cad9b00649ccf84743e848e835cc3b234f95608efb6bcd5bf5ab7'
3
+ metadata.gz: fd093ec957e418592f791bb2dfc1e1e7efa56973fd553534278a6dcb818f3482
4
+ data.tar.gz: 478b185df1d0c2280788691e4bd61bfac817c200348e63bb29a7a36e05f1eb84
5
5
  SHA512:
6
- metadata.gz: 8c4284d80f4071dfde88bef0f7cf1dfa2f684675a8861b13527740f891bf67bb59e48a6cc95afe627425d1017cd6f355ba8075def3e144ac0066287d90c84ab3
7
- data.tar.gz: 46fdbcd3ddae7f293f21bd067c042555ce235daef318f4431edc2d30ea05ce92f0783bf96c7103455f3c41f15f1c2f2f94b8dc5382780d7a6ea4f2fb89e6e052
6
+ metadata.gz: e9044477c9eb149f2212f5dd5e7e44eb8d0fa247e0db7a382aa5256708ba53027e8c48c18f992b4c2c7dde36281cd5f725f86662283d71f713f50ed6a08662a2
7
+ data.tar.gz: ac4c86d4ab841a92e262138bdf6a290af01d93e65d1afe514d6076681bb217dc59bde4ec2e90ccd3e10d4936ae298387ae0d662565e3030e88658a1f0a78deb6
data/lib/.DS_Store ADDED
Binary file
@@ -11,7 +11,7 @@ module MarsBase10
11
11
  end
12
12
 
13
13
  def self.Default
14
- ActionBar.new actions: {'j': 'Move Down', 'k': 'Move Up', 'q': 'Quit'}
14
+ ActionBar.new actions: {'j': 'Move Down', 'k': 'Move Up', 'J': 'Page Down', 'K': 'Page Up', 'q': 'Quit'}
15
15
  end
16
16
 
17
17
  def actions_first_col
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- require 'urbit/urbit'
2
+ require 'urbit'
3
3
 
4
4
  require_relative 'graph_rover'
5
5
  require_relative 'viewport'
@@ -14,66 +14,69 @@ module MarsBase10
14
14
  @viewport = viewport
15
15
  @viewport.controller = self
16
16
 
17
- @panes = []
17
+ self.wire_up_panes
18
+ self.viewport.action_bar = ActionBar.Default.add_action({'i': 'Inspect'})
19
+ self.viewport.activate pane: @graph_list_pane
20
+ self.resync
21
+ end
18
22
 
19
- # The graph list is a fixed width, variable height (full screen) pane on the left.
20
- @graph_list_pane = @viewport.add_pane width_pct: 0.3
21
- @graph_list_pane.viewing subject: @ship.graph_names
23
+ def active_node_index
24
+ @node_list_pane.current_subject_index
25
+ end
22
26
 
23
- # The node list is a variable width, fixed height pane in the upper right.
24
- @node_list_pane = @viewport.add_variable_width_pane at_col: @graph_list_pane.last_col,
25
- height_pct: 0.5
26
- @node_list_pane.viewing subject: @ship.node_list
27
+ def active_node
28
+ self.ship.fetch_node(resource: self.active_resource, index: self.active_node_index)
29
+ end
27
30
 
28
- # The single node viewer is a variable width, variable height pane in the lower right.
29
- @node_view_pane = @viewport.add_variable_both_pane at_row: @node_list_pane.last_row,
30
- at_col: @graph_list_pane.last_col
31
- @node_view_pane.viewing subject: @ship.node
31
+ def active_resource
32
+ @graph_list_pane.current_subject_index
33
+ end
32
34
 
33
- self.viewport.action_bar = ActionBar.Default.add_action({'i': 'Inspect'})
34
- self.viewport.activate pane: @graph_list_pane
35
- self.resync
35
+ def load_history
36
+ return 0 unless @node_list_pane == self.viewport.active_pane
37
+ new_content = self.ship.fetch_older_nodes(resource: self.active_resource, node: self.active_node)
38
+ @node_list_pane.subject.prepend_content(ary: new_content)
39
+ new_content.length
36
40
  end
37
41
 
38
42
  #
39
43
  # Called by a pane in this controller for bubbling a key press up
40
44
  #
41
45
  def send(key:)
46
+ resync_needed = true
42
47
  case key
43
48
  when 'd' # (D)ive
44
49
  begin
45
50
  if @node_view_pane.subject.contents[4].include?('true')
46
51
  self.viewport.action_bar.add_action({'p': 'Pop Out'})
47
- resource = @graph_list_pane.current_subject
48
- node_index = @node_list_pane.current_subject
49
- @stack.push(resource)
52
+ @stack.push(self.active_resource)
50
53
  @node_list_pane.clear
51
- @node_list_pane.subject.contents = self.ship.fetch_node_children resource: resource, index: node_index
52
- @node_list_pane.index = 0
54
+ @node_list_pane.subject.contents = self.ship.fetch_node_children(resource: self.active_resource, index: self.active_node_index)
53
55
  end
54
56
  end
55
57
  when 'i' # (I)nspect
56
58
  begin
57
59
  self.viewport.activate pane: @node_list_pane
58
60
  self.viewport.action_bar = ActionBar.Default.add_action({'d': 'Dive In', 'g': 'Graph List'})
61
+ resync_needed = false
59
62
  end
60
63
  when 'g' # (G)raph View
61
64
  unless @graph_list_pane.active?
62
65
  self.viewport.activate pane: @graph_list_pane
66
+ # resync_needed = false
63
67
  end
64
68
  when 'p' # (P)op
65
69
  begin
66
70
  if (resource = @stack.pop)
67
71
  @node_list_pane.clear
68
72
  @node_list_pane.subject.contents = self.ship.fetch_node_list(resource: resource)
69
- @node_list_pane.index = 0
70
73
  end
71
74
  if (@stack.length == 0)
72
75
  self.viewport.action_bar.remove_action(:p)
73
76
  end
74
77
  end
75
78
  end
76
- self.resync
79
+ self.resync if resync_needed
77
80
  end
78
81
 
79
82
  def start
@@ -87,25 +90,24 @@ module MarsBase10
87
90
  private
88
91
 
89
92
  def resync
90
- self.resync_node_view(self.resync_node_list)
93
+ self.resync_node_list
94
+ self.resync_node_view
91
95
  end
92
96
 
93
97
  def resync_node_list
94
- resource = @graph_list_pane.current_subject
95
98
  if @graph_list_pane == self.viewport.active_pane
96
- @node_list_pane.subject.title = "Nodes of #{resource}"
97
99
  @node_list_pane.clear
98
- @node_list_pane.subject.first_row = 0
99
- @node_list_pane.subject.contents = self.ship.fetch_node_list resource: resource
100
+ @node_list_pane.subject.title = "Nodes of #{self.active_resource}"
101
+ @node_list_pane.subject.contents = self.ship.fetch_node_list(resource: self.active_resource)
100
102
  end
101
- resource
103
+ nil
102
104
  end
103
105
 
104
- def resync_node_view(resource)
105
- node_index = @node_list_pane.current_subject
106
- @node_view_pane.subject.title = "Node #{self.short_index node_index}"
106
+ def resync_node_view
107
+ @node_view_pane.subject.title = "Node #{self.short_index self.active_node_index}"
107
108
  @node_view_pane.clear
108
- @node_view_pane.subject.contents = self.ship.fetch_node_contents(resource: resource, index: node_index)
109
+ @node_view_pane.subject.contents = self.ship.fetch_node_contents(resource: self.active_resource, index: self.active_node_index)
110
+ nil
109
111
  end
110
112
 
111
113
  def short_index(index)
@@ -113,5 +115,21 @@ module MarsBase10
113
115
  tokens = index.split('.')
114
116
  "#{tokens[0]}..#{tokens[tokens.size - 2]}.#{tokens[tokens.size - 1]}"
115
117
  end
118
+
119
+ def wire_up_panes
120
+ @panes = []
121
+
122
+ # The graph list is a fixed width, variable height (full screen) pane on the left.
123
+ @graph_list_pane = @viewport.add_pane width_pct: 0.3
124
+ @graph_list_pane.view(subject: @ship.graph_names)
125
+
126
+ # The node list is a variable width, fixed height pane in the upper right.
127
+ @node_list_pane = @viewport.add_variable_width_pane at_col: @graph_list_pane.last_col, height_pct: 0.5
128
+ @node_list_pane.view(subject: @ship.empty_node_list)
129
+
130
+ # The single node viewer is a variable width, variable height pane in the lower right.
131
+ @node_view_pane = @viewport.add_variable_both_pane at_row: @node_list_pane.last_row, at_col: @graph_list_pane.last_col
132
+ @node_view_pane.view(subject: @ship.empty_node)
133
+ end
116
134
  end
117
135
  end
@@ -10,11 +10,13 @@ module MarsBase10
10
10
  @top_row = at_row
11
11
  @left_edge_col = at_col
12
12
  @height_pct = height_pct
13
- @index = 0
14
- @latch = -1
13
+ @index = 1
14
+ @latch = ''
15
+ @latch_depth = 0
15
16
  @subject = nil
16
17
  @win = nil
17
18
  @viewport = viewport
19
+ @visible_content_shift = 0
18
20
  @width_pct = width_pct
19
21
  end
20
22
 
@@ -23,6 +25,7 @@ module MarsBase10
23
25
  end
24
26
 
25
27
  def clear
28
+ self.reset
26
29
  self.prepare_for_writing_contents
27
30
  (0..(self.last_row - 1)).each do |item|
28
31
  self.window.setpos(self.draw_row, self.draw_col)
@@ -32,19 +35,35 @@ module MarsBase10
32
35
  end
33
36
  end
34
37
 
35
- def current_subject
36
- self.subject.at index: self.index
38
+ def current_subject_index
39
+ self.subject.at(index: self.index)
37
40
  end
38
41
 
39
42
  def draw
40
43
  self.prepare_for_writing_contents
41
44
 
42
- (0..(self.max_contents_rows - 1)).each do |item|
43
- self.window.setpos(self.draw_row, self.draw_col)
44
- # The string here is the gutter followed by the window contents. improving the gutter is tbd.
45
- self.window.attron(Curses::A_REVERSE) if item == self.index
46
- self.window.addstr("#{"%02d" % item} #{self.subject.at index: item}")
47
- self.window.attroff(Curses::A_REVERSE) # if item == self.index
45
+ first_draw_row = self.first_row
46
+ last_draw_row = self.last_drawable_row
47
+
48
+ (first_draw_row..last_draw_row).each do |index|
49
+ self.draw_line
50
+ item_index = index + @visible_content_shift
51
+ self.window.attron(Curses::A_REVERSE) if item_index == self.index
52
+
53
+ if self.subject.line_length_at(index: item_index) > self.last_col
54
+ chunks = self.subject.line_at(index: item_index).chars.each_slice(self.last_col).map(&:join)
55
+ chunks.each do |c|
56
+ self.window.addstr(c)
57
+ self.draw_row += 1
58
+ self.draw_line
59
+ last_draw_row -= 1 # pull the last drawing row in 1 for each new line added.
60
+ end
61
+ self.draw_row -= 1
62
+ else
63
+ self.window.addstr("#{self.subject.line_at(index: item_index)}")
64
+ end
65
+
66
+ self.window.attroff(Curses::A_REVERSE) if item_index == self.index
48
67
  self.window.clrtoeol
49
68
  self.draw_row += 1
50
69
  end
@@ -59,9 +78,13 @@ module MarsBase10
59
78
  self.window.attroff(Curses.color_pair(1) | Curses::A_BOLD) if self.active?
60
79
  end
61
80
 
81
+ def draw_line
82
+ self.window.setpos(self.draw_row, self.draw_col)
83
+ end
84
+
62
85
  def draw_title
63
86
  self.window.setpos(0, 2)
64
- self.window.addstr(" #{self.subject.title} (#{self.subject.rows} total) ")
87
+ self.window.addstr(" #{self.subject.title} (#{self.max_contents_rows} total) ")
65
88
  end
66
89
 
67
90
  def first_col
@@ -73,7 +96,7 @@ module MarsBase10
73
96
  end
74
97
 
75
98
  def gutter_width
76
- 4
99
+ self.subject.index_width
77
100
  end
78
101
 
79
102
  #
@@ -83,6 +106,10 @@ module MarsBase10
83
106
  [(self.viewport.max_cols * self.width_pct).floor, self.min_column_width].max
84
107
  end
85
108
 
109
+ def last_drawable_row
110
+ [self.last_visible_row, self.max_contents_rows].min
111
+ end
112
+
86
113
  #
87
114
  # This is the _relative_ last row, e.g. the height of the pane in columns.
88
115
  #
@@ -90,19 +117,23 @@ module MarsBase10
90
117
  (self.viewport.max_rows * self.height_pct).floor
91
118
  end
92
119
 
120
+ def last_visible_row
121
+ self.last_row - 2
122
+ end
123
+
93
124
  #
94
125
  # The pane is latched if it has consumed 1 key 0-9 and is awaiting the next key.
95
126
  #
96
127
  def latched?
97
- self.latch != -1
128
+ @latch_depth > 0
98
129
  end
99
130
 
100
131
  def max_contents_rows
101
- self.subject.rows
132
+ self.subject.item_index_range.last
102
133
  end
103
134
 
104
135
  def min_column_width
105
- self.gutter_width + self.subject.cols + self.right_pad
136
+ self.gutter_width + self.subject.max_content_width + self.right_pad
106
137
  end
107
138
 
108
139
  def prepare_for_writing_contents
@@ -121,17 +152,27 @@ module MarsBase10
121
152
  key = self.window.getch.to_s
122
153
  case key
123
154
  when 'j'
124
- self.set_row(self.index + 1)
155
+ self.index = self.set_row(self.index + 1)
125
156
  when 'k'
126
- self.set_row(self.index - 1)
157
+ self.index = self.set_row(self.index - 1)
158
+ when 'J'
159
+ self.index = self.set_row([self.index + (self.last_visible_row), self.max_contents_rows].min)
160
+ when 'K'
161
+ self.index = self.set_row(self.index - (self.last_visible_row))
127
162
  when 'q'
128
163
  exit 0
129
164
  when ('0'..'9')
130
165
  if self.latched?
131
- self.set_row((self.latch * 10) + key.to_i)
132
- self.latch = -1
166
+ self.latch << key
167
+ @latch_depth += 1
168
+ if @latch_depth == self.gutter_width
169
+ self.index = self.set_row(self.latch.to_i)
170
+ self.latch = ""
171
+ @latch_depth = 0
172
+ end
133
173
  else
134
- self.latch = key.to_i
174
+ self.latch = key
175
+ @latch_depth = 1
135
176
  end
136
177
  end
137
178
 
@@ -139,38 +180,54 @@ module MarsBase10
139
180
  self.viewport.controller.send key: key
140
181
  end
141
182
 
183
+ def reset
184
+ @index = 1
185
+ @latch = ''
186
+ @latch_depth = 0
187
+ @visible_content_shift = 0
188
+ end
189
+
142
190
  def right_pad
143
191
  2
144
192
  end
145
193
 
194
+ def scroll_to_row(index)
195
+ if index > self.index
196
+ # Scrolling down
197
+ @visible_content_shift = [(index - self.last_drawable_row), 0].max
198
+ else
199
+ # Scrolling up
200
+ @visible_content_shift = [(index - self.first_row), 0].max
201
+ end
202
+ [index, 1].max
203
+ end
204
+
146
205
  #
147
206
  # this is a no-op if the index is out of range
148
207
  #
149
208
  def set_row(i)
150
- self.subject.scroll_limit = [self.last_row - 1, self.max_contents_rows].min
151
-
152
- if (i < 0)
153
- self.subject.scroll_up
154
- i = 0
155
- end
209
+ return i if self.visible_content_range.include?(i)
156
210
 
157
211
  # If we've reached the end of the content, it's a no-op.
158
- if (i >= self.max_contents_rows)
159
- i -= 1
160
- end
212
+ current_index = self.index
213
+ return self.max_contents_rows if (i > self.max_contents_rows)
161
214
 
162
- if (i >= self.last_row - 2)
163
- self.subject.scroll_down
164
- i -= 1
215
+ # Check if we have tried to move "above" the visible screen limit (i = 1) and retrieve more items, if possible.
216
+ if (i < 1)
217
+ i = [self.viewport.controller.load_history, 1].max
165
218
  end
166
219
 
167
- self.index = i # if (i <= self.max_contents_rows) && (i >= 0)
220
+ return self.scroll_to_row(i)
168
221
  end
169
222
 
170
- def viewing(subject:)
223
+ def view(subject:)
171
224
  @subject = subject
172
225
  end
173
226
 
227
+ def visible_content_range
228
+ ((self.first_row + @visible_content_shift)..(self.last_drawable_row + @visible_content_shift))
229
+ end
230
+
174
231
  def window
175
232
  return @win if @win
176
233
  @win = Curses::Window.new(self.last_row, self.last_col, self.top_row, self.left_edge_col)
@@ -8,18 +8,18 @@ module MarsBase10
8
8
  @ship = connection
9
9
  end
10
10
 
11
- def graph_names
12
- Subject.new title: 'Graphs', contents: @ship.graph_names
13
- end
14
-
15
- def node
11
+ def empty_node
16
12
  Subject.new title: 'Node', contents: []
17
13
  end
18
14
 
19
- def node_list
15
+ def empty_node_list
20
16
  Subject.new title: 'Node List', contents: []
21
17
  end
22
18
 
19
+ def graph_names
20
+ Subject.new title: 'Graphs', contents: @ship.graph_names
21
+ end
22
+
23
23
  def fetch_node(resource:, index:)
24
24
  @ship.graph(resource: resource).node(index: index)
25
25
  end
@@ -34,7 +34,11 @@ module MarsBase10
34
34
  end
35
35
 
36
36
  def fetch_node_list(resource:)
37
- @ship.graph(resource: resource).newest_nodes(count: 20).map {|node| node.index}.sort
37
+ @ship.graph(resource: resource).newest_nodes(count: 60).map {|node| node.index}.sort
38
+ end
39
+
40
+ def fetch_older_nodes(resource:, node:)
41
+ @ship.graph(resource: resource).older_sibling_nodes(node: node, count: 60).map {|node| node.index}.sort
38
42
  end
39
43
  end
40
44
  end
@@ -2,22 +2,21 @@
2
2
 
3
3
  module MarsBase10
4
4
  class Subject
5
- attr_accessor :first_row, :scroll_limit, :title
5
+ attr_accessor :current_item, :scroll_limit, :title
6
6
 
7
7
  def initialize(title: 'Untitled', contents:)
8
- @contents = contents
9
- @first_row = 0
10
- @title = title
8
+ @contents = contents
9
+ @title = title
11
10
  end
12
11
 
13
- # Returns the item at: the index: relative to the first_row.
14
- def at(index:)
15
- self.contents[self.first_row + index]
12
+ def prepend_content(ary:)
13
+ self.contents = ary + self.contents
16
14
  end
17
15
 
18
- def cols
19
- return @cols if @cols
20
- @cols = @contents.inject(0) {|a, n| n.length > a ? n.length : a}
16
+ # Returns the item at: the index: relative to the current_item.
17
+ def at(index:)
18
+ index = [1, index].max
19
+ self.contents[index - 1]
21
20
  end
22
21
 
23
22
  def contents
@@ -25,22 +24,33 @@ module MarsBase10
25
24
  end
26
25
 
27
26
  def contents=(a_contents_array)
28
- @rows = nil
29
- $cols = nil
30
27
  @contents = a_contents_array
31
28
  end
32
29
 
33
- def rows
34
- return @rows if @rows
35
- @rows = @contents.size
30
+ def index_width
31
+ [self.item_count.to_s.length, 2].max
32
+ end
33
+
34
+ def line_at(index:)
35
+ # The string here is the gutter followed by the window contents. improving the gutter is tbd.
36
+ "#{"%0#{self.index_width}d" % (index)} #{self.at(index: index)}"
37
+ end
38
+
39
+ def line_length_at(index:)
40
+ return 0 if self.at(index: index).nil?
41
+ (self.at(index: index)).length
42
+ end
43
+
44
+ def item_count
45
+ @contents.size
36
46
  end
37
47
 
38
- def scroll_down
39
- self.first_row = [self.first_row + 1, (self.rows - self.scroll_limit)].min
48
+ def item_index_range
49
+ 1..self.item_count
40
50
  end
41
51
 
42
- def scroll_up
43
- self.first_row = [self.first_row - 1, 0].max
52
+ def max_content_width
53
+ @contents.inject(0) {|a, n| n.length > a ? n.length : a}
44
54
  end
45
55
  end
46
56
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MarsBase10
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  end
data/mars_base_10.gemspec CHANGED
@@ -32,7 +32,7 @@ Gem::Specification.new do |spec|
32
32
  spec.add_dependency "sorted_set", "~> 1.0"
33
33
  spec.add_dependency "thor", "~> 1.1"
34
34
  spec.add_dependency "tty-font", "~> 0.5"
35
- spec.add_dependency "urbit-api", "~> 0.2.1"
35
+ spec.add_dependency "urbit-api", "~> 0.5"
36
36
 
37
37
  spec.add_development_dependency "pry", "~> 0.13"
38
38
  spec.add_development_dependency "rspec", "~> 3.10"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mars_base_10
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daryl Richter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-18 00:00:00.000000000 Z
11
+ date: 2022-05-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: curses
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: 0.2.1
89
+ version: '0.5'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: 0.2.1
96
+ version: '0.5'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: pry
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -146,6 +146,7 @@ extensions: []
146
146
  extra_rdoc_files: []
147
147
  files:
148
148
  - bin/mb10
149
+ - lib/.DS_Store
149
150
  - lib/mars_base_10.rb
150
151
  - lib/mars_base_10/action_bar.rb
151
152
  - lib/mars_base_10/cli.rb