spreadshit 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|