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 +7 -0
- data/lib/notepadqq_api/message_channel.rb +94 -0
- data/lib/notepadqq_api/message_interpreter.rb +113 -0
- data/lib/notepadqq_api/stubs.rb +37 -0
- data/lib/notepadqq_api.rb +38 -0
- metadata +67 -0
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: []
|