amun 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +5 -5
  2. data/.rspec +1 -0
  3. data/.rubocop.yml +6 -1
  4. data/.ruby-version +1 -1
  5. data/Guardfile +26 -6
  6. data/README.md +1 -0
  7. data/Rakefile +4 -6
  8. data/_config.yml +1 -0
  9. data/amun.gemspec +6 -5
  10. data/exe/amun +1 -1
  11. data/lib/amun/application.rb +11 -5
  12. data/lib/amun/behaviours/emacs.rb +14 -122
  13. data/lib/amun/behaviours/erasing.rb +67 -0
  14. data/lib/amun/behaviours/insertion.rb +22 -0
  15. data/lib/amun/behaviours/movement.rb +83 -0
  16. data/lib/amun/buffer.rb +103 -0
  17. data/lib/amun/event_manager.rb +12 -3
  18. data/lib/amun/features/echo_event.rb +4 -1
  19. data/lib/amun/features/files.rb +26 -0
  20. data/lib/amun/helpers/colors.rb +8 -7
  21. data/lib/amun/helpers/keyboard.rb +25 -8
  22. data/lib/amun/major_modes/fundamental.rb +8 -40
  23. data/lib/amun/major_modes/irb.rb +40 -0
  24. data/lib/amun/mode_line_segments/buffer_name.rb +10 -0
  25. data/lib/amun/mode_line_segments/major_mode.rb +11 -0
  26. data/lib/amun/object.rb +21 -0
  27. data/lib/amun/primitives/rect.rb +15 -0
  28. data/lib/amun/version.rb +1 -1
  29. data/lib/amun/windows/base.rb +42 -0
  30. data/lib/amun/windows/buffer_window.rb +73 -0
  31. data/lib/amun/windows/echo_area.rb +34 -0
  32. data/lib/amun/windows/frame.rb +92 -0
  33. data/lib/amun/windows/mini_buffer_window.rb +126 -0
  34. data/lib/amun/windows/mode_line.rb +50 -0
  35. data/lib/amun/windows/text_renderer.rb +41 -0
  36. metadata +56 -31
  37. data/lib/amun/ui/buffer.rb +0 -90
  38. data/lib/amun/ui/echo_area.rb +0 -42
  39. data/lib/amun/ui/mode_line.rb +0 -44
  40. data/lib/amun/ui/mode_line_segments/buffer_name.rb +0 -16
  41. data/lib/amun/ui/mode_line_segments/major_mode.rb +0 -17
  42. data/lib/amun/ui/windows/buffer_window.rb +0 -40
  43. data/lib/amun/ui/windows/frame.rb +0 -75
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 10726ce656cbf6445cbee3177cda26541982d3b7
4
- data.tar.gz: 0da415d031b8e63ff1fb5fb4b92537117ed1ad4f
2
+ SHA256:
3
+ metadata.gz: c19c96c195a1b4c07b725041f93ab2d4b55c686bfe43f43f3b073ae05cc12ade
4
+ data.tar.gz: 1760fc63a9b6748b887c5f502736713c18b1428b03ecc72488073e5444f99a29
5
5
  SHA512:
6
- metadata.gz: a6eef223a7d0a123259da8edea864b7a701b8d8d048fdef6ff0dc1002a187b4008c4ed0b967e1db95fb5345156c611063ef4525e67550efbe7da2017c925d486
7
- data.tar.gz: b475175d8acf045f52ac748be2243c7b46d1a4576334a768d671a42295bcf899f8cdd0e8bbf12152135b1bf22ce7537f8fb9ac38b8481a75be651a2e63f88d7b
6
+ metadata.gz: 7f424f219a3815db2c5499862f5ffdb65c27a1f55eaecb50a79fbda5a9b6fc69aac93bc392a6b19d318cc107953b491bae528e605b07f2c26e6ded8b5b22bfaa
7
+ data.tar.gz: '0866d9947a3e98b8e5a08d8d3f3c09e6c0b41727f86e15cd383f20b1780a0999cdcf14ed3d5087ecd7dd903aad600cb2ac7846b2b971a3d19eb821e5c97dd2bd'
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color --format doc
@@ -1,6 +1,7 @@
1
1
  AllCops:
2
+ TargetRubyVersion: 2.0
2
3
  Exclude:
3
- - 'test/**/*'
4
+ - 'spec/**/*'
4
5
  - '*.gemspec'
5
6
  DisplayCopNames: true
6
7
  Style/FrozenStringLiteralComment:
@@ -9,3 +10,7 @@ Style/StringLiterals:
9
10
  Enabled: false
10
11
  Style/WordArray:
