spreadshit 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/lib/spreadshit.rb +1 -46
- data/lib/spreadshit/cell.rb +45 -0
- data/lib/spreadshit/version.rb +1 -1
- data/lib/spreadshit/window.rb +202 -200
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b8708f78a939f35fc9529dd41df30a084580930
|
4
|
+
data.tar.gz: a49ecd2eaa4f5565520cb6da994468532d91b3ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1379f58cb3098c6e12ed2c755651e629da2156a7e4f5de3a35b7e590c693d3ad74ab19978b5260a8b0f62eec86b4cdff1cf9d8cb667d11805238d9bf5defd6cc
|
7
|
+
data.tar.gz: 4d9731115926c43cf44a975c6b3dc7dfda4131b756d2ed383d98a7bffaa5d12775fbe4fdbb7d177e7124333693502d99c67940cd9d5482a8d67cde34d83c221a
|
data/.gitignore
CHANGED
data/lib/spreadshit.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
class Spreadshit
|
2
|
+
require "spreadshit/cell"
|
2
3
|
require "spreadshit/formula"
|
3
4
|
require "spreadshit/functions"
|
4
5
|
|
@@ -64,49 +65,3 @@ class Spreadshit
|
|
64
65
|
end
|
65
66
|
end
|
66
67
|
end
|
67
|
-
|
68
|
-
class Cell
|
69
|
-
attr_reader :raw
|
70
|
-
|
71
|
-
def initialize(raw = "", &expression)
|
72
|
-
@raw = raw
|
73
|
-
@observers = Set.new
|
74
|
-
@observed = []
|
75
|
-
update(&expression) if block_given?
|
76
|
-
end
|
77
|
-
|
78
|
-
def value
|
79
|
-
if @@caller
|
80
|
-
@observers << @@caller
|
81
|
-
@@caller.observed << self
|
82
|
-
end
|
83
|
-
@value
|
84
|
-
end
|
85
|
-
|
86
|
-
def update(value = raw || "", &expression)
|
87
|
-
@raw = value
|
88
|
-
@expression = expression
|
89
|
-
compute
|
90
|
-
@value
|
91
|
-
end
|
92
|
-
|
93
|
-
protected
|
94
|
-
|
95
|
-
attr_reader :observers, :observed
|
96
|
-
|
97
|
-
def compute
|
98
|
-
@observed.each { |observed| observed.observers.delete(self) }
|
99
|
-
@observed = []
|
100
|
-
|
101
|
-
@@caller = self
|
102
|
-
new_value = @expression.call
|
103
|
-
@@caller = nil
|
104
|
-
|
105
|
-
if new_value != @value
|
106
|
-
@value = new_value
|
107
|
-
observers = @observers
|
108
|
-
@observers = Set.new
|
109
|
-
observers.each { |observer| observer.compute }
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class Spreadshit::Cell
|
2
|
+
attr_reader :raw
|
3
|
+
|
4
|
+
def initialize(raw = "", &expression)
|
5
|
+
@raw = raw
|
6
|
+
@observers = Set.new
|
7
|
+
@observed = []
|
8
|
+
update(&expression) if block_given?
|
9
|
+
end
|
10
|
+
|
11
|
+
def value
|
12
|
+
if @@caller
|
13
|
+
@observers << @@caller
|
14
|
+
@@caller.observed << self
|
15
|
+
end
|
16
|
+
@value
|
17
|
+
end
|
18
|
+
|
19
|
+
def update(value = raw || "", &expression)
|
20
|
+
@raw = value
|
21
|
+
@expression = expression
|
22
|
+
compute
|
23
|
+
@value
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
attr_reader :observers, :observed
|
29
|
+
|
30
|
+
def compute
|
31
|
+
@observed.each { |observed| observed.observers.delete(self) }
|
32
|
+
@observed = []
|
33
|
+
|
34
|
+
@@caller = self
|
35
|
+
new_value = @expression.call
|
36
|
+
@@caller = nil
|
37
|
+
|
38
|
+
if new_value != @value
|
39
|
+
@value = new_value
|
40
|
+
observers = @observers
|
41
|
+
@observers = Set.new
|
42
|
+
observers.each { |observer| observer.compute }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/spreadshit/version.rb
CHANGED
data/lib/spreadshit/window.rb
CHANGED
@@ -1,263 +1,265 @@
|
|
1
1
|
require "curses"
|
2
2
|
|
3
|
-
class Spreadshit
|
4
|
-
class
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
class Spreadshit
|
4
|
+
class Window
|
5
|
+
class Address < Struct.new(:col, :row)
|
6
|
+
def to_s
|
7
|
+
[col, row].join
|
8
|
+
end
|
8
9
|
|
9
|
-
|
10
|
-
|
10
|
+
def to_sym
|
11
|
+
to_s.to_sym
|
12
|
+
end
|
11
13
|
end
|
12
|
-
end
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
class SpreadsheetDelegate
|
16
|
+
def initialize
|
17
|
+
@cell_updated, @cell_value, @cell_content = Proc.new {}
|
18
|
+
end
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
def cell_updated(&block)
|
21
|
+
if block_given?
|
22
|
+
@cell_updated = block
|
23
|
+
else
|
24
|
+
@cell_updated
|
25
|
+
end
|
24
26
|
end
|
25
|
-
end
|
26
27
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
def cell_value(&block)
|
29
|
+
if block_given?
|
30
|
+
@cell_value = block
|
31
|
+
else
|
32
|
+
@cell_value
|
33
|
+
end
|
32
34
|
end
|
33
|
-
end
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
def cell_content(&block)
|
37
|
+
if block_given?
|
38
|
+
@cell_content = block
|
39
|
+
else
|
40
|
+
@cell_content
|
41
|
+
end
|
40
42
|
end
|
41
43
|
end
|
42
|
-
end
|
43
44
|
|
44
|
-
|
45
|
+
include Curses
|
45
46
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
def start
|
57
|
-
init_screen
|
58
|
-
start_color
|
59
|
-
init_pair(COLOR_WHITE, COLOR_BLACK, COLOR_WHITE)
|
60
|
-
init_pair(COLOR_BLUE, COLOR_BLACK, COLOR_BLUE)
|
61
|
-
init_pair(COLOR_GREEN, COLOR_BLACK, COLOR_GREEN)
|
62
|
-
init_pair(COLOR_RED, COLOR_BLACK, COLOR_MAGENTA)
|
63
|
-
use_default_colors
|
64
|
-
redraw
|
65
|
-
|
66
|
-
loop { capture_input }
|
67
|
-
end
|
68
|
-
|
69
|
-
private
|
47
|
+
def initialize
|
48
|
+
@mode = :navigation
|
49
|
+
@x, @y = 0, 0
|
50
|
+
@sx, @sy = 0, 0
|
51
|
+
@col_width = 13
|
52
|
+
@letters = ("A".."ZZZ").to_a
|
53
|
+
@spreadsheet_delegate = SpreadsheetDelegate.new
|
54
|
+
yield @spreadsheet_delegate
|
55
|
+
end
|
70
56
|
|
71
|
-
|
72
|
-
|
73
|
-
|
57
|
+
def start
|
58
|
+
init_screen
|
59
|
+
start_color
|
60
|
+
init_pair(COLOR_WHITE, COLOR_BLACK, COLOR_WHITE)
|
61
|
+
init_pair(COLOR_BLUE, COLOR_BLACK, COLOR_BLUE)
|
62
|
+
init_pair(COLOR_GREEN, COLOR_BLACK, COLOR_GREEN)
|
63
|
+
init_pair(COLOR_RED, COLOR_BLACK, COLOR_MAGENTA)
|
64
|
+
use_default_colors
|
65
|
+
redraw
|
66
|
+
|
67
|
+
loop { capture_input }
|
68
|
+
end
|
74
69
|
|
75
|
-
|
76
|
-
lines - 3
|
77
|
-
end
|
70
|
+
private
|
78
71
|
|
79
|
-
|
80
|
-
|
81
|
-
|
72
|
+
def max_cols
|
73
|
+
cols / @col_width
|
74
|
+
end
|
82
75
|
|
83
|
-
|
84
|
-
|
85
|
-
|
76
|
+
def max_rows
|
77
|
+
lines - 3
|
78
|
+
end
|
86
79
|
|
87
|
-
|
88
|
-
|
89
|
-
|
80
|
+
def address
|
81
|
+
Address.new(@letters[@x], @y + 1)
|
82
|
+
end
|
90
83
|
|
91
|
-
|
92
|
-
|
93
|
-
|
84
|
+
def current_cell_value
|
85
|
+
cell_value_at(address)
|
86
|
+
end
|
94
87
|
|
95
|
-
|
96
|
-
|
97
|
-
|
88
|
+
def cell_value_at(address)
|
89
|
+
@spreadsheet_delegate.cell_value.call(address)
|
90
|
+
end
|
98
91
|
|
99
|
-
|
100
|
-
|
101
|
-
when :navigation
|
102
|
-
navigate
|
103
|
-
when :edit
|
104
|
-
read_cell_definition
|
92
|
+
def current_cell_content
|
93
|
+
@spreadsheet_delegate.cell_content.call(address)
|
105
94
|
end
|
106
|
-
end
|
107
95
|
|
108
|
-
|
109
|
-
|
110
|
-
noecho
|
111
|
-
stdscr.keypad = true
|
112
|
-
case getch
|
113
|
-
when KEY_UP
|
114
|
-
@y -= 1
|
115
|
-
@y = 0 if @y < 0
|
116
|
-
@sy -= 1 if @y < @sy
|
117
|
-
@sy = 0 if @sy < 0
|
118
|
-
when KEY_DOWN
|
119
|
-
@y += 1
|
120
|
-
@sy += 1 if @y >= max_rows
|
121
|
-
when KEY_LEFT
|
122
|
-
@x -= 1
|
123
|
-
@x = 0 if @x < 0
|
124
|
-
@sx -= 1 if @x < @sx
|
125
|
-
@sx = 0 if @sx < 0
|
126
|
-
when KEY_RIGHT
|
127
|
-
@x += 1
|
128
|
-
@sx += 1 if @x >= max_cols
|
129
|
-
when 10
|
130
|
-
@mode = :edit
|
131
|
-
when 27
|
132
|
-
exit 0
|
133
|
-
else
|
134
|
-
return
|
96
|
+
def current_cell_content=(value)
|
97
|
+
@spreadsheet_delegate.cell_updated.call(address, value)
|
135
98
|
end
|
136
99
|
|
137
|
-
|
138
|
-
|
100
|
+
def capture_input
|
101
|
+
case @mode
|
102
|
+
when :navigation
|
103
|
+
navigate
|
104
|
+
when :edit
|
105
|
+
read_cell_definition
|
106
|
+
end
|
107
|
+
end
|
139
108
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
109
|
+
def navigate
|
110
|
+
cbreak
|
111
|
+
noecho
|
112
|
+
stdscr.keypad = true
|
113
|
+
case getch
|
114
|
+
when KEY_UP
|
115
|
+
@y -= 1
|
116
|
+
@y = 0 if @y < 0
|
117
|
+
@sy -= 1 if @y < @sy
|
118
|
+
@sy = 0 if @sy < 0
|
119
|
+
when KEY_DOWN
|
120
|
+
@y += 1
|
121
|
+
@sy += 1 if @y >= max_rows
|
122
|
+
when KEY_LEFT
|
123
|
+
@x -= 1
|
124
|
+
@x = 0 if @x < 0
|
125
|
+
@sx -= 1 if @x < @sx
|
126
|
+
@sx = 0 if @sx < 0
|
127
|
+
when KEY_RIGHT
|
128
|
+
@x += 1
|
129
|
+
@sx += 1 if @x >= max_cols
|
130
|
+
when 10
|
131
|
+
@mode = :edit
|
132
|
+
when 27
|
133
|
+
exit 0
|
134
|
+
else
|
135
|
+
return
|
136
|
+
end
|
146
137
|
|
147
|
-
|
148
|
-
|
149
|
-
draw_letters_header
|
150
|
-
draw_numbers_header
|
151
|
-
draw_text_field
|
152
|
-
cursor_to_input_line
|
153
|
-
refresh
|
154
|
-
end
|
138
|
+
redraw
|
139
|
+
end
|
155
140
|
|
156
|
-
|
157
|
-
|
141
|
+
def read_cell_definition
|
142
|
+
echo
|
143
|
+
self.current_cell_content = getstr
|
144
|
+
@mode = :navigation
|
145
|
+
redraw
|
146
|
+
end
|
158
147
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
center_text: "Press ENTER to edit #{address}",
|
165
|
-
)
|
166
|
-
cursor_to_input_line
|
167
|
-
addstr(current_cell_content.to_s)
|
168
|
-
clrtoeol
|
169
|
-
when :edit
|
170
|
-
draw_divider(
|
171
|
-
color: color_pair(COLOR_GREEN) | A_NORMAL,
|
172
|
-
left_text: current_cell_content.to_s,
|
173
|
-
center_text: "Editing #{address}"
|
174
|
-
)
|
148
|
+
def redraw
|
149
|
+
draw_cells
|
150
|
+
draw_letters_header
|
151
|
+
draw_numbers_header
|
152
|
+
draw_text_field
|
175
153
|
cursor_to_input_line
|
176
|
-
|
154
|
+
refresh
|
177
155
|
end
|
178
|
-
end
|
179
156
|
|
180
|
-
|
181
|
-
attron color do
|
157
|
+
def draw_text_field
|
182
158
|
setpos(divider_line, 0)
|
183
|
-
addstr(" " * cols)
|
184
159
|
|
185
|
-
|
186
|
-
|
160
|
+
case @mode
|
161
|
+
when :navigation
|
162
|
+
draw_divider(
|
163
|
+
color: color_pair(COLOR_RED) | A_NORMAL,
|
164
|
+
left_text: current_cell_value.to_s,
|
165
|
+
center_text: "Press ENTER to edit #{address}",
|
166
|
+
)
|
167
|
+
cursor_to_input_line
|
168
|
+
addstr(current_cell_content.to_s)
|
169
|
+
clrtoeol
|
170
|
+
when :edit
|
171
|
+
draw_divider(
|
172
|
+
color: color_pair(COLOR_GREEN) | A_NORMAL,
|
173
|
+
left_text: current_cell_content.to_s,
|
174
|
+
center_text: "Editing #{address}"
|
175
|
+
)
|
176
|
+
cursor_to_input_line
|
177
|
+
clrtoeol
|
178
|
+
end
|
179
|
+
end
|
187
180
|
|
188
|
-
|
189
|
-
|
181
|
+
def draw_divider(color: color_pair(COLOR_GREEN) | A_NORMAL, left_text: "", right_text: "", center_text: "")
|
182
|
+
attron color do
|
183
|
+
setpos(divider_line, 0)
|
184
|
+
addstr(" " * cols)
|
190
185
|
|
191
|
-
|
192
|
-
|
186
|
+
setpos(divider_line, 2)
|
187
|
+
addstr(left_text.ljust(cols / 3))
|
188
|
+
|
189
|
+
setpos(divider_line, cols / 3)
|
190
|
+
addstr(center_text.center(cols / 3))
|
191
|
+
|
192
|
+
setpos(divider_line, cols - right_text.size - 2)
|
193
|
+
addstr(right_text)
|
194
|
+
end
|
193
195
|
end
|
194
|
-
end
|
195
196
|
|
196
|
-
|
197
|
-
|
198
|
-
|
197
|
+
def visible_letters
|
198
|
+
(@sx...max_cols + @sx).map { |col| @letters[col] }
|
199
|
+
end
|
199
200
|
|
200
|
-
|
201
|
-
|
202
|
-
|
201
|
+
def selected?(row, col)
|
202
|
+
address.col == col && address.row == (@sy + row)
|
203
|
+
end
|
203
204
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
205
|
+
def draw_cells(padding: 4)
|
206
|
+
1.upto(max_rows).each do |row|
|
207
|
+
visible_letters.each.with_index do |col, index|
|
208
|
+
setpos(row, padding + index * @col_width)
|
208
209
|
|
209
|
-
|
210
|
-
|
210
|
+
if selected? row, col
|
211
|
+
attron(color_pair(@mode == :edit ? COLOR_GREEN : COLOR_WHITE) | A_TOP) do
|
212
|
+
draw_cell @sy + row, col
|
213
|
+
end
|
214
|
+
else
|
211
215
|
draw_cell @sy + row, col
|
212
216
|
end
|
213
|
-
else
|
214
|
-
draw_cell @sy + row, col
|
215
217
|
end
|
216
218
|
end
|
217
219
|
end
|
218
|
-
end
|
219
220
|
|
220
|
-
|
221
|
-
|
222
|
-
|
221
|
+
def draw_letters_header(padding: 4, color: COLOR_BLUE)
|
222
|
+
visible_letters.each.with_index do |letter, index|
|
223
|
+
setpos(0, padding + index * @col_width)
|
223
224
|
|
224
|
-
|
225
|
-
|
225
|
+
attron(color_pair(color) | A_TOP) do
|
226
|
+
addstr(letter.center(@col_width))
|
227
|
+
end
|
226
228
|
end
|
227
229
|
end
|
228
|
-
end
|
229
230
|
|
230
|
-
|
231
|
-
|
232
|
-
|
231
|
+
def draw_numbers_header(padding: 4, color: COLOR_BLUE)
|
232
|
+
1.upto(max_rows).each.with_index do |row, index|
|
233
|
+
setpos(row, 0)
|
233
234
|
|
234
|
-
|
235
|
-
|
235
|
+
attron(color_pair(COLOR_BLUE) | A_TOP) do
|
236
|
+
addstr (@sy + row).to_s.rjust(padding)
|
237
|
+
end
|
236
238
|
end
|
237
239
|
end
|
238
|
-
end
|
239
240
|
|
240
|
-
|
241
|
-
|
241
|
+
def draw_cell(row, col)
|
242
|
+
value = cell_value_at(Address.new(col, row)).to_s
|
242
243
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
244
|
+
if value == Float::NAN.to_s
|
245
|
+
addstr("#VALUE!".center(@col_width))
|
246
|
+
elsif value.size >= @col_width
|
247
|
+
addstr(value.chars.last(@col_width).join)
|
248
|
+
else
|
249
|
+
addstr(value.rjust(@col_width))
|
250
|
+
end
|
249
251
|
end
|
250
|
-
end
|
251
252
|
|
252
|
-
|
253
|
-
|
254
|
-
|
253
|
+
def input_line
|
254
|
+
lines - 1
|
255
|
+
end
|
255
256
|
|
256
|
-
|
257
|
-
|
258
|
-
|
257
|
+
def divider_line
|
258
|
+
lines - 2
|
259
|
+
end
|
259
260
|
|
260
|
-
|
261
|
-
|
261
|
+
def cursor_to_input_line
|
262
|
+
setpos(input_line, 2)
|
263
|
+
end
|
262
264
|
end
|
263
265
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spreadshit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rodrigo Navarro
|
@@ -81,6 +81,7 @@ files:
|
|
81
81
|
- bin/demo
|
82
82
|
- bin/setup
|
83
83
|
- lib/spreadshit.rb
|
84
|
+
- lib/spreadshit/cell.rb
|
84
85
|
- lib/spreadshit/formula.rb
|
85
86
|
- lib/spreadshit/formula.treetop
|
86
87
|
- lib/spreadshit/functions.rb
|