amun 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/amun.svg)](http://badge.fury.io/rb/amun)
|
5
|
+
[![Build Status](https://travis-ci.org/blazeeboy/amun.svg?branch=master)](https://travis-ci.org/blazeeboy/amun)
|
6
|
+
[![Code Climate](https://codeclimate.com/github/blazeeboy/amun/badges/gpa.svg)](https://codeclimate.com/github/blazeeboy/amun)
|
7
|
+
[![Test Coverage](https://codeclimate.com/github/blazeeboy/amun/badges/coverage.svg)](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:
|