text_editor 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/.text_editor.rb +81 -2
  4. data/Gemfile.lock +1 -0
  5. data/README.org +103 -0
  6. data/bin/console +1 -1
  7. data/lib/core_ext/hash.rb +2 -0
  8. data/lib/core_ext/pathname.rb +2 -0
  9. data/lib/text_editor.rb +43 -13
  10. data/lib/text_editor/buffer.rb +3 -0
  11. data/lib/text_editor/clipboard.rb +1 -1
  12. data/lib/text_editor/clipboard/cygwin.rb +0 -4
  13. data/lib/text_editor/clipboard/linux.rb +0 -4
  14. data/lib/text_editor/clipboard/mac.rb +0 -4
  15. data/lib/text_editor/clipboard/unknown.rb +0 -4
  16. data/lib/text_editor/clipboard/windows.rb +0 -4
  17. data/lib/text_editor/command.rb +4 -4
  18. data/lib/text_editor/command/append_line_and_insert.rb +11 -0
  19. data/lib/text_editor/command/change_mode.rb +9 -0
  20. data/lib/text_editor/command/cursor_end_and_insert.rb +9 -0
  21. data/lib/text_editor/command/cursor_left.rb +1 -8
  22. data/lib/text_editor/command/cursor_left_with_wrap.rb +16 -0
  23. data/lib/text_editor/command/cursor_right.rb +2 -9
  24. data/lib/text_editor/command/cursor_right_and_insert.rb +9 -0
  25. data/lib/text_editor/command/cursor_right_with_wrap.rb +19 -0
  26. data/lib/text_editor/command/cursor_start_without_whitespace.rb +11 -0
  27. data/lib/text_editor/command/cursor_up.rb +1 -1
  28. data/lib/text_editor/command/forward_word.rb +16 -0
  29. data/lib/text_editor/command/insert_tab.rb +1 -1
  30. data/lib/text_editor/command/prepend_line_and_insert.rb +11 -0
  31. data/lib/text_editor/command/replace_line.rb +13 -0
  32. data/lib/text_editor/command/resolver.rb +3 -2
  33. data/lib/text_editor/component.rb +1 -1
  34. data/lib/text_editor/configuration.rb +11 -3
  35. data/lib/text_editor/configuration/bootstrap.rb +9 -3
  36. data/lib/text_editor/configuration/flavor.rb +20 -0
  37. data/lib/text_editor/configuration/log.rb +7 -0
  38. data/lib/text_editor/configuration/mode.rb +28 -0
  39. data/lib/text_editor/configuration/operating_system.rb +21 -0
  40. data/lib/text_editor/configuration/resolver.rb +1 -1
  41. data/lib/text_editor/configuration/tab.rb +11 -0
  42. data/lib/text_editor/cursor.rb +2 -0
  43. data/lib/text_editor/keyboard.rb +1 -1
  44. data/lib/text_editor/logger.rb +11 -0
  45. data/lib/text_editor/reactor.rb +5 -3
  46. data/lib/text_editor/terminal.rb +2 -19
  47. data/lib/text_editor/window.rb +34 -5
  48. data/spec/spec_helper.rb +3 -0
  49. data/spec/support/command_helper.rb +43 -0
  50. data/spec/text_editor/clipboard_spec.rb +0 -7
  51. data/spec/text_editor/command/append_line_and_insert_spec.rb +38 -0
  52. data/spec/text_editor/command/backspace_spec.rb +54 -0
  53. data/spec/text_editor/command/change_mode_spec.rb +17 -0
  54. data/spec/text_editor/command/cursor_down_spec.rb +57 -0
  55. data/spec/text_editor/command/cursor_end_and_insert_spec.rb +35 -0
  56. data/spec/text_editor/command/cursor_end_spec.rb +30 -0
  57. data/spec/text_editor/command/cursor_left_spec.rb +46 -0
  58. data/spec/text_editor/command/cursor_left_with_wrap_spec.rb +46 -0
  59. data/spec/text_editor/command/cursor_right_and_insert_spec.rb +58 -0
  60. data/spec/text_editor/command/cursor_right_spec.rb +53 -0
  61. data/spec/text_editor/command/cursor_right_with_wrap_spec.rb +54 -0
  62. data/spec/text_editor/command/cursor_start_spec.rb +33 -0
  63. data/spec/text_editor/command/cursor_start_without_whitespace_spec.rb +46 -0
  64. data/spec/text_editor/command/cursor_up_spec.rb +56 -0
  65. data/spec/text_editor/command/forward_word_spec.rb +8 -0
  66. data/spec/text_editor/command/insert_char_spec.rb +24 -0
  67. data/spec/text_editor/command/insert_line_spec.rb +26 -0
  68. data/spec/text_editor/command/insert_tab_spec.rb +27 -0
  69. data/spec/text_editor/command/prepend_line_and_insert_spec.rb +38 -0
  70. data/spec/text_editor/command/replace_line_spec.rb +34 -0
  71. data/spec/text_editor/command/resolver_spec.rb +2 -2
  72. data/spec/text_editor/command/write_file_spec.rb +25 -0
  73. data/spec/text_editor/command_spec.rb +8 -8
  74. data/spec/text_editor/component_spec.rb +7 -0
  75. data/spec/text_editor/configuration/log_spec.rb +26 -0
  76. data/spec/text_editor/{operating_system_spec.rb → configuration/operating_system_spec.rb} +1 -1
  77. data/spec/text_editor/configuration/tab_spec.rb +41 -0
  78. data/spec/text_editor/configuration_spec.rb +11 -4
  79. data/spec/text_editor/logger_spec.rb +17 -0
  80. data/text_editor.gemspec +2 -1
  81. metadata +84 -7
  82. data/lib/text_editor/mode.rb +0 -23
  83. data/lib/text_editor/mode/nano.rb +0 -30
  84. data/lib/text_editor/operating_system.rb +0 -19
@@ -1,10 +1,6 @@
1
1
  class TextEditor
2
2
  class Clipboard
3
3
  class Windows
4
- def clear
5
- raise NotImplementedError
6
- end
7
-
8
4
  def copy(_content)
9
5
  raise NotImplementedError
10
6
  end
@@ -11,10 +11,6 @@ class TextEditor
11
11
  new(editor).run(*args, &block)
12
12
  end
13
13
 
14
- def command
15
- @command ||= Resolver.new(editor)
16
- end
17
-
18
14
  def content
19
15
  buffer.line(line)
20
16
  end
@@ -22,5 +18,9 @@ class TextEditor
22
18
  def run(*)
23
19
  raise NotImplementedError
24
20
  end
21
+
22
+ def scan
23
+ @scan ||= StringScanner.new(content)
24
+ end
25
25
  end
26
26
  end
@@ -0,0 +1,11 @@
1
+ class TextEditor
2
+ class Command
3
+ class AppendLineAndInsert < Command
4
+ def run(*)
5
+ new_buffer = buffer.insert_line(line.next)
6
+ window.update(new_buffer, cursor)
7
+ command.cursor_down.change_mode(:insert)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ class TextEditor
2
+ class Command
3
+ class ChangeMode < Command
4
+ def run(mode, *)
5
+ editor.mode = editor.flavor.modes.fetch(mode)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class TextEditor
2
+ class Command
3
+ class CursorEndAndInsert < Command
4
+ def run(*)
5
+ command.cursor_end.change_mode(:insert)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -2,14 +2,7 @@ class TextEditor
2
2
  class Command
3
3
  class CursorLeft < Command
4
4
  def run(*)
5
- if column.zero? && line.nonzero?
6
- column = buffer.line(line.pred).size
7
- new_cursor = cursor.move(line.pred, column)
8
- else
9
- new_cursor = cursor.left
10
- end
11
-
12
- window.update(buffer, new_cursor)
5
+ window.update(buffer, cursor.left)
13
6
  end
14
7
  end
15
8
  end
@@ -0,0 +1,16 @@
1
+ class TextEditor
2
+ class Command
3
+ class CursorLeftWithWrap < Command
4
+ def run(*)
5
+ if column.zero? && line.nonzero?
6
+ column = buffer.line(line.pred).size
7
+ new_cursor = cursor.move(line.pred, column)
8
+ else
9
+ new_cursor = cursor.left
10
+ end
11
+
12
+ window.update(buffer, new_cursor)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -2,16 +2,9 @@ class TextEditor
2
2
  class Command
3
3
  class CursorRight < Command
4
4
  def run(*)
5
- columns = content.size
6
5
  lines = buffer.size.pred
