amun 0.1.2 → 0.1.3
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 +4 -4
- data/.rubocop.yml +11 -0
- data/.travis.yml +5 -2
- data/README.md +40 -3
- data/Rakefile +1 -1
- data/amun.gemspec +2 -0
- data/lib/amun/application.rb +51 -0
- data/lib/amun/behaviours/emacs.rb +131 -0
- data/lib/amun/event_manager.rb +111 -6
- data/lib/amun/features/echo_event.rb +6 -0
- data/lib/amun/features/quit.rb +6 -0
- data/lib/amun/features_loader.rb +14 -0
- data/lib/amun/helpers/colors.rb +53 -9
- data/lib/amun/helpers/keyboard.rb +28 -0
- data/lib/amun/major_modes/fundamental.rb +60 -0
- data/lib/amun/ui/buffer.rb +90 -0
- data/lib/amun/ui/echo_area.rb +26 -10
- data/lib/amun/ui/mode_line.rb +44 -0
- data/lib/amun/ui/mode_line_segments/buffer_name.rb +16 -0
- data/lib/amun/ui/mode_line_segments/major_mode.rb +17 -0
- data/lib/amun/ui/windows/buffer_window.rb +40 -0
- data/lib/amun/ui/windows/frame.rb +75 -0
- data/lib/amun/version.rb +1 -1
- data/lib/amun.rb +2 -61
- metadata +44 -3
- data/bin/setup +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 10726ce656cbf6445cbee3177cda26541982d3b7
|
4
|
+
data.tar.gz: 0da415d031b8e63ff1fb5fb4b92537117ed1ad4f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6eef223a7d0a123259da8edea864b7a701b8d8d048fdef6ff0dc1002a187b4008c4ed0b967e1db95fb5345156c611063ef4525e67550efbe7da2017c925d486
|
7
|
+
data.tar.gz: b475175d8acf045f52ac748be2243c7b46d1a4576334a768d671a42295bcf899f8cdd0e8bbf12152135b1bf22ce7537f8fb9ac38b8481a75be651a2e63f88d7b
|
data/.rubocop.yml
ADDED
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,25 +1,62 @@
|
|
1
1
|
# Amun (Work in progress)
|
2
2
|
|
3
|
+
## "King of the gods and god of the wind"
|
4
|
+
[](http://badge.fury.io/rb/amun)
|
5
|
+
[](https://travis-ci.org/blazeeboy/amun)
|
6
|
+
[](https://codeclimate.com/github/blazeeboy/amun)
|
7
|
+
[](https://codeclimate.com/github/blazeeboy/amun)
|
8
|
+
|
3
9
|
A minimal CLI text editor, built on Ruby, looking for Emacs as it's father and idol.
|
4
10
|
|
5
11
|
As developing packages for Emacs with Elisp wasn't always a fun or easy task, Starting a project that leverage ruby ability for fast development will be a good move towards
|
6
12
|
an open, easy to extend editor.
|
7
13
|
|
8
|
-
When I started this project I had 2 options, taking the VIM way or emacs way, looking in the current state of the two editors, It's obvious that emacs
|
14
|
+
When I started this project I had 2 options, taking the VIM way or emacs way, looking in the current state of the two editors, It's obvious that emacs approach has a better
|
9
15
|
extensibility over VIM, emacs customizability is far superior to VIM, so building this project as a minimal and emacs-like would open the door for vim users to have their own
|
10
16
|
bindings as a package like emacs Evil mode, but doing the other way around won't help emacs users.
|
11
17
|
|
18
|
+
## Advantages of building an editor in ruby
|
19
|
+
|
20
|
+
* We can use ruby gems as package management
|
21
|
+
* we already have bundler to fix dependencies, upgrade, downgrade gems (plugins in this case), you can even add sources for gems or get a gem from github or company inhouse gems.
|
22
|
+
* you can reflect on the runtime and autocomplete commands
|
23
|
+
* plugins can mutate all parts of the runtime application classes/objects included
|
24
|
+
* ruby is easy to learn so it'll be easier to build gems that is specifically for this editor
|
25
|
+
* lots of gems already exists and could be loaded into the editor environment
|
26
|
+
* you can use it locally or remotly as it's terminal based
|
27
|
+
* documentation included, rdoc is already there to be used
|
28
|
+
|
12
29
|
## Installation
|
13
30
|
|
14
31
|
$ gem install amun
|
15
32
|
|
16
33
|
## Usage
|
17
34
|
|
18
|
-
amun install an executable to your path, so executing `amun` from your
|
35
|
+
amun install an executable to your path, so executing `amun` from your command-line should launch amun
|
36
|
+
|
37
|
+
## Structure
|
38
|
+
|
39
|
+
|
40
|
+
### Helpers
|
41
|
+
|
42
|
+
Helpers are modules that any class can use to do side tasks, think of it like Ruby on rails helpers.
|
43
|
+
|
44
|
+
* Only modules no classes
|
45
|
+
* doesn't depend on each other
|
46
|
+
* depends on the project dependencies only like "Curses"
|
47
|
+
|
48
|
+
### MajorModes
|
49
|
+
|
50
|
+
Classes that are responsible the following for a buffer object:
|
51
|
+
|
52
|
+
* event handling
|
53
|
+
* manipulating IO
|
54
|
+
* Rendering IO into a curses window
|
55
|
+
|
19
56
|
|
20
57
|
## Development
|
21
58
|
|
22
|
-
After checking out the repo, run `
|
59
|
+
After checking out the repo, run `bundle install` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
23
60
|
|
24
61
|
## Contributing
|
25
62
|
|
data/Rakefile
CHANGED
data/amun.gemspec
CHANGED
@@ -28,4 +28,6 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.add_development_dependency "pry"
|
29
29
|
spec.add_development_dependency "guard"
|
30
30
|
spec.add_development_dependency "guard-minitest"
|
31
|
+
spec.add_development_dependency "simplecov"
|
32
|
+
spec.add_development_dependency "codeclimate-test-reporter", "~> 1.0.0"
|
31
33
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'curses'
|
2
|
+
require 'singleton'
|
3
|
+
require 'amun/ui/windows/frame'
|
4
|
+
require 'amun/event_manager'
|
5
|
+
require 'amun/features_loader'
|
6
|
+
require 'amun/helpers/keyboard'
|
7
|
+
|
8
|
+
module Amun
|
9
|
+
# singleton Amun application, it initialize curses,
|
10
|
+
# have the frame and handles keyboard
|
11
|
+
class Application
|
12
|
+
include Singleton
|
13
|
+
|
14
|
+
attr_writer :frame
|
15
|
+
|
16
|
+
def frame
|
17
|
+
@frame ||= UI::Windows::Frame.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def run
|
21
|
+
init_curses
|
22
|
+
frame.render
|
23
|
+
FeaturesLoader.load
|
24
|
+
keyboard_thread.join
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def init_curses
|
30
|
+
Curses.init_screen
|
31
|
+
Curses.curs_set 0
|
32
|
+
Curses.raw
|
33
|
+
Curses.noecho
|
34
|
+
Curses.start_color
|
35
|
+
Curses.stdscr.keypad = true
|
36
|
+
Curses.ESCDELAY = 0
|
37
|
+
end
|
38
|
+
|
39
|
+
def keyboard_thread
|
40
|
+
Thread.new do
|
41
|
+
chain = []
|
42
|
+
while (ch = Helpers::Keyboard.char)
|
43
|
+
chain << ch
|
44
|
+
if EventManager.join(chain.join(' '), frame) != EventManager::CHAINED
|
45
|
+
chain.clear
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'amun/ui/buffer'
|
2
|
+
require 'curses'
|
3
|
+
|
4
|
+
module Amun
|
5
|
+
module Behaviours
|
6
|
+
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
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
data/lib/amun/event_manager.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'set'
|
3
|
+
|
1
4
|
module Amun
|
2
5
|
# = Event manager
|
3
6
|
# Stores a stack of methods to call for each event,
|
@@ -17,20 +20,31 @@ module Amun
|
|
17
20
|
# when the *Buffer* module saves a file it should trigger
|
18
21
|
# that event, which executed the *update_title*.
|
19
22
|
class EventManager
|
23
|
+
# Event is interrupted and no need to continue
|
24
|
+
INTERRUPTED = :interrupted
|
25
|
+
# Event is to be continued
|
26
|
+
CONTINUE = :continue
|
27
|
+
# Event needs to be chained, will wait for next event
|
28
|
+
CHAINED = :chained
|
29
|
+
|
20
30
|
def initialize
|
21
31
|
@bindings = Hash.new { |h, k| h[k] = [] }
|
32
|
+
@chains = Set.new
|
22
33
|
end
|
23
34
|
|
24
|
-
# register an *objects*' *method* to be executed
|
35
|
+
# register an *objects*' *method* to be executed
|
36
|
+
# when the *event* is triggered
|
25
37
|
#
|
26
38
|
# event(String):: and event to bind the method to
|
27
39
|
# object(Object):: an object or class, that respond to +method+
|
28
|
-
# method(Symbol):: a method name that should be executed when the event
|
40
|
+
# method(Symbol):: a method name that should be executed when the event
|
41
|
+
# is triggered
|
29
42
|
def bind(event, object, method)
|
30
|
-
|
43
|
+
if !object.nil? && !object.respond_to?(method)
|
31
44
|
raise ArgumentError, "#{method} : is not a method for #{object}"
|
32
45
|
end
|
33
46
|
|
47
|
+
add_chain(event)
|
34
48
|
@bindings[event].unshift(object: object, method: method)
|
35
49
|
end
|
36
50
|
|
@@ -43,6 +57,8 @@ module Amun
|
|
43
57
|
@bindings[event].delete_if do |binding|
|
44
58
|
binding[:object] == object && binding[:method] == method
|
45
59
|
end
|
60
|
+
@bindings.delete(event) if @bindings[event].empty?
|
61
|
+
update_chains
|
46
62
|
end
|
47
63
|
|
48
64
|
# bind an object method to be executed when any event is triggered
|
@@ -50,7 +66,7 @@ module Amun
|
|
50
66
|
# be executed after the event specific stack is executed and didn't
|
51
67
|
# stop execution
|
52
68
|
def bind_all(object, method)
|
53
|
-
bind
|
69
|
+
bind "all", object, method
|
54
70
|
end
|
55
71
|
|
56
72
|
# remove the *method* from executing after every event,
|
@@ -58,7 +74,7 @@ module Amun
|
|
58
74
|
#
|
59
75
|
# this won't remove the method if it was registered with #bind
|
60
76
|
def unbind_all(object, method)
|
61
|
-
unbind
|
77
|
+
unbind "all", object, method
|
62
78
|
end
|
63
79
|
|
64
80
|
# execute *event* stack of methods in Last-In-First-Out,
|
@@ -66,8 +82,77 @@ module Amun
|
|
66
82
|
# to be executed after all events with #bind_all method
|
67
83
|
# if any method in this chain returned false, it will stop the rest
|
68
84
|
# of the stack.
|
85
|
+
#
|
86
|
+
# a chained event is an event that contain a space
|
87
|
+
# between 2 or more events lie "\C-c \C-x" so this event
|
88
|
+
# should be executed when these 2 events occure after
|
89
|
+
# each other,
|
90
|
+
# if you tried to trigger the first part \C-c the
|
91
|
+
# trigger method will return CHAINED, assuming that \C-c
|
92
|
+
# is not handled and also if handled the handle execution
|
93
|
+
# didn't return false to interrupt the execution
|
69
94
|
def trigger(event)
|
70
|
-
trigger_for_event(event, event) &&
|
95
|
+
return INTERRUPTED unless trigger_for_event(event, event) &&
|
96
|
+
trigger_for_event("all", event)
|
97
|
+
return CHAINED if chained?(event)
|
98
|
+
CONTINUE
|
99
|
+
end
|
100
|
+
|
101
|
+
# class will have the same methods as the EventManager
|
102
|
+
# instance, so you can attack events and trigger it globally
|
103
|
+
# like if you have a global instance if EventManager and
|
104
|
+
# you're attaching and triggering events from it instead
|
105
|
+
# if talking to a specific instance, this global trigger
|
106
|
+
# will be handled by one of the editor toplevel components
|
107
|
+
# and it should be the lowest priority, if every thing else
|
108
|
+
# can't handle the event it should ask this class if anyone
|
109
|
+
# registered something globally to handle this event.
|
110
|
+
#
|
111
|
+
# if you want to handle events in your major mode, minor modes
|
112
|
+
# or anything that the application will ask it to trigger event
|
113
|
+
# you need to instanciate an object, if you need your action to
|
114
|
+
# the default and should be handled globally, like exiting the application
|
115
|
+
# or changing theme, or changing some global variable, update packages
|
116
|
+
# or any similar global actions, then using the class methods will
|
117
|
+
# make sense here.
|
118
|
+
class << self
|
119
|
+
extend Forwardable
|
120
|
+
|
121
|
+
def_delegators :instance, :bind, :unbind, :bind_all, :unbind_all, :trigger
|
122
|
+
|
123
|
+
# trigger an array of event_managers with an event
|
124
|
+
# and return one of the 3 statuses (INTERRUPTED, CHAINED, CONTINUE)
|
125
|
+
#
|
126
|
+
# if any manager returned false or INTERRUPTED will
|
127
|
+
# not execute further and return INTERRUPTED,
|
128
|
+
#
|
129
|
+
# if it faced an event that wants to chain the event
|
130
|
+
# it will return CHAINED if all next managers returned
|
131
|
+
# CONTINUE or also CHAINED
|
132
|
+
#
|
133
|
+
# will return CONTINUE if all manangers returned continue
|
134
|
+
#
|
135
|
+
# event(Symbol):: an event to trigger on all provided managers
|
136
|
+
# *event_managers(*Array):: you can pass as many event managers as
|
137
|
+
# you like as parameters to this method, it will be triggered in order
|
138
|
+
def join(event, *event_managers)
|
139
|
+
event_managers.inject(CONTINUE) do |result, manager|
|
140
|
+
case manager.trigger(event)
|
141
|
+
when INTERRUPTED, false
|
142
|
+
break INTERRUPTED
|
143
|
+
when CHAINED
|
144
|
+
CHAINED
|
145
|
+
else
|
146
|
+
result
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
|
153
|
+
def instance
|
154
|
+
@instance ||= new
|
155
|
+
end
|
71
156
|
end
|
72
157
|
|
73
158
|
private
|
@@ -77,5 +162,25 @@ module Amun
|
|
77
162
|
binding[:object].send binding[:method], event
|
78
163
|
end
|
79
164
|
end
|
165
|
+
|
166
|
+
def update_chains
|
167
|
+
@chains = Set.new
|
168
|
+
@bindings.each do |event|
|
169
|
+
add_chain(event)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def add_chain(event)
|
174
|
+
return unless event.to_s.include?(' ')
|
175
|
+
event.to_s.split(" ").inject("") do |chain, evt|
|
176
|
+
new_chain = (chain + " " + evt).strip
|
177
|
+
@chains << new_chain
|
178
|
+
new_chain
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def chained?(event)
|
183
|
+
@chains.include? event
|
184
|
+
end
|
80
185
|
end
|
81
186
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Amun
|
2
|
+
# load all files in the features directory
|
3
|
+
module FeaturesLoader
|
4
|
+
module_function
|
5
|
+
|
6
|
+
def load
|
7
|
+
path = File.expand_path('../features', __FILE__)
|
8
|
+
features = Dir.glob(File.join(path, '**/*'))
|
9
|
+
features.each do |feature|
|
10
|
+
require feature
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/amun/helpers/colors.rb
CHANGED
@@ -3,13 +3,18 @@ require 'curses'
|
|
3
3
|
module Amun
|
4
4
|
module Helpers
|
5
5
|
##
|
6
|
-
# Colors is responsible for registering new colors
|
7
|
-
#
|
8
|
-
#
|
6
|
+
# Colors is responsible for registering new colors
|
7
|
+
# pairs (foreground and background)
|
8
|
+
# associate the pair with a name, then #use that pair
|
9
|
+
# for the next text printed
|
10
|
+
# on screen, also contains styles constants could be
|
11
|
+
# passed to #use to print bold
|
9
12
|
# or underline text for example
|
10
13
|
#
|
11
|
-
# it's important to understand that the number of pairs
|
12
|
-
#
|
14
|
+
# it's important to understand that the number of pairs
|
15
|
+
# that coul'd be registered
|
16
|
+
# is limited to a number the current terminal specify,
|
17
|
+
# so registering, more color
|
13
18
|
# pairs than allowed will result in a RuntimeError.
|
14
19
|
#
|
15
20
|
# color values could be taken from the xterm-256 color schema from here:
|
@@ -37,6 +42,7 @@ module Amun
|
|
37
42
|
RIGHT = Curses::A_RIGHT
|
38
43
|
|
39
44
|
COLORS = Hash.new { |h, k| h[k] = h.length + 1 }
|
45
|
+
DEFAULT_COLOR = :default
|
40
46
|
private_constant :COLORS
|
41
47
|
|
42
48
|
# register or update a color pair with *foreground* and *background*
|
@@ -47,19 +53,57 @@ module Amun
|
|
47
53
|
# background(Number):: background color in current terminal color schema
|
48
54
|
def register(name, foreground, background)
|
49
55
|
if COLORS.size >= Curses.color_pairs - 1
|
50
|
-
raise "Can't register color #{name}
|
56
|
+
raise "Can't register color: #{name}, max: #{Curses.color_pairs}"
|
51
57
|
end
|
52
58
|
|
53
59
|
Curses.init_pair(COLORS[name], foreground, background)
|
54
60
|
end
|
55
61
|
|
62
|
+
# check if a color pair is registered or not, returns true if registered
|
63
|
+
def registered?(name)
|
64
|
+
COLORS.key? name
|
65
|
+
end
|
66
|
+
|
67
|
+
# works like #register but doesn't override your color if it's already
|
68
|
+
# registered, this is a better method for any
|
69
|
+
# module to use to define colors
|
70
|
+
# #register should be used for redefining a color pair values
|
71
|
+
def register_default(name, foreground, background)
|
72
|
+
return if registered? name
|
73
|
+
register(name, foreground, background)
|
74
|
+
end
|
75
|
+
|
56
76
|
# use color pair for the next printed text
|
77
|
+
# window(Curses Window):: that we need to change it's colors
|
57
78
|
# name(Symbol):: a color pair name registered before with #register
|
58
|
-
# type(Colors::Constant):: a text style constant
|
59
|
-
|
79
|
+
# type(Colors::Constant):: a text style constant
|
80
|
+
# defined in Colors, that manipulate the text style
|
81
|
+
# (Bold, Underline, Invert colors)
|
82
|
+
def use(window, name, type = NORMAL)
|
60
83
|
index = COLORS.key?(name) ? COLORS[name] : 0
|
61
|
-
|
84
|
+
window.attron(Curses.color_pair(index) | type)
|
85
|
+
end
|
86
|
+
|
87
|
+
# print string in Curses window in the choosen color and style
|
88
|
+
def print(window, *strings)
|
89
|
+
strings.each do |string|
|
90
|
+
use(window, string.color || DEFAULT_COLOR, string.style || NORMAL)
|
91
|
+
window << string
|
92
|
+
end
|
62
93
|
end
|
63
94
|
end
|
64
95
|
end
|
65
96
|
end
|
97
|
+
|
98
|
+
# for easier printing of colored string on the screen
|
99
|
+
# we can add color property to any string and then
|
100
|
+
# use it to print the string on the screen
|
101
|
+
class String
|
102
|
+
attr_accessor :color, :style
|
103
|
+
|
104
|
+
def colorize(color, style = Amun::Helpers::Colors::NORMAL)
|
105
|
+
self.color = color
|
106
|
+
self.style = style
|
107
|
+
self
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'curses'
|
2
|
+
|
3
|
+
module Amun
|
4
|
+
module Helpers
|
5
|
+
module Keyboard
|
6
|
+
module_function
|
7
|
+
|
8
|
+
TIMEOUT = 100
|
9
|
+
|
10
|
+
def char
|
11
|
+
ch = Curses.stdscr.get_char
|
12
|
+
Curses.stdscr.timeout = TIMEOUT
|
13
|
+
modified_char = Curses.stdscr.get_char if ch == "\e"
|
14
|
+
Curses.stdscr.timeout = -1
|
15
|
+
|
16
|
+
return ch if modified_char.nil?
|
17
|
+
return "#{ch} #{modified_char}" if modified_char.is_a? Numeric
|
18
|
+
return "#{ch} #{modified_char}" if modified_char.size > 1
|
19
|
+
|
20
|
+
begin
|
21
|
+
eval "?\\M-#{modified_char}"
|
22
|
+
rescue SyntaxError
|
23
|
+
return "#{ch} #{modified_char}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'amun/helpers/colors'
|
2
|
+
require 'amun/behaviours/emacs'
|
3
|
+
|
4
|
+
module Amun
|
5
|
+
module MajorModes
|
6
|
+
# Basic mode that show any IO
|
7
|
+
class Fundamental
|
8
|
+
include Behaviours::Emacs
|
9
|
+
|
10
|
+
def initialize(buffer)
|
11
|
+
@buffer = buffer
|
12
|
+
|
13
|
+
@events = EventManager.new
|
14
|
+
@events.bind_all self, :event_handler
|
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
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
attr_accessor :buffer
|
54
|
+
|
55
|
+
def read_io
|
56
|
+
buffer.text = buffer.io.read
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'amun/event_manager'
|
3
|
+
require 'amun/major_modes/fundamental'
|
4
|
+
require 'amun/ui/mode_line'
|
5
|
+
|
6
|
+
module Amun
|
7
|
+
module UI
|
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
|
11
|
+
attr_accessor :name, :io, :text
|
12
|
+
attr_writer :major_mode, :minor_modes, :mode_line, :point, :mark
|
13
|
+
|
14
|
+
def initialize(name, io = StringIO.new)
|
15
|
+
self.io = io
|
16
|
+
self.name = name
|
17
|
+
self.point = 0
|
18
|
+
end
|
19
|
+
|
20
|
+
def major_mode
|
21
|
+
@major_mode ||= MajorModes::Fundamental.new(self)
|
22
|
+
end
|
23
|
+
|
24
|
+
def mode_line
|
25
|
+
@mode_line ||= UI::ModeLine.new(self)
|
26
|
+
end
|
27
|
+
|
28
|
+
def minor_modes
|
29
|
+
@minor_modes ||= Set.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def point
|
33
|
+
return 0 if @point < 0
|
34
|
+
max = text.size
|
35
|
+
return max if @point > max
|
36
|
+
@point
|
37
|
+
end
|
38
|
+
|
39
|
+
def mark
|
40
|
+
return 0 if @mark < 0
|
41
|
+
max = text.size
|
42
|
+
return max if @mark > max
|
43
|
+
@mark
|
44
|
+
end
|
45
|
+
|
46
|
+
def trigger(event)
|
47
|
+
EventManager.join(
|
48
|
+
event,
|
49
|
+
*(minor_modes + [major_mode])
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
def render(window)
|
54
|
+
major_mode_window = window.subwin(window.maxy - 1, window.maxx, 0, 0)
|
55
|
+
mode_line_window = window.subwin(1, window.maxx, window.maxy - 1, 0)
|
56
|
+
|
57
|
+
major_mode.render(major_mode_window)
|
58
|
+
mode_line.render(mode_line_window)
|
59
|
+
ensure
|
60
|
+
major_mode_window.close
|
61
|
+
mode_line_window.close
|
62
|
+
end
|
63
|
+
|
64
|
+
class << self
|
65
|
+
attr_writer :current, :instances
|
66
|
+
|
67
|
+
def instances
|
68
|
+
@instances ||= Set.new
|
69
|
+
end
|
70
|
+
|
71
|
+
def current
|
72
|
+
@current ||= scratch
|
73
|
+
end
|
74
|
+
|
75
|
+
def scratch
|
76
|
+
@scratch ||= new('*Scratch*')
|
77
|
+
instances << @scratch
|
78
|
+
@scratch
|
79
|
+
end
|
80
|
+
|
81
|
+
def messages
|
82
|
+
@messages ||= new('*Messages*')
|
83
|
+
instances << @messages
|
84
|
+
@messages.text = '' if @messages.text.nil?
|
85
|
+
@messages
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/amun/ui/echo_area.rb
CHANGED
@@ -1,25 +1,41 @@
|
|
1
|
-
require '
|
1
|
+
require 'amun/ui/buffer'
|
2
2
|
|
3
3
|
module Amun
|
4
4
|
module UI
|
5
5
|
# a line that is rendered by default at the end on the screen
|
6
6
|
# takes one line height and extends to take the whole width of screen
|
7
|
-
# should be linked to \*messages\* memory buffer and
|
8
|
-
#
|
7
|
+
# should be linked to \*messages\* memory buffer and display new messages
|
8
|
+
# in the buffer text
|
9
9
|
class EchoArea
|
10
|
+
attr_writer :events
|
11
|
+
|
12
|
+
def events
|
13
|
+
@events ||= EventManager.new
|
14
|
+
end
|
15
|
+
|
10
16
|
def initialize
|
11
|
-
@
|
17
|
+
@last_messages_size = 0
|
12
18
|
end
|
13
19
|
|
14
|
-
|
15
|
-
|
16
|
-
@window << message
|
20
|
+
def trigger(event)
|
21
|
+
EventManager.join(event, events)
|
17
22
|
end
|
18
23
|
|
19
24
|
# render the echo area window
|
20
|
-
def
|
21
|
-
|
22
|
-
|
25
|
+
def render(window)
|
26
|
+
window.clear
|
27
|
+
window << message
|
28
|
+
update_last_messages_size
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def message
|
34
|
+
Buffer.messages.text[@last_messages_size..-1]
|
35
|
+
end
|
36
|
+
|
37
|
+
def update_last_messages_size
|
38
|
+
@last_messages_size = Buffer.messages.text.size
|
23
39
|
end
|
24
40
|
end
|
25
41
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'amun/helpers/colors'
|
2
|
+
require 'amun/ui/mode_line_segments/major_mode'
|
3
|
+
require 'amun/ui/mode_line_segments/buffer_name'
|
4
|
+
|
5
|
+
module Amun
|
6
|
+
module UI
|
7
|
+
# a line of small segments that display
|
8
|
+
# information about the current buffer,
|
9
|
+
# like mode name, line number, buffer name...etc
|
10
|
+
class ModeLine
|
11
|
+
attr_reader :left_segments, :right_segments
|
12
|
+
|
13
|
+
def initialize(buffer)
|
14
|
+
@buffer = buffer
|
15
|
+
@right_segments = []
|
16
|
+
@left_segments = [
|
17
|
+
ModeLineSegments::BufferName.new(buffer),
|
18
|
+
ModeLineSegments::MajorMode.new(buffer)
|
19
|
+
]
|
20
|
+
|
21
|
+
Helpers::Colors.register_default(:mode_line, 0, 255)
|
22
|
+
end
|
23
|
+
|
24
|
+
def render(window)
|
25
|
+
right_output = render_segments(right_segments, window)
|
26
|
+
left_output = render_segments(left_segments, window)
|
27
|
+
|
28
|
+
size = (right_output + left_output).map(&:size).inject(0, :+)
|
29
|
+
empty_space = [0, window.maxx - size].max
|
30
|
+
filler = (' ' * empty_space).colorize(:mode_line)
|
31
|
+
|
32
|
+
Helpers::Colors.print(window, *left_output, filler, *right_output)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def render_segments(segments, window)
|
38
|
+
segments.map do |segment|
|
39
|
+
segment.render(window)
|
40
|
+
end.flatten
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Amun
|
2
|
+
module UI
|
3
|
+
module ModeLineSegments
|
4
|
+
# display buffer name in modeline
|
5
|
+
class BufferName
|
6
|
+
def initialize(buffer)
|
7
|
+
@buffer = buffer
|
8
|
+
end
|
9
|
+
|
10
|
+
def render(*)
|
11
|
+
"#{@buffer.name} ".colorize(:mode_line)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Amun
|
2
|
+
module UI
|
3
|
+
module ModeLineSegments
|
4
|
+
# display major mode name in mode line
|
5
|
+
class MajorMode
|
6
|
+
def initialize(buffer)
|
7
|
+
@buffer = buffer
|
8
|
+
end
|
9
|
+
|
10
|
+
def render(*)
|
11
|
+
mode = @buffer.major_mode.class.name.split('::').last
|
12
|
+
"(#{mode}) ".colorize(:mode_line)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'amun/ui/buffer'
|
2
|
+
|
3
|
+
module Amun
|
4
|
+
module UI
|
5
|
+
module Windows
|
6
|
+
# a window to display any buffer
|
7
|
+
# or the current buffer
|
8
|
+
class BufferWindow
|
9
|
+
def initialize(buffer = nil)
|
10
|
+
@buffer = buffer
|
11
|
+
end
|
12
|
+
|
13
|
+
# set a specific buffer to be displayed in this window
|
14
|
+
def display_buffer(buffer)
|
15
|
+
@buffer = buffer
|
16
|
+
end
|
17
|
+
|
18
|
+
# render current buffer from the Buffer class
|
19
|
+
def display_current_buffer
|
20
|
+
@buffer = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
# get current buffer that this window is rendering
|
24
|
+
def buffer
|
25
|
+
@buffer || Buffer.current
|
26
|
+
end
|
27
|
+
|
28
|
+
# render buffer
|
29
|
+
def render(window)
|
30
|
+
buffer.render(window)
|
31
|
+
end
|
32
|
+
|
33
|
+
# trigger the event in buffer
|
34
|
+
def trigger(event)
|
35
|
+
buffer.trigger(event)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'curses'
|
2
|
+
require 'amun/event_manager'
|
3
|
+
require 'amun/ui/windows/buffer_window'
|
4
|
+
require 'amun/ui/echo_area'
|
5
|
+
|
6
|
+
module Amun
|
7
|
+
module UI
|
8
|
+
module Windows
|
9
|
+
# a Frame fills all the space in terminal
|
10
|
+
# renders an echo area and an object that
|
11
|
+
# respond to #render and #trigger, like buffer,
|
12
|
+
# or another window or so
|
13
|
+
class Frame
|
14
|
+
attr_writer :screen, :echo_area, :window
|
15
|
+
|
16
|
+
def window
|
17
|
+
@window ||= BufferWindow.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def echo_area
|
21
|
+
@echo_area ||= EchoArea.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def trigger(event)
|
25
|
+
EventManager.join(event, echo_area, window, EventManager)
|
26
|
+
rescue StandardError => e
|
27
|
+
handle_exception(e)
|
28
|
+
ensure
|
29
|
+
render
|
30
|
+
end
|
31
|
+
|
32
|
+
def render
|
33
|
+
render_content
|
34
|
+
render_echo_area
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def screen
|
40
|
+
@screen ||= Curses.stdscr
|
41
|
+
end
|
42
|
+
|
43
|
+
def content_window
|
44
|
+
@content_window ||= screen.subwin(screen.maxy - 1, screen.maxx, 0, 0)
|
45
|
+
end
|
46
|
+
|
47
|
+
def echo_window
|
48
|
+
@echo_window ||= screen.subwin(1, screen.maxx, screen.maxy - 1, 0)
|
49
|
+
end
|
50
|
+
|
51
|
+
def render_content
|
52
|
+
begin
|
53
|
+
window.render(content_window)
|
54
|
+
rescue StandardError => e
|
55
|
+
handle_exception(e)
|
56
|
+
end
|
57
|
+
content_window.refresh
|
58
|
+
end
|
59
|
+
|
60
|
+
def render_echo_area
|
61
|
+
begin
|
62
|
+
echo_area.render(echo_window)
|
63
|
+
rescue StandardError => e
|
64
|
+
handle_exception(e)
|
65
|
+
end
|
66
|
+
echo_window.refresh
|
67
|
+
end
|
68
|
+
|
69
|
+
def handle_exception(e)
|
70
|
+
Buffer.messages.text << "#{e.message} (#{e.backtrace.first})\n"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/lib/amun/version.rb
CHANGED
data/lib/amun.rb
CHANGED
@@ -1,65 +1,6 @@
|
|
1
1
|
require 'amun/version'
|
2
|
-
require 'amun/
|
3
|
-
require 'amun/ui/echo_area'
|
4
|
-
require 'curses'
|
2
|
+
require 'amun/application'
|
5
3
|
|
4
|
+
# King of the gods and god of the wind
|
6
5
|
module Amun
|
7
|
-
module_function
|
8
|
-
|
9
|
-
class Application
|
10
|
-
attr_accessor :keyboard, :events, :echo_area
|
11
|
-
|
12
|
-
def self.instance
|
13
|
-
@instance ||= new
|
14
|
-
end
|
15
|
-
|
16
|
-
def quit(_event)
|
17
|
-
exit 0
|
18
|
-
end
|
19
|
-
|
20
|
-
def write_char(char)
|
21
|
-
screen.addstr(char.to_s)
|
22
|
-
end
|
23
|
-
|
24
|
-
def run
|
25
|
-
init_curses
|
26
|
-
init_ui
|
27
|
-
|
28
|
-
echo_area.echo 'Press ESC to exit.'
|
29
|
-
|
30
|
-
Thread.new do
|
31
|
-
while ch = screen.get_char
|
32
|
-
keyboard.trigger(ch)
|
33
|
-
echo_area.refresh
|
34
|
-
end
|
35
|
-
end.join
|
36
|
-
end
|
37
|
-
|
38
|
-
def screen
|
39
|
-
@screen ||= Curses.stdscr
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
def initialize(keyboard = EventManager.new, events: EventManager.new)
|
45
|
-
self.keyboard = keyboard
|
46
|
-
self.events = events
|
47
|
-
|
48
|
-
keyboard.bind "\e", self, :quit
|
49
|
-
keyboard.bind_all self, :write_char
|
50
|
-
end
|
51
|
-
|
52
|
-
def init_curses
|
53
|
-
Curses.init_screen
|
54
|
-
Curses.raw
|
55
|
-
Curses.noecho
|
56
|
-
Curses.start_color
|
57
|
-
Curses.stdscr.keypad(true)
|
58
|
-
Curses.ESCDELAY = 0
|
59
|
-
end
|
60
|
-
|
61
|
-
def init_ui
|
62
|
-
self.echo_area = Amun::UI::EchoArea.new
|
63
|
-
end
|
64
|
-
end
|
65
6
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: amun
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Emad Elsaid
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-03-
|
11
|
+
date: 2017-03-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: curses
|
@@ -108,6 +108,34 @@ dependencies:
|
|
108
108
|
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: simplecov
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: codeclimate-test-reporter
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 1.0.0
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 1.0.0
|
111
139
|
description: A CLI editor built to have an Emacs similar development environment,
|
112
140
|
with ruby in the heart of it instead of Elisp, that will make developing plugins
|
113
141
|
and extensions faster and more enjoyable, this editor is kept to the minimum, anything
|
@@ -120,6 +148,7 @@ extensions: []
|
|
120
148
|
extra_rdoc_files: []
|
121
149
|
files:
|
122
150
|
- ".gitignore"
|
151
|
+
- ".rubocop.yml"
|
123
152
|
- ".ruby-version"
|
124
153
|
- ".travis.yml"
|
125
154
|
- Gemfile
|
@@ -129,12 +158,24 @@ files:
|
|
129
158
|
- Rakefile
|
130
159
|
- amun.gemspec
|
131
160
|
- bin/console
|
132
|
-
- bin/setup
|
133
161
|
- exe/amun
|
134
162
|
- lib/amun.rb
|
163
|
+
- lib/amun/application.rb
|
164
|
+
- lib/amun/behaviours/emacs.rb
|
135
165
|
- lib/amun/event_manager.rb
|
166
|
+
- lib/amun/features/echo_event.rb
|
167
|
+
- lib/amun/features/quit.rb
|
168
|
+
- lib/amun/features_loader.rb
|
136
169
|
- lib/amun/helpers/colors.rb
|
170
|
+
- lib/amun/helpers/keyboard.rb
|
171
|
+
- lib/amun/major_modes/fundamental.rb
|
172
|
+
- lib/amun/ui/buffer.rb
|
137
173
|
- lib/amun/ui/echo_area.rb
|
174
|
+
- lib/amun/ui/mode_line.rb
|
175
|
+
- lib/amun/ui/mode_line_segments/buffer_name.rb
|
176
|
+
- lib/amun/ui/mode_line_segments/major_mode.rb
|
177
|
+
- lib/amun/ui/windows/buffer_window.rb
|
178
|
+
- lib/amun/ui/windows/frame.rb
|
138
179
|
- lib/amun/version.rb
|
139
180
|
homepage: http://www.github.com/blazeeboy/amun
|
140
181
|
licenses:
|