1
+ // @ts -ignore
2
+ const Me = imports . misc . extensionUtils . getCurrentExtension ( )
3
+
4
+ const { Gio, GLib } = imports . gi
5
+
6
+ import * as utils from 'utils'
7
+
8
+ /** The trait which all builtin plugins implement */
9
+ export abstract class Builtin {
10
+ /** Stores the last search result */
11
+ last_response : null | Response . Response = null
12
+
13
+ /** Results of the last query */
14
+ selections : Array < Response . Selection > = new Array ( )
15
+
16
+ /** Initializes default values and resets state */
17
+ abstract init ( ) : void
18
+
19
+ /** Uses the search input to query for search results */
20
+ abstract query ( query : string ) : Response . Response
21
+
22
+ /** Applies an option by its ID */
23
+ abstract submit ( id : number ) : Response . Response
24
+
25
+ /** Dispatches a launcher request, and stores the response */
26
+ handle ( event : Request . Request ) {
27
+ switch ( event . event ) {
28
+ case "complete" :
29
+ this . last_response = { event : "noop" }
30
+ break
31
+ case "query" :
32
+ this . last_response = this . query ( event . value )
33
+ break
34
+ case "submit" :
35
+ this . last_response = this . submit ( event . id )
36
+ break
37
+ default :
38
+ this . last_response = { event : "noop" }
39
+
40
+ }
41
+ }
42
+ }
43
+
44
+ export namespace Request {
45
+ export type Request = Complete | Submit | Query | Quit
46
+
47
+ export interface Complete {
48
+ event : 'complete' ,
49
+ }
50
+
51
+ export interface Submit {
52
+ event : 'submit' ,
53
+ id : number
54
+ }
55
+
56
+ export interface Quit {
57
+ event : 'quit'
58
+ }
59
+
60
+ export interface Query {
61
+ event : 'query' ,
62
+ value : string
63
+ }
64
+ }
65
+
66
+ export namespace Response {
67
+ export interface Selection {
68
+ id : number
69
+ name : string
70
+ description : null | string
71
+ icon ?: string
72
+ content_type ?: string
73
+ }
74
+
75
+ export interface Query {
76
+ event : "queried" ,
77
+ selections : Array < Selection >
78
+ }
79
+
80
+ export interface Fill {
81
+ event : "fill" ,
82
+ text : string
83
+ }
84
+
85
+ export interface Close {
86
+ event : "close"
87
+ }
88
+
89
+ export interface NoOp {
90
+ event : 'noop'
91
+ }
92
+
93
+ export type Response = Query | Fill | Close | NoOp
94
+
95
+ export function parse ( input : string ) : null | Response {
96
+ try {
97
+ let object = JSON . parse ( input ) as Response
98
+ switch ( object . event ) {
99
+ case "close" :
100
+ case "fill" :
101
+ case "queried" :
102
+ return object
103
+ }
104
+ } catch ( e ) {
105
+
106
+ }
107
+
108
+ return null
109
+ }
110
+ }
111
+
112
+ export namespace Plugin {
113
+ export interface Config {
114
+ name : string
115
+ description : string
116
+ pattern : string
117
+ exec : string
118
+ icon : string
119
+ }
120
+
121
+ export function read ( file : string ) : Config | null {
122
+ global . log ( `found plugin at ${ file } ` )
123
+ try {
124
+ let [ ok , contents ] = Gio . file_new_for_path ( file )
125
+ . load_contents ( null )
126
+
127
+ if ( ok ) return parse ( imports . byteArray . toString ( contents ) )
128
+ } catch ( e ) {
129
+
130
+ }
131
+
132
+ return null
133
+ }
134
+
135
+ export function parse ( input : string ) : Config | null {
136
+ try {
137
+ return JSON . parse ( input )
138
+ } catch ( e ) {
139
+ return null
140
+ }
141
+ }
142
+
143
+ export interface External {
144
+ cmd : string
145
+ proc : null | utils . AsyncIPC
146
+ }
147
+
148
+ export interface BuiltinVariant {
149
+ builtin : Builtin
150
+ }
151
+
152
+ export interface Source {
153
+ config : Config
154
+ backend : External | BuiltinVariant
155
+ pattern : null | RegExp
156
+ }
157
+
158
+ export function listen ( plugin : Plugin . Source ) : null | Response . Response {
159
+ if ( 'builtin' in plugin . backend ) {
160
+ return plugin . backend . builtin . last_response
161
+ } else {
162
+ const backend = plugin . backend
163
+ if ( ! backend . proc ) {
164
+ const proc = Plugin . start ( backend )
165
+ if ( proc ) {
166
+ backend . proc = proc
167
+ } else {
168
+ return null
169
+ }
170
+ }
171
+
172
+ try {
173
+ let [ bytes , ] = backend . proc . stdout . read_line ( null )
174
+ return Response . parse ( imports . byteArray . toString ( bytes ) )
175
+ } catch ( e ) {
176
+ return null
177
+ }
178
+ }
179
+ }
180
+
181
+ export function complete ( plugin : Plugin . Source ) : boolean {
182
+ return send ( plugin , { event : "complete" } )
183
+ }
184
+
185
+ export function query ( plugin : Plugin . Source , value : string ) : boolean {
186
+ return send ( plugin , { event : "query" , value } )
187
+ }
188
+
189
+ export function quit ( plugin : Plugin . Source ) {
190
+ if ( 'proc' in plugin . backend ) {
191
+ if ( plugin . backend . proc ) {
192
+ send ( plugin , { event : "quit" } )
193
+ plugin . backend . proc = null
194
+ }
195
+ } else {
196
+ send ( plugin , { event : "quit" } )
197
+ }
198
+ }
199
+
200
+ export function submit ( plugin : Plugin . Source , id : number ) : boolean {
201
+ return send ( plugin , { event : "submit" , id } )
202
+ }
203
+
204
+ export function send ( plugin : Plugin . Source , event : Request . Request ) : boolean {
205
+ const backend = plugin . backend
206
+
207
+ if ( 'builtin' in backend ) {
208
+ backend . builtin . handle ( event )
209
+ return true
210
+ } else {
211
+ let string = JSON . stringify ( event )
212
+
213
+ if ( ! backend . proc ) {
214
+ backend . proc = start ( backend )
215
+ }
216
+
217
+ function attempt ( name : string , plugin : Plugin . External , string : string ) {
218
+ if ( ! plugin . proc ) return false
219
+
220
+ try {
221
+ plugin . proc . stdin . write_bytes ( new GLib . Bytes ( string + "\n" ) , null )
222
+ return true
223
+ } catch ( e ) {
224
+ global . log ( `failed to send message to ${ name } : ${ e } ` )
225
+ return false
226
+ }
227
+ }
228
+
229
+ if ( ! attempt ( plugin . config . name , backend , string ) ) {
230
+ backend . proc = start ( backend )
231
+ if ( ! attempt ( plugin . config . name , backend , string ) ) return false
232
+ }
233
+ }
234
+
235
+ return true
236
+ }
237
+
238
+ export function start ( plugin : Plugin . External ) : null | utils . AsyncIPC {
239
+ return utils . async_process_ipc ( [ plugin . cmd ] )
240
+ }
241
+ }
0 commit comments