vedeu 0.2.8 → 0.2.9
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/lib/vedeu.rb +1 -0
- data/lib/vedeu/application.rb +1 -1
- data/lib/vedeu/models/buffer.rb +103 -0
- data/lib/vedeu/output/clear.rb +41 -8
- data/lib/vedeu/output/compositor.rb +46 -21
- data/lib/vedeu/output/render.rb +1 -5
- data/lib/vedeu/repositories/buffers.rb +19 -76
- data/lib/vedeu/repositories/focus.rb +0 -16
- data/lib/vedeu/support/coercions.rb +3 -0
- data/lib/vedeu/support/colour_translator.rb +29 -0
- data/lib/vedeu/support/log.rb +2 -0
- data/lib/vedeu/support/repository.rb +1 -0
- data/test/lib/vedeu/api/api_test.rb +7 -2
- data/test/lib/vedeu/application_test.rb +20 -8
- data/test/lib/vedeu/models/buffer_test.rb +182 -0
- data/test/lib/vedeu/output/clear_test.rb +8 -5
- data/test/lib/vedeu/output/compositor_test.rb +91 -43
- data/test/lib/vedeu/output/render_test.rb +3 -50
- data/test/lib/vedeu/repositories/buffers_test.rb +4 -48
- data/test/lib/vedeu/repositories/focus_test.rb +0 -26
- data/test/lib/vedeu/support/repository_test.rb +38 -7
- data/vedeu.gemspec +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fd5bd7cd569896aed34eadea8a2bb15312c99d4a
|
4
|
+
data.tar.gz: ea45bf2468c2ce0d22250da6f2b903ada4ed5028
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 796459529347814e7644f0d9c4903c74dd197c93c548f50b06eb2c8fe5fc13c783f93bb8a09e02de6668da7b739310ee0f428521c2a5bf68eb5addc0f3cb32b9
|
7
|
+
data.tar.gz: 18c57141d04b4a2f2034ebdbe0384ba05d29a2f6e3ccf64166192c999b55ca6123264da5cfaabd26d3a22d8aee28e5d6f20309caa56131a13270b00bf00639d0
|
data/lib/vedeu.rb
CHANGED
data/lib/vedeu/application.rb
CHANGED
@@ -43,7 +43,7 @@ module Vedeu
|
|
43
43
|
# - We enter into the main sequence where the application will either run
|
44
44
|
# once or continuous, interactively or standalone.
|
45
45
|
#
|
46
|
-
# @return []
|
46
|
+
# @return [Array] The last output sent to the terminal.
|
47
47
|
def start
|
48
48
|
Terminal.open do
|
49
49
|
Terminal.set_cursor_mode
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Vedeu
|
2
|
+
|
3
|
+
# The Buffer object represents the states of display for an interface. The
|
4
|
+
# states are 'front', 'back' and 'previous'.
|
5
|
+
#
|
6
|
+
# - 'front': The currently displayed buffer; contains the content which was
|
7
|
+
# last output.
|
8
|
+
# - 'back': The next buffer to be displayed; contains the content which
|
9
|
+
# will be shown on next refresh.
|
10
|
+
# - 'previous': The previous buffer which was displayed; contains the content
|
11
|
+
# that was shown before 'front'.
|
12
|
+
#
|
13
|
+
class Buffer
|
14
|
+
|
15
|
+
attr_reader :back, :front, :name, :previous
|
16
|
+
alias_method :current, :front
|
17
|
+
|
18
|
+
# Return a new instance of Buffer.
|
19
|
+
#
|
20
|
+
# @param attributes [Hash]
|
21
|
+
# @return [Buffer]
|
22
|
+
def initialize(attributes = {})
|
23
|
+
@attributes = defaults.merge(attributes)
|
24
|
+
|
25
|
+
@name = @attributes[:name]
|
26
|
+
@back = @attributes[:back]
|
27
|
+
@front = @attributes[:front]
|
28
|
+
@previous = @attributes[:previous]
|
29
|
+
end
|
30
|
+
|
31
|
+
# Return a boolean indicating content on the front buffer.
|
32
|
+
#
|
33
|
+
# @return [Boolean]
|
34
|
+
def front?
|
35
|
+
front.any? { |k, v| k == :lines && v.any? }
|
36
|
+
end
|
37
|
+
alias_method :current?, :front?
|
38
|
+
|
39
|
+
# Return a boolean indicating content on the back buffer.
|
40
|
+
#
|
41
|
+
# @return [Boolean]
|
42
|
+
def back?
|
43
|
+
back.any? { |k, v| k == :lines && v.any? }
|
44
|
+
end
|
45
|
+
|
46
|
+
# Return a boolean indicating content on the previous buffer.
|
47
|
+
#
|
48
|
+
# @return [Boolean]
|
49
|
+
def previous?
|
50
|
+
previous.any? { |k, v| k == :lines && v.any? }
|
51
|
+
end
|
52
|
+
|
53
|
+
# Add the content to the back buffer, then update the repository. Returns
|
54
|
+
# boolean indicating that the repository was updated.
|
55
|
+
#
|
56
|
+
# @param content [Hash]
|
57
|
+
# @return [Boolean]
|
58
|
+
def add(content = {})
|
59
|
+
self.back = content
|
60
|
+
|
61
|
+
update!
|
62
|
+
end
|
63
|
+
|
64
|
+
# Return a boolean indicating content was swapped between buffers. It also
|
65
|
+
# resets the offsets (i.e. scroll/cursor position).
|
66
|
+
#
|
67
|
+
# @return [Boolean]
|
68
|
+
def swap
|
69
|
+
return false unless back?
|
70
|
+
|
71
|
+
# Offsets.update({ name: name })
|
72
|
+
|
73
|
+
self.previous = front
|
74
|
+
self.front = back
|
75
|
+
self.back = {}
|
76
|
+
|
77
|
+
update!
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
attr_writer :back, :front, :previous
|
83
|
+
|
84
|
+
# @see Buffers#update
|
85
|
+
def update!
|
86
|
+
Buffers.update(self)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Return the default attributes of a Buffer.
|
90
|
+
#
|
91
|
+
# @return [Hash]
|
92
|
+
def defaults
|
93
|
+
{
|
94
|
+
name: '',
|
95
|
+
back: {},
|
96
|
+
front: {},
|
97
|
+
previous: {},
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
end # Buffer
|
102
|
+
|
103
|
+
end # Vedeu
|
data/lib/vedeu/output/clear.rb
CHANGED
@@ -4,31 +4,56 @@ module Vedeu
|
|
4
4
|
# class is called every time an interface is rendered to prepare the area
|
5
5
|
# for new data.
|
6
6
|
#
|
7
|
+
# @note We don't simply clear the entire terminal as this would remove the
|
8
|
+
# content of other interfaces which are being displayed.
|
9
|
+
#
|
10
|
+
# @todo What if an interface 'moves' or changes shape due to the terminal
|
11
|
+
# resizing?
|
12
|
+
#
|
7
13
|
# @api private
|
8
14
|
class Clear
|
9
15
|
|
10
16
|
# Blanks the area defined by the interface.
|
11
17
|
#
|
12
18
|
# @param interface [Interface]
|
19
|
+
# @param options [Hash]
|
13
20
|
# @return [String]
|
14
|
-
|
15
|
-
|
21
|
+
# @see #initialize
|
22
|
+
def self.call(interface, options = {})
|
23
|
+
new(interface, options).clear
|
16
24
|
end
|
17
25
|
|
18
26
|
# Returns a new instance of Clear.
|
19
27
|
#
|
20
28
|
# @param interface [Interface]
|
29
|
+
# @param options [Hash]
|
30
|
+
# @option options :direct [Boolean] Send escape sequences to clear an area
|
31
|
+
# directly to the Terminal.
|
21
32
|
# @return [Clear]
|
22
|
-
def initialize(interface)
|
33
|
+
def initialize(interface, options = {})
|
23
34
|
@interface = interface
|
35
|
+
@options = defaults.merge!(options)
|
24
36
|
end
|
25
37
|
|
38
|
+
# Send the cleared area to the terminal.
|
39
|
+
#
|
40
|
+
# @return [Array]
|
41
|
+
def clear
|
42
|
+
return Terminal.output(view) if direct?
|
43
|
+
|
44
|
+
view
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
attr_reader :interface, :options
|
50
|
+
|
26
51
|
# For each visible line of the interface, set the foreground and background
|
27
52
|
# colours to those specified when the interface was defined, then starting
|
28
53
|
# write space characters over the area which the interface occupies.
|
29
54
|
#
|
30
55
|
# @return [String]
|
31
|
-
def
|
56
|
+
def view
|
32
57
|
Vedeu.log("Clearing view: '#{interface.name}'")
|
33
58
|
|
34
59
|
rows.inject([colours]) do |line, index|
|
@@ -36,10 +61,6 @@ module Vedeu
|
|
36
61
|
end.join
|
37
62
|
end
|
38
63
|
|
39
|
-
private
|
40
|
-
|
41
|
-
attr_reader :interface
|
42
|
-
|
43
64
|
# @return [String]
|
44
65
|
def colours
|
45
66
|
interface.colour.to_s
|
@@ -50,6 +71,18 @@ module Vedeu
|
|
50
71
|
interface.height.times
|
51
72
|
end
|
52
73
|
|
74
|
+
# @return [Boolean]
|
75
|
+
def direct?
|
76
|
+
options[:direct]
|
77
|
+
end
|
78
|
+
|
79
|
+
# @return [Hash]
|
80
|
+
def defaults
|
81
|
+
{
|
82
|
+
direct: true
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
53
86
|
end # Clear
|
54
87
|
|
55
88
|
end # Vedeu
|
@@ -38,49 +38,74 @@ module Vedeu
|
|
38
38
|
|
39
39
|
# Renders the cursor into the currently focussed interface. May be hidden.
|
40
40
|
#
|
41
|
-
# @return [String]
|
41
|
+
# @return [String] The escape sequence to render the cursor as shown or
|
42
|
+
# hidden.
|
42
43
|
def cursor
|
43
|
-
Interface.new(
|
44
|
+
Interface.new(Interfaces.find(Focus.current)).cursor.to_s
|
44
45
|
end
|
45
46
|
|
46
|
-
#
|
47
|
-
# interface occupies.
|
47
|
+
# Return the content for this buffer.
|
48
48
|
#
|
49
|
-
#
|
49
|
+
# - If we have new content (i.e. content on 'back') to be shown, we first
|
50
|
+
# clear the area occupied by the previous content, then clear the area for
|
51
|
+
# the new content, and then finally render the new content.
|
52
|
+
# - If there is no new content (i.e. 'back' is empty), check the 'front'
|
53
|
+
# buffer and display that.
|
54
|
+
# - If there is no new content, and the front buffer is empty, display the
|
55
|
+
# 'previous' buffer.
|
56
|
+
# - If the 'previous' buffer is empty, return an empty hash.
|
57
|
+
#
|
58
|
+
# @return [Hash]
|
50
59
|
def view
|
51
|
-
if buffer
|
52
|
-
|
60
|
+
if buffer.back?
|
61
|
+
Clear.call(compose(buffer.previous)) if buffer.previous?
|
62
|
+
|
63
|
+
buffer.swap
|
64
|
+
|
65
|
+
Render.call(compose(buffer.front))
|
66
|
+
|
67
|
+
elsif buffer.front?
|
68
|
+
Render.call(compose(buffer.front))
|
69
|
+
|
70
|
+
elsif buffer.previous?
|
71
|
+
Render.call(compose(buffer.previous))
|
53
72
|
|
54
73
|
else
|
55
|
-
Clear.call(
|
74
|
+
Clear.call(compose({}), { direct: false })
|
56
75
|
|
57
76
|
end
|
58
77
|
end
|
59
78
|
|
60
|
-
#
|
61
|
-
#
|
79
|
+
# Return a new instance of Interface built by combining the buffer content
|
80
|
+
# attributes with the stored interface attributes.
|
62
81
|
#
|
63
|
-
# @return [
|
64
|
-
def
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
82
|
+
# @return [Interface]
|
83
|
+
def compose(content)
|
84
|
+
if defined_value?(content[:geometry])
|
85
|
+
content[:geometry].each do |k, v|
|
86
|
+
interface[:geometry][k] = v if defined_value?(k)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
interface[:lines] = content[:lines]
|
91
|
+
interface[:colour] = content[:colour] if defined_value?(content[:colour])
|
92
|
+
interface[:style] = content[:style] if defined_value?(content[:style])
|
93
|
+
|
94
|
+
Interface.new(interface)
|
70
95
|
end
|
71
96
|
|
72
97
|
# Returns the attributes of the named interface (layout).
|
73
98
|
#
|
74
99
|
# @return [Hash]
|
75
100
|
def interface
|
76
|
-
@_interface ||=
|
101
|
+
@_interface ||= Interfaces.find(name)
|
77
102
|
end
|
78
103
|
|
79
|
-
#
|
104
|
+
# Return the named Buffer (view).
|
80
105
|
#
|
81
|
-
# @return [
|
106
|
+
# @return [Buffer]
|
82
107
|
def buffer
|
83
|
-
@_buffer ||=
|
108
|
+
@_buffer ||= Buffers.find(name)
|
84
109
|
end
|
85
110
|
|
86
111
|
end # Compositor
|
data/lib/vedeu/output/render.rb
CHANGED
@@ -30,7 +30,7 @@ module Vedeu
|
|
30
30
|
#
|
31
31
|
# @return [String]
|
32
32
|
def render
|
33
|
-
out = [
|
33
|
+
out = [ Clear.call(interface, { direct: false }) ]
|
34
34
|
|
35
35
|
Vedeu.log("Rendering view: '#{interface.name}'")
|
36
36
|
|
@@ -45,10 +45,6 @@ module Vedeu
|
|
45
45
|
|
46
46
|
attr_reader :interface
|
47
47
|
|
48
|
-
def clear
|
49
|
-
Clear.call(interface)
|
50
|
-
end
|
51
|
-
|
52
48
|
end # Render
|
53
49
|
|
54
50
|
end # Vedeu
|
@@ -14,107 +14,50 @@ module Vedeu
|
|
14
14
|
# buffer added to storage.
|
15
15
|
#
|
16
16
|
# @param attributes [Hash]
|
17
|
-
# @return [String]
|
17
|
+
# @return [String] The name of the buffer that has been added.
|
18
18
|
def add(attributes)
|
19
19
|
validate_attributes!(attributes)
|
20
20
|
|
21
|
-
|
22
|
-
buffer = find(attributes[:name])
|
21
|
+
name = attributes[:name]
|
23
22
|
|
24
|
-
|
23
|
+
if registered?(name)
|
24
|
+
Vedeu.log("Adding new content to existing buffer: '#{name}'")
|
25
25
|
|
26
|
-
|
27
|
-
storage.store(attributes[:name], {
|
28
|
-
back_buffer: attributes,
|
29
|
-
front_buffer: nil,
|
30
|
-
})
|
26
|
+
find(name).add(attributes)
|
31
27
|
|
32
|
-
|
28
|
+
else
|
29
|
+
Vedeu.log("Adding new buffer: '#{name}'")
|
33
30
|
|
34
|
-
|
35
|
-
end
|
31
|
+
Buffer.new({ name: name }).add(attributes)
|
36
32
|
|
37
|
-
|
38
|
-
#
|
39
|
-
# @param name [String]
|
40
|
-
# @raise [BufferNotFound] When the named buffer cannot be found.
|
41
|
-
# @return [Hash|Nil]
|
42
|
-
def back(name)
|
43
|
-
find(name)[:back_buffer]
|
44
|
-
end
|
33
|
+
end
|
45
34
|
|
46
|
-
|
47
|
-
#
|
48
|
-
# @param name [String]
|
49
|
-
# @raise [BufferNotFound] When the named buffer cannot be found.
|
50
|
-
# @return [Hash|Nil]
|
51
|
-
def front(name)
|
52
|
-
find(name)[:front_buffer]
|
35
|
+
name
|
53
36
|
end
|
54
37
|
|
55
|
-
#
|
56
|
-
#
|
57
|
-
# front buffer.
|
58
|
-
#
|
59
|
-
# When the back buffer has new content, we swap the back onto the front and
|
60
|
-
# return the front buffer to be rendered.
|
38
|
+
# Update the repository with the provided Buffer. Returns a boolean
|
39
|
+
# indicating whether this was successful.
|
61
40
|
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
|
65
|
-
|
66
|
-
# and we should return nothing.
|
67
|
-
#
|
68
|
-
# @param name [String]
|
69
|
-
# @return [Hash|NilClass]
|
70
|
-
def latest(name)
|
71
|
-
swap_buffers(name) if new_content?(name)
|
41
|
+
# @param buffer [Buffer]
|
42
|
+
# @return [Boolean]
|
43
|
+
def update(buffer)
|
44
|
+
storage.store(buffer.name, buffer)
|
72
45
|
|
73
|
-
|
46
|
+
true
|
74
47
|
end
|
75
48
|
|
76
49
|
private
|
77
50
|
|
78
|
-
# Swap the named back buffer into the front buffer of the same name. This is
|
79
|
-
# called when the back buffer has new content (perhaps as part of a
|
80
|
-
# refresh). It also resets the offsets (i.e. scroll position)
|
81
|
-
#
|
82
|
-
# @param name [String]
|
83
|
-
# @return [Hash]
|
84
|
-
def swap_buffers(name)
|
85
|
-
buffer = find(name)
|
86
|
-
|
87
|
-
Offsets.update({ name: name })
|
88
|
-
|
89
|
-
storage.store(name, {
|
90
|
-
front_buffer: buffer[:back_buffer],
|
91
|
-
back_buffer: nil,
|
92
|
-
})
|
93
|
-
end
|
94
|
-
|
95
|
-
# Return a boolean indicating whether the named back buffer has new content.
|
96
|
-
#
|
97
|
-
# @param name [String]
|
98
|
-
# @return [Boolean]
|
99
|
-
def new_content?(name)
|
100
|
-
defined_value?(back(name))
|
101
|
-
end
|
102
|
-
|
103
51
|
# @return [Hash]
|
104
52
|
def in_memory
|
105
|
-
|
106
|
-
hash[interface_name] = {
|
107
|
-
front_buffer: nil,
|
108
|
-
back_buffer: nil,
|
109
|
-
}
|
110
|
-
end
|
53
|
+
{}
|
111
54
|
end
|
112
55
|
|
113
56
|
# @param name [String]
|
114
57
|
# @raise [BufferNotFound] When the entity cannot be found with this name.
|
115
58
|
# @return [BufferNotFound]
|
116
59
|
def not_found(name)
|
117
|
-
fail BufferNotFound, "Cannot find buffer
|
60
|
+
fail BufferNotFound, "Cannot find buffer: '#{name}'"
|
118
61
|
end
|
119
62
|
|
120
63
|
end # Buffers
|