notepadqq_api 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 39710914d68c92d17c5a22ecf2127f080e7129e4
4
+ data.tar.gz: ea8fd30106d0259a57f4142dba03f256a761e232
5
+ SHA512:
6
+ metadata.gz: bfb8a113bc860463d9b05ca26018e3deeab3af14d3c927def213d894d2ec28fd0caace181b6ade134479895a12cf0748549b44c6fff90b250589cc9ba737b99d
7
+ data.tar.gz: c0ab795d4f91060b5c7cef325d17ab3638f476e9ad9c429a70516868713641291819deb99b9b53ded7d54e458bb33ea1fc4f01c3ffe2210fcc1f1926e3b921f0
@@ -0,0 +1,94 @@
1
+ require 'socket'
2
+ require 'json'
3
+
4
+ class NotepadqqApi
5
+ class MessageChannel
6
+
7
+ def initialize(socketPath)
8
+ # Connect to Notepadqq socket
9
+ @client = UNIXSocket.open(socketPath)
10
+
11
+ @incomingBuffer = "" # Incomplete json messages (as strings)
12
+ @parsedBuffer = [] # Unprocessed object messages
13
+ end
14
+
15
+ # Sends a JSON message to Notepadqq
16
+ def sendMessage(msg)
17
+ sendRawMessage(JSON.generate(msg))
18
+ end
19
+
20
+ # Read incoming messages
21
+ def getMessages(block=true)
22
+
23
+ begin
24
+ if block and @incomingBuffer.empty? and @parsedBuffer.empty?
25
+ read = @client.recv(1048576)
26
+ else
27
+ read = @client.recv_nonblock(1048576)
28
+ end
29
+ rescue
30
+ read = ""
31
+ end
32
+
33
+ @incomingBuffer += read
34
+ messages = @incomingBuffer.split("\n")
35
+
36
+ if @incomingBuffer.end_with? "\n"
37
+ # We only got complete messages: clear the buffer
38
+ @incomingBuffer.clear
39
+ else
40
+ # We need to store the incomplete message in the buffer
41
+ @incomingBuffer = messages.pop || ""
42
+ end
43
+
44
+ converted = []
45
+ for i in 0...messages.length
46
+ begin
47
+ msg = JSON.parse(messages[i])
48
+ converted.push(msg)
49
+ rescue
50
+ puts "Invalid message received."
51
+ end
52
+ end
53
+
54
+ retval = @parsedBuffer + converted
55
+ @parsedBuffer = []
56
+
57
+ # Make sure that, when block=true, at least one message is received
58
+ if block and retval.empty?
59
+ retval += getMessages(true)
60
+ end
61
+
62
+ return retval
63
+ end
64
+
65
+ # Get the next message of type "result".
66
+ # The other messages will still be returned by getMessages
67
+ def getNextResultMessage
68
+ discarded = []
69
+
70
+ while true do
71
+ chunk = self.getMessages
72
+ for i in 0...chunk.length
73
+ if chunk[i].has_key?("result")
74
+ discarded += chunk[0...i]
75
+ discarded += chunk[i+1..-1]
76
+ @parsedBuffer = discarded
77
+ return chunk[i]
78
+ end
79
+ end
80
+
81
+ discarded += chunk
82
+ end
83
+
84
+ end
85
+
86
+ private
87
+
88
+ # Sends a raw string message to Notepadqq
89
+ def sendRawMessage(msg)
90
+ @client.send(msg, 0)
91
+ end
92
+
93
+ end
94
+ end
@@ -0,0 +1,113 @@
1
+ class NotepadqqApi
2
+ class MessageInterpreter
3
+
4
+ def initialize(messageChannel)
5
+ @messageChannel = messageChannel
6
+
7
+ # Hash of event handlers, for example
8
+ # {
9
+ # 1: {
10
+ # "newWindow": [<callback1>, ..., <callbackn>]
11
+ # },
12
+ # ...
13
+ # }
14
+ # Where 1 is an objectId and "newWindow" is an event of that object
15
+ @eventHandlers = {}
16
+ end
17
+
18
+ # Assign an event of a particular objectId to a callback
19
+ def registerEventHandler(objectId, event, callback)
20
+ event = event.to_sym
21
+
22
+ @eventHandlers[objectId] ||= {}
23
+ @eventHandlers[objectId][event] ||= []
24
+
25
+ @eventHandlers[objectId][event].push(callback)
26
+ end
27
+
28
+ # Calls a method on the remote object objectId
29
+ def invokeApi(objectId, method, args)
30
+ message = {
31
+ :objectId => objectId,
32
+ :method => method,
33
+ :args => args
34
+ }
35
+
36
+ @messageChannel.sendMessage(message)
37
+ reply = @messageChannel.getNextResultMessage
38
+
39
+ result = [reply["result"]]
40
+ convertStubs!(result)
41
+ result = result[0]
42
+
43
+ # Fixme check for errors in reply["err"]
44
+
45
+ return result
46
+ end
47
+
48
+ def processMessage(message)
49
+ if message.has_key?("event")
50
+ processEventMessage(message)
51
+ elsif message.has_key?("result")
52
+ # We shouldn't have received it here... ignore it
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ # Call the handlers connected to this event
59
+ def processEventMessage(message)
60
+ event = message["event"].to_sym
61
+ objectId = message["objectId"]
62
+
63
+ if @eventHandlers[objectId] and @eventHandlers[objectId][event]
64
+ handlers = @eventHandlers[objectId][event]
65
+
66
+ args = message["args"]
67
+ convertStubs!(args)
68
+
69
+ (handlers.length-1).downto(0).each { |i|
70
+ handlers[i].call(*args)
71
+ }
72
+ end
73
+ end
74
+
75
+ def convertStubs!(dataArray)
76
+ # FIXME Use a stack
77
+
78
+ dataArray.map! { |value|
79
+ unless value.nil?
80
+ if value.kind_of?(Array)
81
+ convertStubs!(value)
82
+
83
+ elsif value.kind_of?(Hash) and
84
+ value["$__nqq__stub_type"].kind_of?(String) and
85
+ value["id"].kind_of?(Fixnum)
86
+
87
+ stubType = value["$__nqq__stub_type"]
88
+ begin
89
+ stub = Object::const_get(Stubs.name + "::" + stubType)
90
+ stub.new(self, value["id"])
91
+ rescue
92
+ puts "Unknown stub: " + stubType
93
+ value
94
+ end
95
+
96
+ elsif value.kind_of?(Hash)
97
+ value.each do |key, data|
98
+ tmpArray = [data]
99
+ convertStubs!(tmpArray)
100
+ value[key] = tmpArray[0]
101
+ end
102
+
103
+ value
104
+
105
+ else
106
+ value
107
+ end
108
+ end
109
+ }
110
+ end
111
+
112
+ end
113
+ end
@@ -0,0 +1,37 @@
1
+ class NotepadqqApi
2
+ module Stubs
3
+
4
+ class Stub
5
+
6
+ def initialize(messageInterpreter, id)
7
+ @messageInterpreter = messageInterpreter
8
+ @id = id
9
+ end
10
+
11
+ def on(event, &callback)
12
+ @messageInterpreter.registerEventHandler(@id, event, callback)
13
+ end
14
+
15
+ def method_missing(method, *args, &block)
16
+ return @messageInterpreter.invokeApi(@id, method, args)
17
+ end
18
+
19
+ def ==(other)
20
+ other.class <= Stub && id == other.id
21
+ end
22
+
23
+ protected
24
+
25
+ def id
26
+ @id
27
+ end
28
+
29
+ end
30
+
31
+ class Notepadqq < Stub; end
32
+ class Editor < Stub; end
33
+ class Window < Stub; end
34
+ class MenuItem < Stub; end
35
+
36
+ end
37
+ end
@@ -0,0 +1,38 @@
1
+ require 'notepadqq_api/message_channel'
2
+ require 'notepadqq_api/message_interpreter'
3
+ require 'notepadqq_api/stubs'
4
+
5
+ class NotepadqqApi
6
+
7
+ NQQ_STUB_ID = 1
8
+
9
+ attr_reader :extensionId
10
+
11
+ def initialize(socketPath = ARGV[0], extensionId = ARGV[1])
12
+ @socketPath = socketPath
13
+ @extensionId = extensionId
14
+
15
+ @messageChannel = MessageChannel.new(@socketPath)
16
+ @messageInterpreter = MessageInterpreter.new(@messageChannel)
17
+ end
18
+
19
+ # Start reading messages and calling event handlers
20
+ def runEventLoop
21
+ yield
22
+
23
+ while true do
24
+ messages = @messageChannel.getMessages
25
+ messages.each do |msg|
26
+ @messageInterpreter.processMessage(msg)
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ # Returns an instance of Notepadqq
33
+ def notepadqq
34
+ @nqq ||= Stubs::Notepadqq.new(@messageInterpreter, NQQ_STUB_ID);
35
+ return @nqq
36
+ end
37
+
38
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: notepadqq_api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Daniele Di Sarli
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-04-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: json
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.8'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.8.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '1.8'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.8.0
33
+ description: Notepadqq API Layer for extensions
34
+ email: danieleds0@gmail.com
35
+ executables: []
36
+ extensions: []
37
+ extra_rdoc_files: []
38
+ files:
39
+ - lib/notepadqq_api.rb
40
+ - lib/notepadqq_api/message_channel.rb
41
+ - lib/notepadqq_api/message_interpreter.rb
42
+ - lib/notepadqq_api/stubs.rb
43
+ homepage: http://rubygems.org/gems/hola
44
+ licenses:
45
+ - MIT
46
+ metadata: {}
47
+ post_install_message:
48
+ rdoc_options: []
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ requirements: []
62
+ rubyforge_project:
63
+ rubygems_version: 2.2.2
64
+ signing_key:
65
+ specification_version: 4
66
+ summary: Notepadqq API Layer
67
+ test_files: []