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.
- checksums.yaml +5 -5
- data/.rspec +1 -0
- data/.rubocop.yml +6 -1
- data/.ruby-version +1 -1
- data/Guardfile +26 -6
- data/README.md +1 -0
- data/Rakefile +4 -6
- data/_config.yml +1 -0
- data/amun.gemspec +6 -5
- data/exe/amun +1 -1
- data/lib/amun/application.rb +11 -5
- data/lib/amun/behaviours/emacs.rb +14 -122
- data/lib/amun/behaviours/erasing.rb +67 -0
- data/lib/amun/behaviours/insertion.rb +22 -0
- data/lib/amun/behaviours/movement.rb +83 -0
- data/lib/amun/buffer.rb +103 -0
- data/lib/amun/event_manager.rb +12 -3
- data/lib/amun/features/echo_event.rb +4 -1
- data/lib/amun/features/files.rb +26 -0
- data/lib/amun/helpers/colors.rb +8 -7
- data/lib/amun/helpers/keyboard.rb +25 -8
- data/lib/amun/major_modes/fundamental.rb +8 -40
- data/lib/amun/major_modes/irb.rb +40 -0
- data/lib/amun/mode_line_segments/buffer_name.rb +10 -0
- data/lib/amun/mode_line_segments/major_mode.rb +11 -0
- data/lib/amun/object.rb +21 -0
- data/lib/amun/primitives/rect.rb +15 -0
- data/lib/amun/version.rb +1 -1
- data/lib/amun/windows/base.rb +42 -0
- data/lib/amun/windows/buffer_window.rb +73 -0
- data/lib/amun/windows/echo_area.rb +34 -0
- data/lib/amun/windows/frame.rb +92 -0
- data/lib/amun/windows/mini_buffer_window.rb +126 -0
- data/lib/amun/windows/mode_line.rb +50 -0
- data/lib/amun/windows/text_renderer.rb +41 -0
- metadata +56 -31
- data/lib/amun/ui/buffer.rb +0 -90
- data/lib/amun/ui/echo_area.rb +0 -42
- data/lib/amun/ui/mode_line.rb +0 -44
- data/lib/amun/ui/mode_line_segments/buffer_name.rb +0 -16
- data/lib/amun/ui/mode_line_segments/major_mode.rb +0 -17
- data/lib/amun/ui/windows/buffer_window.rb +0 -40
- data/lib/amun/ui/windows/frame.rb +0 -75
data/lib/amun/buffer.rb
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
require 'set'
|
|
2
|
+
require 'amun/object'
|
|
3
|
+
require 'amun/event_manager'
|
|
4
|
+
require 'amun/major_modes/fundamental'
|
|
5
|
+
require 'forwardable'
|
|
6
|
+
|
|
7
|
+
module Amun
|
|
8
|
+
# A buffer could present any kind of IO object (File, StringIO...etc)
|
|
9
|
+
# also it has a major mode responsible update lines and visual lines
|
|
10
|
+
class Buffer < Object
|
|
11
|
+
extend Forwardable
|
|
12
|
+
attr_accessor :name, :io, :major_mode, :minor_modes
|
|
13
|
+
|
|
14
|
+
def initialize(name, io = StringIO.new)
|
|
15
|
+
super()
|
|
16
|
+
self.io = io
|
|
17
|
+
self.name = name
|
|
18
|
+
self.point = 0
|
|
19
|
+
self.text = ''
|
|
20
|
+
self.major_mode = MajorModes::Fundamental.new(self)
|
|
21
|
+
self.minor_modes = Set.new
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
attr_writer :point
|
|
25
|
+
def point
|
|
26
|
+
return 0 if @point.negative?
|
|
27
|
+
return length if @point > length
|
|
28
|
+
@point
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
attr_writer :mark
|
|
32
|
+
def mark
|
|
33
|
+
return nil if @mark.nil?
|
|
34
|
+
return 0 if @mark.negative?
|
|
35
|
+
return length if @mark > length
|
|
36
|
+
@mark
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def trigger(event)
|
|
40
|
+
EventManager.join(
|
|
41
|
+
event,
|
|
42
|
+
*([events] + minor_modes.to_a + [major_mode])
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# buffer should wrap the inner @text
|
|
47
|
+
# and expose any reading method but
|
|
48
|
+
# control the writing methods
|
|
49
|
+
|
|
50
|
+
def_delegators :text,
|
|
51
|
+
:length, :size, :[],
|
|
52
|
+
:count, :index, :rindex,
|
|
53
|
+
:empty?
|
|
54
|
+
|
|
55
|
+
def insert(index, other_str)
|
|
56
|
+
text.insert(index, other_str)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def slice!(start, length = 1)
|
|
60
|
+
text.slice!(start, length)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def <<(p1)
|
|
64
|
+
insert(length, p1)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def clear
|
|
68
|
+
slice!(0, length)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def to_s
|
|
72
|
+
text.dup
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class << self
|
|
76
|
+
attr_writer :current, :instances
|
|
77
|
+
|
|
78
|
+
def instances
|
|
79
|
+
@instances ||= Set.new
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def current
|
|
83
|
+
@current ||= scratch
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def scratch
|
|
87
|
+
@scratch ||= new('*Scratch*')
|
|
88
|
+
instances << @scratch
|
|
89
|
+
@scratch
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def messages
|
|
93
|
+
@messages ||= new('*Messages*')
|
|
94
|
+
instances << @messages
|
|
95
|
+
@messages
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
private
|
|
100
|
+
|
|
101
|
+
attr_accessor :text
|
|
102
|
+
end
|
|
103
|
+
end
|
data/lib/amun/event_manager.rb
CHANGED
|
@@ -44,8 +44,8 @@ module Amun
|
|
|
44
44
|
raise ArgumentError, "#{method} : is not a method for #{object}"
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
-
add_chain(event)
|
|
48
|
-
@bindings[event].unshift(object: object, method: method)
|
|
47
|
+
add_chain(event.to_s)
|
|
48
|
+
@bindings[event.to_s].unshift(object: object, method: method)
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
# remove all occurence of *method* from the *event* stack
|
|
@@ -148,6 +148,15 @@ module Amun
|
|
|
148
148
|
end
|
|
149
149
|
end
|
|
150
150
|
|
|
151
|
+
# clear the globally bound events
|
|
152
|
+
# if you need to reset this class to
|
|
153
|
+
# its default state, this method
|
|
154
|
+
# should clear all events and its associated
|
|
155
|
+
# objects/methods
|
|
156
|
+
def clear
|
|
157
|
+
@instance = nil
|
|
158
|
+
end
|
|
159
|
+
|
|
151
160
|
private
|
|
152
161
|
|
|
153
162
|
def instance
|
|
@@ -172,7 +181,7 @@ module Amun
|
|
|
172
181
|
|
|
173
182
|
def add_chain(event)
|
|
174
183
|
return unless event.to_s.include?(' ')
|
|
175
|
-
event.to_s.split(" ").inject("") do |chain, evt|
|
|
184
|
+
event.to_s.split(" ")[0...-1].inject("") do |chain, evt|
|
|
176
185
|
new_chain = (chain + " " + evt).strip
|
|
177
186
|
@chains << new_chain
|
|
178
187
|
new_chain
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
require 'amun/event_manager'
|
|
2
|
+
require 'amun/buffer'
|
|
2
3
|
|
|
3
4
|
def log(event)
|
|
4
|
-
|
|
5
|
+
# log valid strings only no control characters
|
|
6
|
+
event = event.encode!('UTF-8', 'UTF-8', invalid: :replace)
|
|
7
|
+
Amun::Buffer.messages << "#{event}\n"
|
|
5
8
|
end
|
|
6
9
|
Amun::EventManager.bind_all nil, :log
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'amun/event_manager'
|
|
2
|
+
require 'amun/buffer'
|
|
3
|
+
require 'amun/windows/mini_buffer_window'
|
|
4
|
+
|
|
5
|
+
def find_file(*)
|
|
6
|
+
Amun::Windows::MiniBufferWindow.new('Open file: ', Dir.pwd) do |window|
|
|
7
|
+
file_path = window.buffer.to_s
|
|
8
|
+
|
|
9
|
+
file_buffer = Amun::Buffer.new(file_path, File.open(file_path, 'r+'))
|
|
10
|
+
Amun::Buffer.instances << file_buffer
|
|
11
|
+
Amun::Buffer.current = file_buffer
|
|
12
|
+
end.attach(Amun::Application.frame)
|
|
13
|
+
|
|
14
|
+
true
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
Amun::EventManager.bind "\C-x \C-f", nil, :find_file
|
|
18
|
+
|
|
19
|
+
unless ARGV.empty?
|
|
20
|
+
ARGV.each do |file|
|
|
21
|
+
file_buffer = Amun::Buffer.new(file, File.open(file, 'r+'))
|
|
22
|
+
Amun::Buffer.instances << file_buffer
|
|
23
|
+
Amun::Buffer.current = file_buffer
|
|
24
|
+
Amun::Application.frame.render
|
|
25
|
+
end
|
|
26
|
+
end
|
data/lib/amun/helpers/colors.rb
CHANGED
|
@@ -2,6 +2,7 @@ require 'curses'
|
|
|
2
2
|
|
|
3
3
|
module Amun
|
|
4
4
|
module Helpers
|
|
5
|
+
class ColorLimitExceeded < StandardError; end
|
|
5
6
|
##
|
|
6
7
|
# Colors is responsible for registering new colors
|
|
7
8
|
# pairs (foreground and background)
|
|
@@ -52,8 +53,8 @@ module Amun
|
|
|
52
53
|
# foreground(Number):: foreground color in current terminal color schema
|
|
53
54
|
# background(Number):: background color in current terminal color schema
|
|
54
55
|
def register(name, foreground, background)
|
|
55
|
-
if COLORS.size >= Curses.color_pairs - 1
|
|
56
|
-
raise "Can't register color: #{name}, max: #{Curses.color_pairs}"
|
|
56
|
+
if !COLORS.key?(name) && COLORS.size >= Curses.color_pairs - 1
|
|
57
|
+
raise ColorLimitExceeded, "Can't register color: #{name}, max: #{Curses.color_pairs}"
|
|
57
58
|
end
|
|
58
59
|
|
|
59
60
|
Curses.init_pair(COLORS[name], foreground, background)
|
|
@@ -79,16 +80,16 @@ module Amun
|
|
|
79
80
|
# type(Colors::Constant):: a text style constant
|
|
80
81
|
# defined in Colors, that manipulate the text style
|
|
81
82
|
# (Bold, Underline, Invert colors)
|
|
82
|
-
def use(
|
|
83
|
+
def use(curses_window, name, type = NORMAL)
|
|
83
84
|
index = COLORS.key?(name) ? COLORS[name] : 0
|
|
84
|
-
|
|
85
|
+
curses_window.attron(Curses.color_pair(index) | type)
|
|
85
86
|
end
|
|
86
87
|
|
|
87
88
|
# print string in Curses window in the choosen color and style
|
|
88
|
-
def print(
|
|
89
|
+
def print(curses_window, *strings)
|
|
89
90
|
strings.each do |string|
|
|
90
|
-
use(
|
|
91
|
-
|
|
91
|
+
use(curses_window, string.color || DEFAULT_COLOR, string.style || NORMAL)
|
|
92
|
+
curses_window << string
|
|
92
93
|
end
|
|
93
94
|
end
|
|
94
95
|
end
|
|
@@ -2,27 +2,44 @@ require 'curses'
|
|
|
2
2
|
|
|
3
3
|
module Amun
|
|
4
4
|
module Helpers
|
|
5
|
+
# a module to deal with the keyboard
|
|
6
|
+
# a complementary module to curses
|
|
7
|
+
# doesn't intend to replace it
|
|
8
|
+
# it was created to overcome the shorcoming
|
|
9
|
+
# of getting a character + meta from the keyboard
|
|
10
|
+
# in the first place.
|
|
5
11
|
module Keyboard
|
|
6
12
|
module_function
|
|
7
13
|
|
|
8
14
|
TIMEOUT = 100
|
|
9
15
|
|
|
16
|
+
# get a character from the keyboard
|
|
17
|
+
# and make sure you detect the meta combination
|
|
10
18
|
def char
|
|
11
19
|
ch = Curses.stdscr.get_char
|
|
12
|
-
|
|
13
|
-
modified_char = Curses.stdscr.get_char if ch == "\e"
|
|
14
|
-
Curses.stdscr.timeout = -1
|
|
20
|
+
modified_character = modified_char if ch == "\e"
|
|
15
21
|
|
|
16
|
-
return ch if
|
|
17
|
-
return "#{ch} #{
|
|
18
|
-
return "#{ch} #{
|
|
22
|
+
return ch.to_s if modified_character.nil?
|
|
23
|
+
return "#{ch} #{modified_character}" if modified_character.is_a? Numeric
|
|
24
|
+
return "#{ch} #{modified_character}" if modified_character.length > 1
|
|
19
25
|
|
|
20
26
|
begin
|
|
21
|
-
eval "?\\M-#{
|
|
27
|
+
eval "?\\M-#{modified_character}"
|
|
22
28
|
rescue SyntaxError
|
|
23
|
-
return "#{ch} #{
|
|
29
|
+
return "#{ch} #{modified_character}"
|
|
24
30
|
end
|
|
25
31
|
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
module_function
|
|
36
|
+
|
|
37
|
+
def modified_char
|
|
38
|
+
Curses.stdscr.timeout = TIMEOUT
|
|
39
|
+
char = Curses.stdscr.get_char
|
|
40
|
+
Curses.stdscr.timeout = -1
|
|
41
|
+
char
|
|
42
|
+
end
|
|
26
43
|
end
|
|
27
44
|
end
|
|
28
45
|
end
|
|
@@ -1,51 +1,19 @@
|
|
|
1
|
+
require 'amun/object'
|
|
1
2
|
require 'amun/helpers/colors'
|
|
2
3
|
require 'amun/behaviours/emacs'
|
|
3
4
|
|
|
4
5
|
module Amun
|
|
5
6
|
module MajorModes
|
|
6
|
-
# Basic mode
|
|
7
|
-
class Fundamental
|
|
7
|
+
# Basic mode with emacs defaults
|
|
8
|
+
class Fundamental < Object
|
|
8
9
|
include Behaviours::Emacs
|
|
9
10
|
|
|
10
11
|
def initialize(buffer)
|
|
11
|
-
|
|
12
|
+
super()
|
|
13
|
+
self.buffer = buffer
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
emacs_behaviour_initialize(@events)
|
|
16
|
-
|
|
17
|
-
read_io if buffer.text.nil?
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def trigger(event)
|
|
21
|
-
EventManager.join(event, @events)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def render(window)
|
|
25
|
-
window.clear
|
|
26
|
-
point = buffer.point
|
|
27
|
-
|
|
28
|
-
window << buffer.text[0...point]
|
|
29
|
-
window.attron(Helpers::Colors::REVERSE)
|
|
30
|
-
|
|
31
|
-
at_point = buffer.text[point]
|
|
32
|
-
window << (at_point == "\n" || at_point.nil? ? " \n" : at_point)
|
|
33
|
-
window.attroff(Helpers::Colors::REVERSE)
|
|
34
|
-
window << buffer.text[(point + 1)..-1]
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def event_handler(event)
|
|
38
|
-
return true unless event.is_a? String
|
|
39
|
-
return true unless event.length == 1
|
|
40
|
-
return true unless event.valid_encoding?
|
|
41
|
-
|
|
42
|
-
case event
|
|
43
|
-
when /[^[:print:]\n\t]/
|
|
44
|
-
true
|
|
45
|
-
else
|
|
46
|
-
buffer.text.insert(buffer.point, event)
|
|
47
|
-
buffer.point += 1
|
|
48
|
-
end
|
|
15
|
+
emacs_behaviour_initialize
|
|
16
|
+
read_io if buffer.empty?
|
|
49
17
|
end
|
|
50
18
|
|
|
51
19
|
private
|
|
@@ -53,7 +21,7 @@ module Amun
|
|
|
53
21
|
attr_accessor :buffer
|
|
54
22
|
|
|
55
23
|
def read_io
|
|
56
|
-
buffer
|
|
24
|
+
buffer << buffer.io.read
|
|
57
25
|
end
|
|
58
26
|
end
|
|
59
27
|
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require 'amun/object'
|
|
2
|
+
require 'amun/helpers/colors'
|
|
3
|
+
require 'amun/behaviours/emacs'
|
|
4
|
+
|
|
5
|
+
module Amun
|
|
6
|
+
module MajorModes
|
|
7
|
+
# mode that executes the last line in
|
|
8
|
+
# the current environment and print the output
|
|
9
|
+
class IRB < Object
|
|
10
|
+
include Behaviours::Emacs
|
|
11
|
+
|
|
12
|
+
def initialize(buffer)
|
|
13
|
+
super()
|
|
14
|
+
self.buffer = buffer
|
|
15
|
+
|
|
16
|
+
emacs_behaviour_initialize
|
|
17
|
+
bind "\n", self, :execute_last_line
|
|
18
|
+
read_io if buffer.empty?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def execute_last_line(*)
|
|
22
|
+
last_line = buffer.lines.last
|
|
23
|
+
result = eval(last_line)
|
|
24
|
+
buffer << "\n#{result}"
|
|
25
|
+
rescue StandardError, SyntaxError => error
|
|
26
|
+
buffer << "\n#{error.inspect}\n#{error.backtrace}"
|
|
27
|
+
ensure
|
|
28
|
+
buffer.point = buffer.length
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
attr_accessor :buffer
|
|
34
|
+
|
|
35
|
+
def read_io
|
|
36
|
+
buffer << buffer.io.read
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
data/lib/amun/object.rb
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require 'amun/event_manager'
|
|
2
|
+
require 'forwardable'
|
|
3
|
+
|
|
4
|
+
module Amun
|
|
5
|
+
# an object wrapping event manager inside it
|
|
6
|
+
# and exposing every method to the public
|
|
7
|
+
# this way you can have this object and switch
|
|
8
|
+
# behavior and states by switching internal event manager
|
|
9
|
+
# instances, for example you can have a mode that switch between
|
|
10
|
+
# normal and insert mode (ahem ahmed VIM style)
|
|
11
|
+
class Object
|
|
12
|
+
extend Forwardable
|
|
13
|
+
|
|
14
|
+
attr_accessor :events
|
|
15
|
+
def_delegators :events, :bind, :bind_all, :unbind, :unbind_all, :trigger
|
|
16
|
+
|
|
17
|
+
def initialize
|
|
18
|
+
@events = EventManager.new
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# a primitive class that incapsulate
|
|
2
|
+
# a rectangle on screen, with position
|
|
3
|
+
# (top, left) and size (width, height)
|
|
4
|
+
class Rect
|
|
5
|
+
attr_reader :top, :left, :width, :height
|
|
6
|
+
|
|
7
|
+
# create a rectangle with top, left, width and height
|
|
8
|
+
# opts(Hash):: a hash with keys (top, left, width, height)
|
|
9
|
+
def initialize(opts = {})
|
|
10
|
+
@top = opts[:top]
|
|
11
|
+
@left = opts[:left]
|
|
12
|
+
@width = opts[:width]
|
|
13
|
+
@height = opts[:height]
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/amun/version.rb
CHANGED