7
-
8
- new_cursor =
9
- if column == columns && line != lines
10
- cursor.move(line.next, 0)
11
- else
12
- cursor.right.clamp(lines, columns)
13
- end
14
-
6
+ columns = buffer.line(line).size
7
+ new_cursor = cursor.right.clamp(lines, columns)
15
8
  window.update(buffer, new_cursor)
16
9
  end
17
10
  end
@@ -0,0 +1,9 @@
1
+ class TextEditor
2
+ class Command
3
+ class CursorRightAndInsert < Command
4
+ def run(*)
5
+ command.cursor_right.change_mode(:insert)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ class TextEditor
2
+ class Command
3
+ class CursorRightWithWrap < Command
4
+ def run(*)
5
+ columns = content.size
6
+ lines = buffer.size.pred
7
+
8
+ new_cursor =
9
+ if column == columns && line != lines
10
+ cursor.move(line.next, 0)
11
+ else
12
+ cursor.right.clamp(lines, columns)
13
+ end
14
+
15
+ window.update(buffer, new_cursor)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ class TextEditor
2
+ class Command
3
+ class CursorStartWithoutWhitespace < Command
4
+ def run(*)
5
+ index = scan.skip(/\s+/) || 0
6
+ new_cursor = cursor.move(line, index)
7
+ window.update(buffer, new_cursor)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -2,7 +2,7 @@ class TextEditor
2
2
  class Command
3
3
  class CursorUp < Command
4
4
  def run(*)
5
- return unless line.zero?
5
+ return if line.zero?
6
6
 
7
7
  columns = buffer.line(line.pred).size
8
8
  new_cursor = cursor.up.clamp(line.pred, columns)
@@ -0,0 +1,16 @@
1
+ class TextEditor
2
+ class Command
3
+ class ForwardWord < Command
4
+ def run(*)
5
+ lines = buffer.size.pred
6
+ index = content.index(/\W/, column.next)
7
+ index ||= column
8
+
9
+ new_cursor = cursor.move(line, index.next)
10
+ new_cursor = new_cursor.clamp(lines, content.size)
11
+
12
+ window.update(buffer, new_cursor)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -2,7 +2,7 @@ class TextEditor
2
2
  class Command
3
3
  class InsertTab < Command
4
4
  def run(*)
5
- command.insert_char(" ")
5
+ command.insert_char(config.tab.to_s)
6
6
  end
7
7
  end
8
8
  end
@@ -0,0 +1,11 @@
1
+ class TextEditor
2
+ class Command
3
+ class PrependLineAndInsert < Command
4
+ def run(*)
5
+ new_buffer = buffer.insert_line(line)
6
+ window.update(new_buffer, cursor)
7
+ command.cursor_start.change_mode(:insert)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ class TextEditor
2
+ class Command
3
+ class ReplaceLine < Command
4
+ def run(*)
5
+ new_buffer = buffer.replace_line(line)
6
+ new_cursor = cursor.move(line, 0)
7
+ window.update(new_buffer, new_cursor)
8
+
9
+ command.change_mode(:insert)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -6,11 +6,12 @@ class TextEditor
6
6
  def method_missing(method, *args, &block)
7
7
  command = resolve(method)
8
8
  return super unless command
9
- command.run(*args, &block)
9
+ command.call(editor, *args, &block)
10
10
  self
11
11
  end
12
12
 
13
13
  def resolve(method)
14
+ return method if method.respond_to?(:call)
14
15
  parts = method.to_s.split("_")
15
16
  name = parts.map(&:capitalize).join
16
17
  command(name)
@@ -19,7 +20,7 @@ class TextEditor
19
20
  private
20
21
 
21
22
  def command(name)
22
- namespace.const_get(name).new(editor)
23
+ namespace.const_get(name)
23
24
  rescue NameError
24
25
  nil
25
26
  end
@@ -2,7 +2,7 @@ class TextEditor
2
2
  module Component
3
3
  extend Forwardable
4
4
 
5
- def_delegators :editor, :config, :logger, :terminal
5
+ def_delegators :editor, :command, :config, :logger, :terminal
6
6
 
7
7
  attr_reader :editor
8
8
 
@@ -2,14 +2,22 @@ class TextEditor
2
2
  class Configuration
3
3
  include Component
4
4
 
5
- attr_accessor :files
5
+ attr_accessor :files, :default_flavor
6
+
7
+ def config
8
+ self
9
+ end
10
+
11
+ def flavor(name, &block)
12
+ editor.flavors[name] = Flavor.new(name, &block)
13
+ end
6
14
 
