notepadqq_api 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []