text_editor 0.0.0
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 +7 -0
- data/.gitignore +3 -0
- data/.rspec +2 -0
- data/.rubocop.yml +33 -0
- data/.ruby-version +1 -0
- data/.text_editor.rb +9 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +58 -0
- data/LICENSE +20 -0
- data/README.org +29 -0
- data/bin/console +9 -0
- data/bin/te +3 -0
- data/lib/core_ext/hash.rb +12 -0
- data/lib/core_ext/pathname.rb +9 -0
- data/lib/text_editor/buffer.rb +85 -0
- data/lib/text_editor/clipboard/cygwin.rb +20 -0
- data/lib/text_editor/clipboard/linux.rb +17 -0
- data/lib/text_editor/clipboard/mac.rb +18 -0
- data/lib/text_editor/clipboard/unknown.rb +17 -0
- data/lib/text_editor/clipboard/windows.rb +17 -0
- data/lib/text_editor/clipboard.rb +18 -0
- data/lib/text_editor/command/backspace.rb +26 -0
- data/lib/text_editor/command/cursor_down.rb +14 -0
- data/lib/text_editor/command/cursor_end.rb +10 -0
- data/lib/text_editor/command/cursor_left.rb +16 -0
- data/lib/text_editor/command/cursor_right.rb +19 -0
- data/lib/text_editor/command/cursor_start.rb +10 -0
- data/lib/text_editor/command/cursor_up.rb +13 -0
- data/lib/text_editor/command/insert_char.rb +11 -0
- data/lib/text_editor/command/insert_line.rb +16 -0
- data/lib/text_editor/command/insert_tab.rb +9 -0
- data/lib/text_editor/command/quit.rb +9 -0
- data/lib/text_editor/command/resolver.rb +36 -0
- data/lib/text_editor/command/write_file.rb +13 -0
- data/lib/text_editor/command.rb +26 -0
- data/lib/text_editor/component.rb +13 -0
- data/lib/text_editor/configuration/bootstrap.rb +43 -0
- data/lib/text_editor/configuration/dsl.rb +11 -0
- data/lib/text_editor/configuration/resolver.rb +45 -0
- data/lib/text_editor/configuration.rb +15 -0
- data/lib/text_editor/cursor.rb +42 -0
- data/lib/text_editor/keyboard.rb +54 -0
- data/lib/text_editor/mode/nano.rb +30 -0
- data/lib/text_editor/mode.rb +23 -0
- data/lib/text_editor/operating_system.rb +19 -0
- data/lib/text_editor/reactor.rb +38 -0
- data/lib/text_editor/state.rb +10 -0
- data/lib/text_editor/terminal.rb +57 -0
- data/lib/text_editor/window.rb +44 -0
- data/lib/text_editor.rb +121 -0
- data/log/.keep +0 -0
- data/spec/core_ext/hash_spec.rb +15 -0
- data/spec/core_ext/pathname_spec.rb +26 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/text_editor/buffer_spec.rb +181 -0
- data/spec/text_editor/clipboard_spec.rb +28 -0
- data/spec/text_editor/command/quit_spec.rb +14 -0
- data/spec/text_editor/command/resolver_spec.rb +56 -0
- data/spec/text_editor/command_spec.rb +85 -0
- data/spec/text_editor/component_spec.rb +40 -0
- data/spec/text_editor/configuration_spec.rb +25 -0
- data/spec/text_editor/cursor_spec.rb +93 -0
- data/spec/text_editor/operating_system_spec.rb +55 -0
- data/spec/text_editor/reactor_spec.rb +34 -0
- data/spec/text_editor/state_spec.rb +20 -0
- data/text_editor.gemspec +18 -0
- metadata +143 -0
@@ -0,0 +1,181 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe TextEditor::Buffer do
|
4
|
+
subject { described_class.new(lines) }
|
5
|
+
|
6
|
+
let(:lines) { %w(one two) }
|
7
|
+
|
8
|
+
describe "#delete_char" do
|
9
|
+
it "deletes char at line and column" do
|
10
|
+
actual = subject.delete_char(0, 0)
|
11
|
+
expect(actual.to_s).to eq("ne\ntwo")
|
12
|
+
|
13
|
+
actual = subject.delete_char(0, 1)
|
14
|
+
expect(actual.to_s).to eq("oe\ntwo")
|
15
|
+
|
16
|
+
actual = subject.delete_char(1, 0)
|
17
|
+
expect(actual.to_s).to eq("one\nwo")
|
18
|
+
|
19
|
+
actual = subject.delete_char(1, 1)
|
20
|
+
expect(actual.to_s).to eq("one\nto")
|
21
|
+
end
|
22
|
+
|
23
|
+
it "ignores columns out of bounds" do
|
24
|
+
actual = subject.delete_char(0, 10)
|
25
|
+
expect(actual.to_s).to eq("one\ntwo")
|
26
|
+
end
|
27
|
+
|
28
|
+
it "raises IndexError when line is out of bounds" do
|
29
|
+
expect { subject.delete_char(10, 0) }.to raise_error(IndexError)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#delete_line" do
|
34
|
+
it "deletes line at index" do
|
35
|
+
actual = subject.delete_line(0)
|
36
|
+
expect(actual.to_s).to eq("two")
|
37
|
+
|
38
|
+
actual = subject.delete_line(1)
|
39
|
+
expect(actual.to_s).to eq("one")
|
40
|
+
end
|
41
|
+
|
42
|
+
it "ignores out of bound indexes" do
|
43
|
+
actual = subject.delete_line(3)
|
44
|
+
expect(actual.to_s).to eq("one\ntwo")
|
45
|
+
end
|
46
|
+
|
47
|
+
it "does not mutate the original buffer" do
|
48
|
+
subject.delete_line(0)
|
49
|
+
expect(subject.to_s).to eq("one\ntwo")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#each_line" do
|
54
|
+
it "delegates to lines" do
|
55
|
+
expect(lines).to receive(:each)
|
56
|
+
subject.each_line { |line| line }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "#insert_char" do
|
61
|
+
it "inserts char at index" do
|
62
|
+
actual = subject.insert_char(0, 0, "x")
|
63
|
+
expect(actual.to_s).to eq("xone\ntwo")
|
64
|
+
|
65
|
+
actual = subject.insert_char(0, 1, "x")
|
66
|
+
expect(actual.to_s).to eq("oxne\ntwo")
|
67
|
+
|
68
|
+
actual = subject.insert_char(1, 0, "x")
|
69
|
+
expect(actual.to_s).to eq("one\nxtwo")
|
70
|
+
|
71
|
+
actual = subject.insert_char(1, 1, "x")
|
72
|
+
expect(actual.to_s).to eq("one\ntxwo")
|
73
|
+
end
|
74
|
+
|
75
|
+
it "raises IndexError when line is out of bounds" do
|
76
|
+
expect { subject.insert_char(10, 0, "x") }.to raise_error(IndexError)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "raises IndexError when column is out of bounds" do
|
80
|
+
expect { subject.insert_char(0, 10, "x") }.to raise_error(IndexError)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "#insert_line" do
|
85
|
+
it "inserts line at index" do
|
86
|
+
actual = subject.insert_line(0)
|
87
|
+
expect(actual.to_s).to eq("\none\ntwo")
|
88
|
+
|
89
|
+
actual = subject.insert_line(1)
|
90
|
+
expect(actual.to_s).to eq("one\n\ntwo")
|
91
|
+
|
92
|
+
actual = subject.insert_line(2)
|
93
|
+
expect(actual.to_s).to eq("one\ntwo\n")
|
94
|
+
end
|
95
|
+
|
96
|
+
it "optionally accepts content" do
|
97
|
+
actual = subject.insert_line(1, "test")
|
98
|
+
expect(actual.to_s).to eq("one\ntest\ntwo")
|
99
|
+
end
|
100
|
+
|
101
|
+
it "inserts at tail when index is out of bounds" do
|
102
|
+
actual = subject.insert_line(5)
|
103
|
+
expect(actual.to_s).to eq("one\ntwo\n")
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "#line" do
|
108
|
+
it "returns line by index" do
|
109
|
+
actual = subject.line(0)
|
110
|
+
expect(actual).to eq("one")
|
111
|
+
|
112
|
+
actual = subject.line(1)
|
113
|
+
expect(actual).to eq("two")
|
114
|
+
end
|
115
|
+
|
116
|
+
it "returns empty string otherwise" do
|
117
|
+
actual = subject.line(2)
|
118
|
+
expect(actual).to eq("")
|
119
|
+
end
|
120
|
+
|
121
|
+
it "returns a copy of the line" do
|
122
|
+
first_id = subject.line(0).object_id
|
123
|
+
second_id = subject.line(0).object_id
|
124
|
+
expect(first_id).not_to eq(second_id)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context "#replace_char" do
|
129
|
+
it "replace char at index" do
|
130
|
+
actual = subject.replace_char(0, 0, "x")
|
131
|
+
expect(actual.to_s).to eq("xne\ntwo")
|
132
|
+
|
133
|
+
actual = subject.replace_char(0, 1, "x")
|
134
|
+
expect(actual.to_s).to eq("oxe\ntwo")
|
135
|
+
|
136
|
+
actual = subject.replace_char(1, 0, "x")
|
137
|
+
expect(actual.to_s).to eq("one\nxwo")
|
138
|
+
|
139
|
+
actual = subject.replace_char(1, 1, "x")
|
140
|
+
expect(actual.to_s).to eq("one\ntxo")
|
141
|
+
end
|
142
|
+
|
143
|
+
it "raises IndexError when line is out of bounds" do
|
144
|
+
expect { subject.replace_char(10, 0, "x") }.to raise_error(IndexError)
|
145
|
+
end
|
146
|
+
|
147
|
+
it "raises IndexError when column is out of bounds" do
|
148
|
+
expect { subject.replace_char(0, 10, "x") }.to raise_error(IndexError)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
context "#replace_line" do
|
153
|
+
it "replaces line at index" do
|
154
|
+
actual = subject.replace_line(0, "test")
|
155
|
+
expect(actual.to_s).to eq("test\ntwo")
|
156
|
+
|
157
|
+
actual = subject.replace_line(1, "test")
|
158
|
+
expect(actual.to_s).to eq("one\ntest")
|
159
|
+
end
|
160
|
+
|
161
|
+
it "replaces line with empty string by default" do
|
162
|
+
actual = subject.replace_line(0)
|
163
|
+
expect(actual.to_s).to eq("\ntwo")
|
164
|
+
end
|
165
|
+
|
166
|
+
it "raises IndexError when line is out of bounds" do
|
167
|
+
expect { subject.replace_line(10, "test") }.to raise_error(IndexError)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
describe "#to_s" do
|
172
|
+
it "joins lines with a newline" do
|
173
|
+
expect(subject.to_s).to eq("one\ntwo")
|
174
|
+
end
|
175
|
+
|
176
|
+
it "returns an empty string if lines are empty" do
|
177
|
+
lines.clear
|
178
|
+
expect(subject.to_s).to eq("")
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe TextEditor::Clipboard do
|
4
|
+
subject { described_class.new(editor) }
|
5
|
+
|
6
|
+
let(:editor) { double("editor") }
|
7
|
+
|
8
|
+
describe "#clear" do
|
9
|
+
it "delegates to adapter.clear" do
|
10
|
+
expect(subject).to receive_message_chain(:adapter, :clear)
|
11
|
+
subject.clear
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#copy" do
|
16
|
+
it "delegates to adapter.copy" do
|
17
|
+
expect(subject).to receive_message_chain(:adapter, :copy)
|
18
|
+
subject.copy
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#paste" do
|
23
|
+
it "delegates to adapter.paste" do
|
24
|
+
expect(subject).to receive_message_chain(:adapter, :paste)
|
25
|
+
subject.paste
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe TextEditor::Command::Quit do
|
4
|
+
subject { described_class.new(editor) }
|
5
|
+
|
6
|
+
let(:editor) { double("editor") }
|
7
|
+
|
8
|
+
describe "#run" do
|
9
|
+
it "delegates to editor.reactor.stop" do
|
10
|
+
expect(subject).to receive_message_chain(:editor, :reactor, :stop)
|
11
|
+
subject.run
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe TextEditor::Command::Resolver do
|
4
|
+
subject { described_class.new(editor) }
|
5
|
+
|
6
|
+
let(:editor) { double("editor") }
|
7
|
+
let(:fake) { Class.new(namespace) }
|
8
|
+
let(:namespace) { TextEditor::Command }
|
9
|
+
|
10
|
+
before { namespace.const_set(:FakeCommand, fake) }
|
11
|
+
after { namespace.send(:remove_const, :FakeCommand) }
|
12
|
+
|
13
|
+
describe "#method_missing" do
|
14
|
+
it "runs a command instance" do
|
15
|
+
expect { subject.fake_command }.to raise_error(NotImplementedError)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "returns self" do
|
19
|
+
fake.send(:define_method, :run) { true }
|
20
|
+
expect(subject.fake_command).to eq(subject)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "raises NoMethodError for invalid commands" do
|
24
|
+
expect { subject.invalid_command }.to raise_error(NoMethodError)
|
25
|
+
expect { subject.invalid_command! }.to raise_error(NoMethodError)
|
26
|
+
expect { subject.invalid_command? }.to raise_error(NoMethodError)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#resolve" do
|
31
|
+
it "finds existing string commands" do
|
32
|
+
actual = subject.resolve("fake_command")
|
33
|
+
expect(actual).to be_a(fake)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "finds existing symbol commands" do
|
37
|
+
actual = subject.resolve(:fake_command)
|
38
|
+
expect(actual).to be_a(fake)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "returns nil for missing commands" do
|
42
|
+
actual = subject.resolve(:invalid)
|
43
|
+
expect(actual).to be_nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#respond_to?" do
|
48
|
+
it "only returns true for defined commands" do
|
49
|
+
actual = subject.respond_to?(:fake_command)
|
50
|
+
expect(actual).to eq(true)
|
51
|
+
|
52
|
+
actual = subject.respond_to?(:invalid_command)
|
53
|
+
expect(actual).to eq(false)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe TextEditor::Command do
|
4
|
+
subject { described_class.new(editor) }
|
5
|
+
|
6
|
+
let(:editor) { double("editor") }
|
7
|
+
|
8
|
+
describe ".call" do
|
9
|
+
it "delegates to new instance" do
|
10
|
+
args = [1, 2, 3]
|
11
|
+
block = proc { true }
|
12
|
+
command = double("command")
|
13
|
+
expect(command).to receive(:run).with(*args, &block)
|
14
|
+
expect(described_class).to receive(:new).with(editor).and_return(command)
|
15
|
+
described_class.call(editor, *args, &block)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#buffer" do
|
20
|
+
it "delegates to editor.window" do
|
21
|
+
expect(editor).to receive_message_chain(:window, :buffer)
|
22
|
+
subject.buffer
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#column" do
|
27
|
+
it "delegates to editor.window.cursor" do
|
28
|
+
expect(editor).to receive_message_chain(:window, :cursor, :column)
|
29
|
+
subject.column
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#command" do
|
34
|
+
let(:resolver) { described_class::Resolver }
|
35
|
+
|
36
|
+
it "returns a command resolver" do
|
37
|
+
expect(subject.command).to be_a(resolver)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#content" do
|
42
|
+
it "returns buffer content for the current line" do
|
43
|
+
line = "test"
|
44
|
+
buffer = double("buffer")
|
45
|
+
expect(buffer).to receive(:line).with(line)
|
46
|
+
expect(subject).to receive(:line).and_return(line)
|
47
|
+
expect(subject).to receive(:buffer).and_return(buffer)
|
48
|
+
subject.content
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "#cursor" do
|
53
|
+
it "delegates to editor.window" do
|
54
|
+
expect(editor).to receive_message_chain(:window, :cursor)
|
55
|
+
subject.cursor
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "#line" do
|
60
|
+
it "delegates to editor.window.line" do
|
61
|
+
expect(editor).to receive_message_chain(:window, :cursor, :line)
|
62
|
+
subject.line
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "#run" do
|
67
|
+
it "must be implemented" do
|
68
|
+
expect { subject.run }.to raise_error(NotImplementedError)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "#state" do
|
73
|
+
it "delegates to editor.window" do
|
74
|
+
expect(editor).to receive_message_chain(:window, :state)
|
75
|
+
subject.state
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "#window" do
|
80
|
+
it "delegates to editor" do
|
81
|
+
expect(editor).to receive(:window)
|
82
|
+
subject.window
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe TextEditor::Component do
|
4
|
+
subject { constant.new(editor) }
|
5
|
+
|
6
|
+
let(:editor) { double("editor") }
|
7
|
+
|
8
|
+
let(:constant) do
|
9
|
+
Class.new.instance_exec(described_class) do |mod|
|
10
|
+
include mod
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#config" do
|
15
|
+
it "delegates to editor.config" do
|
16
|
+
expect(subject).to receive_message_chain(:editor, :config)
|
17
|
+
subject.config
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#editor" do
|
22
|
+
it "returns initialized editor" do
|
23
|
+
expect(subject.editor).to eq(editor)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#logger" do
|
28
|
+
it "delegates to editor.logger" do
|
29
|
+
expect(subject).to receive_message_chain(:editor, :logger)
|
30
|
+
subject.logger
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "#terminal" do
|
35
|
+
it "delegates to editor.terminal" do
|
36
|
+
expect(subject).to receive_message_chain(:editor, :terminal)
|
37
|
+
subject.terminal
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe TextEditor::Configuration do
|
4
|
+
subject { described_class.new(editor) }
|
5
|
+
|
6
|
+
let(:editor) { double("editor") }
|
7
|
+
|
8
|
+
describe "#files" do
|
9
|
+
it "is an accessor" do
|
10
|
+
expect(subject.files).to be_nil
|
11
|
+
|
12
|
+
subject.files = "test"
|
13
|
+
expect(subject.files).to eq("test")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#log" do
|
18
|
+
it "is an accessor" do
|
19
|
+
expect(subject.log).to be_nil
|
20
|
+
|
21
|
+
subject.log = "test"
|
22
|
+
expect(subject.log).to eq("test")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe TextEditor::Cursor do
|
4
|
+
subject { described_class.new(line, column) }
|
5
|
+
|
6
|
+
let(:line) { 1 }
|
7
|
+
let(:column) { 2 }
|
8
|
+
|
9
|
+
describe "#column" do
|
10
|
+
it "returns current column" do
|
11
|
+
expect(subject.column).to eq(2)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#down" do
|
16
|
+
it "moves cursor down" do
|
17
|
+
actual = subject.down
|
18
|
+
expect(actual.position).to eq([2, 2])
|
19
|
+
|
20
|
+
actual = subject.down(2)
|
21
|
+
expect(actual.position).to eq([3, 2])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#initialize" do
|
26
|
+
it "sets line and column to 0 by default" do
|
27
|
+
expect(described_class.new.position).to eq([0, 0])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "#left" do
|
32
|
+
it "moves cursor left" do
|
33
|
+
actual = subject.left
|
34
|
+
expect(actual.position).to eq([1, 1])
|
35
|
+
|
36
|
+
actual = subject.left(2)
|
37
|
+
expect(actual.position).to eq([1, 0])
|
38
|
+
|
39
|
+
actual = subject.left(3)
|
40
|
+
expect(actual.position).to eq([1, 0])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#line" do
|
45
|
+
it "returns current line" do
|
46
|
+
expect(subject.line).to eq(1)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "#move" do
|
51
|
+
it "moves cursor" do
|
52
|
+
actual = subject.move(4, 5)
|
53
|
+
expect(actual.position).to eq([4, 5])
|
54
|
+
end
|
55
|
+
|
56
|
+
it "does not allow negative values" do
|
57
|
+
actual = subject.move(-4, 5)
|
58
|
+
expect(actual.position).to eq([0, 5])
|
59
|
+
|
60
|
+
actual = subject.move(5, -4)
|
61
|
+
expect(actual.position).to eq([5, 0])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "#position" do
|
66
|
+
let(:line) { 1 }
|
67
|
+
let(:column) { 2 }
|
68
|
+
|
69
|
+
it "returns an array with the line and column" do
|
70
|
+
expect(subject.position).to eq([1, 2])
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "#right" do
|
75
|
+
it "moves cursor right" do
|
76
|
+
actual = subject.right
|
77
|
+
expect(actual.position).to eq([1, 3])
|
78
|
+
|
79
|
+
actual = subject.right(2)
|
80
|
+
expect(actual.position).to eq([1, 4])
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "#up" do
|
85
|
+
it "moves cursor up" do
|
86
|
+
actual = subject.up
|
87
|
+
expect(actual.position).to eq([0, 2])
|
88
|
+
|
89
|
+
actual = subject.up(2)
|
90
|
+
expect(actual.position).to eq([0, 2])
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe TextEditor::OperatingSystem do
|
4
|
+
subject { described_class.new(version) }
|
5
|
+
|
6
|
+
describe "#type" do
|
7
|
+
context "cygwin" do
|
8
|
+
let(:version) { "some-cygwin-version" }
|
9
|
+
|
10
|
+
it "parses correct type" do
|
11
|
+
expect(subject.type).to eq(:Cygwin)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context "linux" do
|
16
|
+
let(:version) { "i686-linux" }
|
17
|
+
|
18
|
+
it "parses correct type" do
|
19
|
+
expect(subject.type).to eq(:Linux)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "mac" do
|
24
|
+
let(:version) { "x86_64-darwin14" }
|
25
|
+
|
26
|
+
it "parses correct type" do
|
27
|
+
expect(subject.type).to eq(:Mac)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "unknown" do
|
32
|
+
let(:version) { "some-other-version" }
|
33
|
+
|
34
|
+
it "parses correct type" do
|
35
|
+
expect(subject.type).to eq(:Unknown)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "windows" do
|
40
|
+
let(:version) { "some-mswin-version" }
|
41
|
+
|
42
|
+
it "parses correct type" do
|
43
|
+
expect(subject.type).to eq(:Windows)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#version" do
|
49
|
+
let(:version) { "example" }
|
50
|
+
|
51
|
+
it "returns initialized version" do
|
52
|
+
expect(subject.version).to eq(version)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe TextEditor::Reactor do
|
4
|
+
subject { described_class.new(editor) }
|
5
|
+
|
6
|
+
let(:editor) { double("editor") }
|
7
|
+
|
8
|
+
describe "#start" do
|
9
|
+
it "starts running" do
|
10
|
+
expect(subject).to receive(:render)
|
11
|
+
expect(subject).to receive(:listen)
|
12
|
+
|
13
|
+
expect(subject).not_to be_running
|
14
|
+
subject.start
|
15
|
+
expect(subject).to be_running
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#stop" do
|
20
|
+
it "stops running" do
|
21
|
+
subject.instance_variable_set("@running", true)
|
22
|
+
expect(subject).to be_running
|
23
|
+
|
24
|
+
subject.stop
|
25
|
+
expect(subject).not_to be_running
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#running?" do
|
30
|
+
it "is not running by default" do
|
31
|
+
expect(subject).not_to be_running
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe TextEditor::State do
|
4
|
+
subject { described_class.new(buffer, cursor) }
|
5
|
+
|
6
|
+
let(:buffer) { double("buffer") }
|
7
|
+
let(:cursor) { double("cursor") }
|
8
|
+
|
9
|
+
describe "#buffer" do
|
10
|
+
it "returns initialized buffer" do
|
11
|
+
expect(subject.buffer).to eq(buffer)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#cursor" do
|
16
|
+
it "returns initialized cursor" do
|
17
|
+
expect(subject.cursor).to eq(cursor)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/text_editor.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.authors = "Sean Huber"
|
3
|
+
s.email = "sean@shuber.io"
|
4
|
+
s.extra_rdoc_files = %w(LICENSE)
|
5
|
+
s.files = `git ls-files`.split("\n")
|
6
|
+
s.homepage = "https://github.com/shuber/text_editor"
|
7
|
+
s.license = "MIT"
|
8
|
+
s.name = "text_editor"
|
9
|
+
s.required_ruby_version = ">= 2.0.0"
|
10
|
+
s.rdoc_options = %w(--charset=UTF-8 --inline-source --line-numbers --main README.org)
|
11
|
+
s.summary = "Text editor"
|
12
|
+
s.test_files = `git ls-files -- spec/*`.split("\n")
|
13
|
+
s.version = "0.0.0"
|
14
|
+
|
15
|
+
s.executables << "te"
|
16
|
+
|
17
|
+
s.add_dependency "curses", "1.0.2"
|
18
|
+
end
|