7
15
  def log
8
- @log ||= OpenStruct.new
16
+ @log ||= Log.new
9
17
  end
10
18
 
11
19
  def tab
12
- @tab ||= OpenStruct.new
20
+ @tab ||= Tab.new
13
21
  end
14
22
  end
15
23
  end
@@ -14,15 +14,21 @@ class TextEditor
14
14
  end
15
15
 
16
16
  resolver.files.each do |file|
17
- require file
17
+ load file
18
18
  end
19
19
  end
20
20
 
21
21
  def options
22
22
  @options ||= OptionParser.new do |opts|
23
- opts.on("--log [FILE]") { |log| config.log = log }
23
+ opts.on("--log FILE", "-l") do |log|
24
+ config.log.file = log
25
+ end
26
+
27
+ opts.on("--flavor FLAVOR", "-f") do |flavor|
28
+ config.default_flavor = flavor.to_sym
29
+ end
24
30
 
25
- opts.on("--version") do
31
+ opts.on("--version", "-v") do
26
32
  puts editor.version
27
33
  raise SystemExit
28
34
  end
@@ -0,0 +1,20 @@
1
+ class TextEditor
2
+ class Configuration
3
+ class Flavor
4
+ attr_reader :name
5
+
6
+ def initialize(name, &block)
7
+ @name = name.to_sym
8
+ instance_eval(&block) if block
9
+ end
10
+
11
+ def mode(name, &block)
12
+ modes[name.to_sym] = Mode.new(name, &block)
13
+ end
14
+
15
+ def modes
16
+ @modes ||= {}
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,7 @@
1
+ class TextEditor
2
+ class Configuration
3
+ class Log
4
+ attr_accessor :file, :level, :options
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,28 @@
1
+ class TextEditor
2
+ class Configuration
3
+ class Mode
4
+ attr_reader :name
5
+
6
+ def initialize(name, &block)
7
+ @name = name.to_sym
8
+ instance_eval(&block) if block
9
+ end
10
+
11
+ def bind(key, command = nil, *args, &block)
12
+ keybindings[key] = [command || block, args]
13
+ end
14
+
15
+ def keybindings
16
+ @keybindings ||= {}
17
+ end
18
+
19
+ def resolve(key)
20
+ keybindings.each do |keybinding, command|
21
+ return command if keybinding === key
22
+ end
23
+
24
+ nil
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,21 @@
1
+ class TextEditor
2
+ class Configuration
3
+ class OperatingSystem
4
+ attr_reader :version
5
+
6
+ def initialize(version)
7
+ @version = version
8
+ end
9
+
10
+ def type
11
+ case version
12
+ when /cygwin/ then :Cygwin
13
+ when /bsd|linux/ then :Linux
14
+ when /darwin|mac/ then :Mac
15
+ when /mingw|mswin/ then :Windows
16
+ else :Unknown
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -3,7 +3,7 @@ class TextEditor
3
3
  class Resolver
4
4
  include Component
5
5
 
6
- FILE = ".text_editor.rb"
6
+ FILE = ".text_editor.rb".freeze
7
7
 
8
8
  def files
9
9
  @files ||= configs.select(&:exist?)
@@ -0,0 +1,11 @@
1
+ class TextEditor
2
+ class Configuration
3
+ class Tab
4
+ attr_accessor :soft, :width
5
+
6
+ def to_s
7
+ soft ? " " * width : "\t"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -1,4 +1,6 @@
1
1
  class TextEditor
2
+ # An internal representation of a cursor.
3
+ # Each {Window} has it's own cursor instance.
2
4
  class Cursor
3
5
  attr_reader :line, :column
4
6
 
@@ -16,7 +16,7 @@ class TextEditor
16
16
 
17
17
  def codes
18
18
  @codes ||= {
19
- 0 => [:ctrl, :space],
19
+ 0 => :ctrl_space,
20
20
  9 => :tab,
21
21
  27 => :escape,
22
22
  127 => :backspace,
@@ -0,0 +1,11 @@
1
+ class TextEditor
2
+ class Logger < ::Logger
3
+ def initialize(editor)
4
+ log = editor.config.log
5
+
6
+ super(log.file, *log.options) do |logger|
7
+ logger.level = log.level
8
+ end
9
+ end
10
+ end
11
+ end