amun 0.1.3 → 0.2.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.
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
@@ -0,0 +1,42 @@
1
+ require 'amun/object'
2
+ require 'forwardable'
3
+ require 'curses'
4
+
5
+ module Amun
6
+ module Windows
7
+ # based on amun object, means it has event manager inside
8
+ # and respond to all event manager methods (bind, undind, trigger...etc)
9
+ # and has a size (Rect instance), it also expose the methods of the size
10
+ # to the public, it also creates a subwindow from the curses standard screen
11
+ # and resizes it whenever you set a new (size) value
12
+ class Base < Object
13
+ extend Forwardable
14
+
15
+ attr_reader :size
16
+ def_delegators :size, :top, :left, :width, :height
17
+
18
+ def initialize(size)
19
+ super()
20
+ @size = size
21
+ @curses_window = Curses.stdscr.subwin(height, width, top, left)
22
+ end
23
+
24
+ # change the object size
25
+ # the internal curses window will be
26
+ # resized and moved along with it
27
+ def size=(size)
28
+ @size = size
29
+ resize
30
+ end
31
+
32
+ private
33
+
34
+ attr_accessor :curses_window
35
+
36
+ def resize
37
+ curses_window.resize(height, width)
38
+ curses_window.move(top, left)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,73 @@
1
+ require 'amun/buffer'
2
+ require 'amun/windows/base'
3
+ require 'amun/windows/text_renderer'
4
+ require 'amun/windows/mode_line'
5
+ require 'amun/primitives/rect'
6
+ require 'forwardable'
7
+
8
+ module Amun
9
+ module Windows
10
+ # a window to display any buffer
11
+ # or the current buffer
12
+ class BufferWindow < Base
13
+ extend Forwardable
14
+
15
+ def_delegator :buffer, :trigger
16
+
17
+ attr_accessor :mode_line
18
+
19
+ def initialize(size, buffer = nil)
20
+ super(size)
21
+ @buffer = buffer
22
+ @mode_line = ModeLine.new(mode_line_size)
23
+ @text_renderer = TextRenderer.new(text_renderer_size)
24
+ end
25
+
26
+ def render
27
+ @text_renderer.render(buffer)
28
+ @mode_line.render(buffer)
29
+ end
30
+
31
+ # set a specific buffer to be displayed in this window
32
+ def display_buffer(buffer)
33
+ @buffer = buffer
34
+ end
35
+
36
+ # render current buffer from the Buffer class
37
+ def display_current_buffer
38
+ @buffer = nil
39
+ end
40
+
41
+ # get current buffer that this window is rendering
42
+ def buffer
43
+ @buffer || Buffer.current
44
+ end
45
+
46
+ private
47
+
48
+ def resize
49
+ super
50
+ @mode_line.size = mode_line_size
51
+ @text_renderer.size = text_renderer_size
52
+ end
53
+
54
+ def text_renderer_size
55
+ Rect.new(
56
+ top: top,
57
+ left: left,
58
+ width: width,
59
+ height: height - 1
60
+ )
61
+ end
62
+
63
+ def mode_line_size
64
+ Rect.new(
65
+ top: top + height - 1,
66
+ left: left,
67
+ width: width,
68
+ height: 1
69
+ )
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,34 @@
1
+ require 'amun/windows/base'
2
+ require 'amun/buffer'
3
+
4
+ module Amun
5
+ module Windows
6
+ # a line that is rendered by default at the end on the screen
7
+ # takes the whole width of screen
8
+ # should be linked to \*messages\* memory buffer and display new messages
9
+ # in the buffer text
10
+ class EchoArea < Base
11
+ def initialize(size)
12
+ super(size)
13
+ @last_messages_size = 0
14
+ end
15
+
16
+ def render
17
+ curses_window.erase
18
+ curses_window << message
19
+ curses_window.refresh
20
+ update_last_messages_size
21
+ end
22
+
23
+ private
24
+
25
+ def message
26
+ Buffer.messages[@last_messages_size..-1].strip.lines.last
27
+ end
28
+
29
+ def update_last_messages_size
30
+ @last_messages_size = Buffer.messages.length
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,92 @@
1
+ require 'curses'
2
+ require 'amun/event_manager'
3
+ require 'amun/buffer'
4
+ require 'amun/windows/base'
5
+ require 'amun/windows/buffer_window'
6
+ require 'amun/windows/echo_area'
7
+
8
+ module Amun
9
+ module Windows
10
+ # a Frame fills all the space in terminal
11
+ # renders an echo area or mini buffer if it exists
12
+ # and an object that
13
+ # respond to #render and #trigger, like buffer,
14
+ # or another window or so
15
+ class Frame < Base
16
+ attr_accessor :mini_buffer, :window, :echo_area
17
+ attr_writer :screen
18
+
19
+ def initialize(size = default_size)
20
+ super(size)
21
+ @window = BufferWindow.new(top_window_size)
22
+ @echo_area = EchoArea.new(bottom_window_size)
23
+ bind(Curses::KEY_RESIZE, self, :set_size_to_terminal)
24
+ end
25
+
26
+ def trigger(event)
27
+ EventManager.join(event, self.events, echo_area, mini_buffer || window, EventManager)
28
+ rescue StandardError => error
29
+ handle_exception(error)
30
+ end
31
+
32
+ def render
33
+ render_window(window)
34
+ render_window(bottom_area)
35
+ end
36
+
37
+ def set_size_to_terminal(*)
38
+ self.size = default_size
39
+ end
40
+
41
+ private
42
+
43
+ def resize
44
+ super
45
+ window.size = top_window_size
46
+ echo_area.size = bottom_window_size
47
+ end
48
+
49
+ def bottom_area
50
+ mini_buffer || echo_area
51
+ end
52
+
53
+ def default_size
54
+ Rect.new(
55
+ top: 0,
56
+ left: 0,
57
+ width: Curses.stdscr.maxx,
58
+ height: Curses.stdscr.maxy
59
+ )
60
+ end
61
+
62
+ def top_window_size
63
+ Rect.new(
64
+ top: top,
65
+ left: left,
66
+ width: width,
67
+ height: height - 1
68
+ )
69
+ end
70
+
71
+ def bottom_window_size
72
+ Rect.new(
73
+ top: top + height - 1,
74
+ left: left,
75
+ width: width,
76
+ height: 1
77
+ )
78
+ end
79
+
80
+ def render_window(window)
81
+ window.render
82
+ mini_buffer.render if mini_buffer
83
+ rescue StandardError => error
84
+ handle_exception(error)
85
+ end
86
+
87
+ def handle_exception(e)
88
+ Buffer.messages << "#{e.message} (#{e.backtrace.first})\n"
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,126 @@
1
+ require 'amun/windows/base'
2
+ require 'amun/buffer'
3
+ require 'amun/windows/text_renderer'
4
+ require 'amun/primitives/rect'
5
+
6
+ module Amun
7
+ module Windows
8
+ # a minibuffer that when attached will replace the frame
9
+ # echo area and display one line with a label (buffer name)
10
+ # and the value typed by the user (buffer content)
11
+ # it will fire 2 events, done and cancel, you can listen
12
+ # on them to do stuff with the input data,
13
+ # also after both events the buffer will be cleared
14
+ # to allow you to reattach the same window again and reuse it,
15
+ # also it allow passing a block to the initializer to execute it
16
+ # when the user press enter (done event) so you can have a fast usage as such
17
+ # MiniBufferWindow.new("Are you sure?[Y/N]", "Y") do |window|
18
+ # exit if window.buffer.to_s.downcase == 'y'
19
+ # end.attach(Amun::Application.frame)
20
+ # that will create a minibuffer and attach it to current active frame
21
+ # also will have a label asking the user "Are you sure?" and a default
22
+ # value "Y" so user can just press enter, or clear it and press any other character
23
+ # when user press enter the block will be executed with the window itself as a parameter
24
+ # so the block will exit amun if the answer to the question is "y"
25
+ class MiniBufferWindow < Base
26
+ attr_reader :buffer
27
+
28
+ def initialize(name, default_value = '', &block)
29
+ super(default_size)
30
+
31
+ @buffer = Buffer.new(name)
32
+ @buffer << default_value
33
+ @buffer.point = default_value.length
34
+ @done_block = block if block_given?
35
+ @text_renderer = TextRenderer.new(text_renderer_size)
36
+
37
+ bind "\e", self, :cancel
38
+ bind "\C-g", self, :cancel
39
+ bind "\n", self, :done
40
+ bind "done", self, :exec_done_block
41
+ end
42
+
43
+ # attach the mini buffer to a frame of your choice,
44
+ # that will make it replace the echo are in this frame
45
+ def attach(frame)
46
+ detach if attached?
47
+ self.size = Rect.new(
48
+ top: frame.top + frame.height - 1,
49
+ left: frame.left,
50
+ width: frame.width,
51
+ height: 1
52
+ )
53
+ @frame = frame
54
+ @frame.mini_buffer = self
55
+ end
56
+
57
+ # deatach the mini buffer from its frame
58
+ def detach
59
+ @frame.mini_buffer = nil
60
+ @frame = nil
61
+ end
62
+
63
+ # is the mini buffer currently attached to any frame?
64
+ def attached?
65
+ @frame && @frame.mini_buffer == self
66
+ end
67
+
68
+ def render
69
+ curses_window.erase
70
+ curses_window << buffer.name
71
+ curses_window.refresh
72
+ @text_renderer.render(buffer)
73
+ end
74
+
75
+ def trigger(event)
76
+ EventManager.join(
77
+ event,
78
+ events,
79
+ buffer
80
+ )
81
+ end
82
+
83
+ def cancel(*)
84
+ detach
85
+ trigger("cancel")
86
+ buffer.clear
87
+ end
88
+
89
+ def done(*)
90
+ detach
91
+ trigger("done")
92
+ buffer.clear
93
+ end
94
+
95
+ def exec_done_block(*)
96
+ return unless @done_block
97
+ @done_block.call(self)
98
+ end
99
+
100
+ private
101
+
102
+ def resize
103
+ super
104
+ @text_renderer.size = text_renderer_size
105
+ end
106
+
107
+ def text_renderer_size
108
+ Rect.new(
109
+ top: top,
110
+ left: left + buffer.name.length,
111
+ width: width - buffer.name.length,
112
+ height: height
113
+ )
114
+ end
115
+
116
+ def default_size
117
+ Rect.new(
118
+ top: 0,
119
+ left: 0,
120
+ width: Curses.stdscr.maxx,
121
+ height: Curses.stdscr.maxy
122
+ )
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,50 @@
1
+ require 'amun/windows/base'
2
+ require 'amun/helpers/colors'
3
+ require 'amun/mode_line_segments/major_mode'
4
+ require 'amun/mode_line_segments/buffer_name'
5
+
6
+ module Amun
7
+ module Windows
8
+ # a line of small segments that display
9
+ # information about the current window,
10
+ # like mode name, line number, buffer name...etc
11
+ class ModeLine < Base
12
+ attr_reader :left_segments, :right_segments
13
+
14
+ def initialize(size)
15
+ super(size)
16
+ @right_segments = []
17
+ @left_segments = [
18
+ ModeLineSegments::BufferName.new,
19
+ ModeLineSegments::MajorMode.new
20
+ ]
21
+
22
+ Helpers::Colors.register_default(:mode_line, 0, 255)
23
+ end
24
+
25
+ def render(buffer)
26
+ right_output = render_segments(right_segments, buffer)
27
+ left_output = render_segments(left_segments, buffer)
28
+ filler = empty_space(right_output, left_output)
29
+
30
+ curses_window.erase
31
+ Helpers::Colors.print(curses_window, *left_output, filler, *right_output)
32
+ curses_window.refresh
33
+ end
34
+
35
+ private
36
+
37
+ def empty_space(right_output, left_output)
38
+ text_size = (right_output + left_output).map(&:size).inject(0, :+)
39
+ empty_space = [0, width - text_size].max
40
+ (' ' * empty_space).colorize(:mode_line)
41
+ end
42
+
43
+ def render_segments(segments, buffer)
44
+ segments.map do |segment|
45
+ segment.render(buffer)
46
+ end.flatten
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,41 @@
1
+ require 'amun/windows/base'
2
+
3
+ module Amun
4
+ module Windows
5
+ # this class renders the buffer in a curses window
6
+ # makes sure the current character under point is highlighted
7
+ # highlight the space between point and mark
8
+ # and make sure to color text and other stuff
9
+ # consider it the rendering engine of the buffer
10
+ class TextRenderer < Base
11
+ def render(buffer)
12
+ curses_window.erase
13
+ curses_window.scrollok(true)
14
+ render_text(buffer)
15
+ curses_window.refresh
16
+ end
17
+
18
+ private
19
+
20
+ def render_text(buffer)
21
+ point = buffer.point
22
+ curses_window << buffer[0...point]
23
+ render_point(buffer)
24
+ curses_window << buffer[(point + 1)..-1]
25
+ end
26
+
27
+ def render_point(buffer)
28
+ curses_window.attron(Helpers::Colors::REVERSE)
29
+ curses_window << case buffer[buffer.point]
30
+ when "\n"
31
+ " \n"
32
+ when nil
33
+ " "
34
+ else
35
+ buffer[buffer.point]
36
+ end
37
+ curses_window.attroff(Helpers::Colors::REVERSE)
38
+ end
39
+ end
40
+ end
41
+ end