11
12
  Enabled: false
13
+ Metrics/LineLength:
14
+ Max: 120
15
+ Security/Eval:
16
+ Enabled: false
@@ -1 +1 @@
1
- 2.4.0
1
+ 2.6.6
data/Guardfile CHANGED
@@ -1,7 +1,27 @@
1
- directories ['lib', 'test']
2
- guard :minitest do
3
- # with Minitest::Unit
4
- watch(%r{^test/(.*)\/?(.*)_test\.rb$})
5
- watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}#{m[2]}_test.rb" }
6
- watch(%r{^test/test_helper\.rb$}) { 'test' }
1
+ directories ['lib', 'spec']
2
+
3
+ # Note: The cmd option is now required due to the increasing number of ways
4
+ # rspec may be run, below are examples of the most common uses.
5
+ # * bundler: 'bundle exec rspec'
6
+ # * bundler binstubs: 'bin/rspec'
7
+ # * spring: 'bin/rspec' (This will use spring if running and you have
8
+ # installed the spring binstubs per the docs)
9
+ # * zeus: 'zeus rspec' (requires the server to be started separately)
10
+ # * 'just' rspec: 'rspec'
11
+
12
+ guard :rspec, cmd: "bundle exec rspec" do
13
+ require "guard/rspec/dsl"
14
+ dsl = Guard::RSpec::Dsl.new(self)
15
+
16
+ # Feel free to open issues for suggestions and improvements
17
+
18
+ # RSpec files
19
+ rspec = dsl.rspec
20
+ watch(rspec.spec_helper) { rspec.spec_dir }
21
+ watch(rspec.spec_support) { rspec.spec_dir }
22
+ watch(rspec.spec_files)
23
+
24
+ # Ruby files
25
+ ruby = dsl.ruby
26
+ dsl.watch_spec_files_for(ruby.lib_files)
7
27
  end
data/README.md CHANGED
@@ -5,6 +5,7 @@
5
5
  [![Build Status](https://travis-ci.org/blazeeboy/amun.svg?branch=master)](https://travis-ci.org/blazeeboy/amun)
6
6
  [![Code Climate](https://codeclimate.com/github/blazeeboy/amun/badges/gpa.svg)](https://codeclimate.com/github/blazeeboy/amun)
7
7
  [![Test Coverage](https://codeclimate.com/github/blazeeboy/amun/badges/coverage.svg)](https://codeclimate.com/github/blazeeboy/amun)
8
+ [![Inline docs](http://inch-ci.org/github/blazeeboy/amun.svg?branch=master)](http://inch-ci.org/github/blazeeboy/amun)
8
9
 
9
10
  A minimal CLI text editor, built on Ruby, looking for Emacs as it's father and idol.
10
11
 
data/Rakefile CHANGED
@@ -1,10 +1,8 @@
1
1
  require "bundler/gem_tasks"
2
- require "rake/testtask"
3
2
 
4
- Rake::TestTask.new(:test) do |t|
5
- t.libs << "test"
6
- t.libs << "lib"
7
- t.test_files = FileList['test/**/*_test.rb']
3
+ begin
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec)
8
6
  end
9
7
 
10
- task default: :test
8
+ task default: :spec
@@ -0,0 +1 @@
1
+ theme: jekyll-theme-minimal
@@ -7,7 +7,7 @@ Gem::Specification.new do |spec|
7
7
  spec.name = "amun"
8
8
  spec.version = Amun::VERSION
9
9
  spec.authors = ["Emad Elsaid"]
10
- spec.email = ["blazeeboy@gmail.com"]
10
+ spec.email = ["emad.elsaid.hamed@gmail.com"]
11
11
 
12
12
  spec.summary = "Emacs like editor, with Ruby core instead of ELisp"
13
13
  spec.description = "A CLI editor built to have an Emacs similar development environment, with ruby in the heart of it instead of Elisp, that will make developing plugins and extensions faster and more enjoyable, this editor is kept to the minimum, anything that could be written as an pluging will be found as a plugin."
@@ -21,13 +21,14 @@ Gem::Specification.new do |spec|
21
21
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
22
  spec.require_paths = ["lib"]
23
23
 
24
- spec.add_dependency "curses", "~> 1.2"
25
- spec.add_development_dependency "bundler", "~> 1.14"
24
+ spec.add_dependency "curses"
25
+ spec.add_development_dependency "bundler"
26
26
  spec.add_development_dependency "rake", "~> 10.0"
27
- spec.add_development_dependency "minitest", "~> 5.0"
28
27
  spec.add_development_dependency "pry"
29
28
  spec.add_development_dependency "guard"
30
- spec.add_development_dependency "guard-minitest"
29
+ spec.add_development_dependency "rspec"
30
+ spec.add_development_dependency "rspec-its"
31
+ spec.add_development_dependency "guard-rspec"
31
32
  spec.add_development_dependency "simplecov"
32
33
  spec.add_development_dependency "codeclimate-test-reporter", "~> 1.0.0"
33
34
  end
data/exe/amun CHANGED
@@ -4,4 +4,4 @@ $LOAD_PATH.unshift(File.dirname(File.realpath(__FILE__)) + '/../lib')
4
4
 
5
5
  require 'amun'
6
6
 
7
- Amun::Application.instance.run
7
+ Amun::Application.run
@@ -1,6 +1,7 @@
1
1
  require 'curses'
2
2
  require 'singleton'
3
- require 'amun/ui/windows/frame'
3
+ require 'forwardable'
4
+ require 'amun/windows/frame'
4
5
  require 'amun/event_manager'
5
6
  require 'amun/features_loader'
6
7
  require 'amun/helpers/keyboard'
@@ -14,7 +15,7 @@ module Amun
14
15
  attr_writer :frame
15
16
 
16
17
  def frame
17
- @frame ||= UI::Windows::Frame.new
18
+ @frame ||= Windows::Frame.new
18
19
  end
19
20
 
20
21
  def run
@@ -24,6 +25,12 @@ module Amun
24
25
  keyboard_thread.join
25
26
  end
26
27
 
28
+ class << self
29
+ extend Forwardable
30
+ def_delegators :instance,
31
+ :run, :frame, :frame=
32
+ end
33
+
27
34
  private
28
35
 
29
36
  def init_curses
@@ -41,9 +48,8 @@ module Amun
41
48
  chain = []
42
49
  while (ch = Helpers::Keyboard.char)
43
50
  chain << ch
44
- if EventManager.join(chain.join(' '), frame) != EventManager::CHAINED
45
- chain.clear
46
- end
51
+ chain.clear if frame.trigger(chain.join(' ')) != EventManager::CHAINED
52
+ frame.render
47
53
  end
48
54
  end
49
55
  end
@@ -1,130 +1,22 @@
1
- require 'amun/ui/buffer'
1
+ require 'amun/behaviours/movement'
2
+ require 'amun/behaviours/erasing'
3
+ require 'amun/behaviours/insertion'
2
4
  require 'curses'
3
5
 
4
6
  module Amun
5
7
  module Behaviours
8
+ # Emacs default behaviour module
9
+ # should be included in the mode
10
+ # which needs this behaviour as part of it
6
11
  module Emacs
7
- def emacs_behaviour_initialize(event_manager)
8
- event_manager.bind "\C-f", self, :forward_char
9
- event_manager.bind Curses::KEY_RIGHT.to_s, self, :forward_char
10
-
11
- event_manager.bind "\C-b", self, :backward_char
12
- event_manager.bind Curses::KEY_LEFT.to_s, self, :backward_char
13
-
14
- event_manager.bind "\C-n", self, :next_line
15
- event_manager.bind Curses::KEY_DOWN.to_s, self, :next_line
16
-
17
- event_manager.bind "\C-p", self, :previous_line
18
- event_manager.bind Curses::KEY_UP.to_s, self, :previous_line
19
-
20
- event_manager.bind "\C-a", self, :beginning_of_line
21
- event_manager.bind Curses::KEY_HOME.to_s, self, :beginning_of_line
22
-
23
- event_manager.bind "\C-e", self, :end_of_line
24
- event_manager.bind Curses::KEY_END.to_s, self, :end_of_line
25
-
26
- event_manager.bind "\C-d", self, :delete_char
27
- event_manager.bind Curses::Key::BACKSPACE.to_s, self, :backward_delete_char # this doesn't work, check linux
28
- event_manager.bind "\C-?", self, :backward_delete_char # C-? is backspace on mac terminal for some reason
29
-
30
- event_manager.bind Curses::Key::DC.to_s, self, :forward_delete_char
31
-
32
- event_manager.bind "\C-k", self, :kill_line
33
- event_manager.bind "\M-d", self, :kill_word
34
- end
35
-
36
- def forward_char(*)
37
- buffer.point += 1
38
- true
39
- end
40
-
41
- def backward_char(*)
42
- buffer.point -= 1
43
- true
44
- end
45
-
46
- def next_line(*)
47
- line_begin = buffer.text.rindex("\n", buffer.point) || 0
48
- line_end = buffer.text.index("\n", buffer.point + 1) || buffer.text.size + 1
49
- next_line_end = buffer.text.index("\n", line_end + 1) || buffer.text.size + 1
50
- point_offset = buffer.point - line_begin
51
- buffer.point = [line_end + point_offset, next_line_end].min
52
- true
53
- end
54
-
55
- def previous_line(*)
56
- line_begin = buffer.text.rindex("\n", buffer.point) || 0
57
- previous_line_begin = buffer.text.rindex("\n", line_begin - 1) || 0
58
- point_offset = buffer.point - line_begin
59
- buffer.point = [previous_line_begin + point_offset, line_begin - 1].min
60
- true
61
- end
62
-
63
- def beginning_of_line(*)
64
- point = buffer.point
65
- return true if point.zero?
66
- return true if buffer.text[point - 1] == "\n"
67
-
68
- line_start = buffer.text.rindex("\n", point - 1)
69
- buffer.point = line_start.nil? ? 0 : line_start + 1
70
- true
71
- end
72
-
73
- def end_of_line(*)
74
- point = buffer.point
75
- return true if buffer.text[point] == "\n"
76
-
77
- line_end = buffer.text.index("\n", point)
78
- buffer.point = line_end || buffer.text.length
79
- true
80
- end
81
-
82
- def delete_char(*)
83
- buffer.text.slice!(buffer.point)
84
- true
85
- end
86
-
87
- def backward_delete_char(*)
88
- return true if buffer.point.zero?
89
-
90
- buffer.point -= 1
91
- buffer.text.slice!(buffer.point)
92
- true
93
- end
94
-
95
- def forward_delete_char(*)
96
- delete_char
97
- end
98
-
99
- # TODO should move text to kill ring
100
- def kill_line(*)
101
- if buffer.text[buffer.point] == "\n"
102
- buffer.text.slice!(buffer.point)
103
- return true
104
- end
105
-
106
- line_end = buffer.text.index(/$/, buffer.point)
107
- buffer.text.slice!(buffer.point...line_end)
108
- true
109
- end
110
-
111
- # TODO should move text to kill ring
112
- def kill_word(*)
113
- first_non_letter = buffer.text.index(/\P{L}/, buffer.point) || buffer.text.size
114
- word_beginning = buffer.text.index(/\p{L}/, first_non_letter) || buffer.text.size
115
- buffer.text.slice!(buffer.point...word_beginning)
116
- true
117
- end
118
-
119
- # This should be bound to \M-BACKSPACE or \M-DEL but I think the terminal doesn't send it
120
- # So the implementation will remain there until we find a way to catch this key
121
- #
122
- # TODO should move text to kill ring
123
- def backward_kill_word(*)
124
- first_letter_backward = buffer.text.rindex(/\p{L}/, buffer.point) || 0
125
- first_non_letter_before_word = buffer.text.rindex(/\P{L}/, first_letter_backward) || -1
126
- buffer.text.slice!(first_non_letter_before_word + 1 .. buffer.point)
127
- true
12
+ include Movement
13
+ include Erasing
14
+ include Insertion
15
+
16
+ def emacs_behaviour_initialize
17
+ movement_keymap_initialize
18
+ erasing_keymap_initialize
19
+ insertion_keymap_initialize
128
20
  end
129
21
  end
130
22
  end
@@ -0,0 +1,67 @@
1
+ module Amun
2
+ module Behaviours
3
+ # erasing, killing and deleting text from buffer and Emacs keymap for it
4
+ module Erasing
5
+ def erasing_keymap_initialize
6
+ bind "\C-d", self, :delete_char
7
+
8
+ # this doesn't work, check linux
9
+ bind Curses::Key::BACKSPACE, self, :backward_delete_char
10
+ # C-? is backspace on mac terminal for some reason
11
+ bind "\C-?", self, :backward_delete_char
12
+
13
+ bind Curses::Key::DC, self, :forward_delete_char
14
+ bind "\C-k", self, :kill_line
15
+ bind "\M-d", self, :kill_word
16
+ end
17
+
18
+ def delete_char(*)
19
+ buffer.slice!(buffer.point)
20
+ true
21
+ end
22
+
23
+ def backward_delete_char(*)
24
+ return true if buffer.point.zero?
25
+
26
+ buffer.point -= 1
27
+ buffer.slice!(buffer.point)
28
+ true
29
+ end
30
+
31
+ def forward_delete_char(*)
32
+ delete_char
33
+ end
34
+
35
+ # TODO: should move text to kill ring
36
+ def kill_line(*)
37
+ if buffer[buffer.point] == "\n"
38
+ buffer.slice!(buffer.point)
39
+ return true
40
+ end
41
+
42
+ line_end = buffer.index(/$/, buffer.point)
43
+ buffer.slice!(buffer.point, line_end - buffer.point)
44
+ true
45
+ end
46
+
47
+ # TODO: should move text to kill ring
48
+ def kill_word(*)
49
+ first_non_letter = buffer.index(/\P{L}/, buffer.point) || buffer.length
50
+ word_beginning = buffer.index(/\p{L}/, first_non_letter) || buffer.length
51
+ buffer.slice!(buffer.point, word_beginning - buffer.point)
52
+ true
53
+ end
54
+
55
+ # This should be bound to \M-BACKSPACE or \M-DEL but I think the terminal doesn't send it
56
+ # So the implementation will remain there until we find a way to catch this key
57
+ #
58
+ # TODO: should move text to kill ring
59
+ def backward_kill_word(*)
60
+ first_letter_backward = buffer.rindex(/\p{L}/, buffer.point) || 0
61
+ first_non_letter_before_word = buffer.rindex(/\P{L}/, first_letter_backward) || -1
62
+ buffer.slice!(first_non_letter_before_word + 1..buffer.point)
63
+ true
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,22 @@
1
+ module Amun
2
+ module Behaviours
3
+ # inserting text to buffer behaviour and emacs keymap
4
+ module Insertion
5
+ def insertion_keymap_initialize
6
+ bind_all self, :insert_char
7
+ end
8
+
9
+ def insert_char(char)
10
+ return true unless char.is_a? String
11
+ return true unless char.length == 1
12
+ return true unless char.valid_encoding?
13
+ return true unless char.match?(/[[:print:]\n\t]/)
14
+
15
+ buffer.insert(buffer.point, char)
16
+ buffer.point += 1
17
+
18
+ true
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,83 @@
1
+ module Amun
2
+ module Behaviours
3
+ # Movement behaviour between lines, characters and paragraphs
4
+ # along with emacs keymap for it
5
+ module Movement
6
+ # attach the movement events only
7
+ def movement_keymap_initialize
8
+ bind "\C-f", self, :forward_char
9
+ bind Curses::KEY_RIGHT, self, :forward_char
10
+
11
+ bind "\C-b", self, :backward_char
12
+ bind Curses::KEY_LEFT, self, :backward_char
13
+
14
+ bind "\C-n", self, :next_line
15
+ bind Curses::KEY_DOWN, self, :next_line
16
+
17
+ bind "\C-p", self, :previous_line
18
+ bind Curses::KEY_UP, self, :previous_line
19
+
20
+ bind "\C-a", self, :beginning_of_line
21
+ bind Curses::KEY_HOME, self, :beginning_of_line
22
+
23
+ bind "\C-e", self, :end_of_line
24
+ bind Curses::KEY_END, self, :end_of_line
25
+ end
26
+
27
+ def forward_char(*)
28
+ buffer.point += 1
29
+ true
30
+ end
31
+
32
+ def backward_char(*)
33
+ buffer.point -= 1
34
+ true
35
+ end
36
+
37
+ def next_line(*)
38
+ point = buffer.point
39
+
40
+ line_begin = buffer.rindex(/^/, point)
41
+
42
+ line_end = buffer.index(/$/, point)
43
+ return true if line_end == buffer.size
44
+
45
+ next_line_end = buffer.index(/$/, line_end + 1)
46
+ point_offset = point - line_begin
47
+ buffer.point = [line_end + 1 + point_offset, next_line_end].min
48
+ true
49
+ end
50
+
51
+ def previous_line(*)
52
+ point = buffer.point
53
+
54
+ line_begin = point == buffer.size && buffer[point - 1] == "\n" ? point : buffer.rindex(/^/, point)
55
+ return true if line_begin.zero?
56
+
57
+ previous_line_begin = buffer.rindex(/^/, line_begin - 1)
58
+
59
+ point_offset = point - line_begin
60
+ buffer.point = [previous_line_begin + point_offset, line_begin - 1].min
61
+ true
62
+ end
63
+
64
+ def beginning_of_line(*)
65
+ point = buffer.point
66
+ return true if point == buffer.size
67
+
68
+ line_start = buffer.rindex(/^/, point)
69
+ buffer.point = line_start <= point ? line_start : 0
70
+ true
71
+ end
72
+
73
+ def end_of_line(*)
74
+ point = buffer.point
75
+ return true if buffer[point] == "\n"
76
+
77
+ line_end = buffer.index("\n", point)
78
+ buffer.point = line_end || buffer.length
79
+ true
80
+ end
81
+ end
82
+ end
83
+ end