rtext 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +20 -0
- data/{README → README.rdoc} +5 -1
- data/RText_Protocol +444 -0
- data/Rakefile +10 -10
- data/lib/rtext/completer.rb +32 -26
- data/lib/rtext/context_builder.rb +113 -59
- data/lib/rtext/default_loader.rb +73 -8
- data/lib/rtext/default_service_provider.rb +30 -14
- data/lib/rtext/frontend/config.rb +58 -0
- data/lib/rtext/frontend/connector.rb +233 -0
- data/lib/rtext/frontend/connector_manager.rb +81 -0
- data/lib/rtext/frontend/context.rb +56 -0
- data/lib/rtext/generic.rb +13 -0
- data/lib/rtext/instantiator.rb +30 -7
- data/lib/rtext/language.rb +54 -27
- data/lib/rtext/link_detector.rb +57 -0
- data/lib/rtext/message_helper.rb +77 -0
- data/lib/rtext/parser.rb +19 -68
- data/lib/rtext/serializer.rb +18 -3
- data/lib/rtext/service.rb +182 -118
- data/lib/rtext/tokenizer.rb +102 -0
- data/test/completer_test.rb +327 -70
- data/test/context_builder_test.rb +671 -91
- data/test/instantiator_test.rb +153 -0
- data/test/integration/backend.out +10 -0
- data/test/integration/crash_on_request_editor.rb +12 -0
- data/test/integration/ecore_editor.rb +50 -0
- data/test/integration/frontend.log +25138 -0
- data/test/integration/model/invalid_encoding.invenc +2 -0
- data/test/integration/model/test.crash_on_request +18 -0
- data/test/integration/model/test.crashing_backend +18 -0
- data/test/integration/model/test.dont_open_socket +0 -0
- data/test/integration/model/test.invalid_cmd_line +0 -0
- data/test/integration/model/test.not_in_rtext +0 -0
- data/test/integration/model/test_large_with_errors.ect3 +43523 -0
- data/test/integration/model/test_metamodel.ect +18 -0
- data/test/integration/model/test_metamodel2.ect +5 -0
- data/test/integration/model/test_metamodel_error.ect2 +3 -0
- data/test/integration/model/test_metamodel_ok.ect2 +18 -0
- data/test/integration/test.rb +684 -0
- data/test/link_detector_test.rb +276 -0
- data/test/message_helper_test.rb +118 -0
- data/test/rtext_test.rb +4 -1
- data/test/serializer_test.rb +96 -1
- data/test/tokenizer_test.rb +125 -0
- metadata +36 -10
- data/RText_Plugin_Implementation_Guide +0 -268
- data/lib/rtext_plugin/connection_manager.rb +0 -59
data/CHANGELOG
CHANGED
@@ -23,3 +23,23 @@
|
|
23
23
|
* Fixed used port detection in service
|
24
24
|
* Fixed completion option order to match order defined in lanugage
|
25
25
|
|
26
|
+
=0.5.0
|
27
|
+
|
28
|
+
* Added annotations
|
29
|
+
* Added generic value support
|
30
|
+
* Added RText frontend support (for Ruby based RText plugins and testing)
|
31
|
+
* Changed frontend/backend protocol to JSON over TCP
|
32
|
+
* Generally improved completer and context builder, fixed bugs, added tests
|
33
|
+
* Added automated frontend/backend tests
|
34
|
+
* Added example language (ECore editor)
|
35
|
+
* Added configurable backward reference attribute
|
36
|
+
* Fixed backward reference resolution in case line ends with name attribute
|
37
|
+
* Added more information to unlabled value completion options
|
38
|
+
* Added backend service support for lanugage exchange at runtime
|
39
|
+
* Extended support for relative reference calculation and resolution
|
40
|
+
* Added on_progress hook to default_loader
|
41
|
+
* Added explicit handling of encoding
|
42
|
+
* Improved backend service progress reporting
|
43
|
+
* Fixed backward reference list to not include opposites of forward references
|
44
|
+
* Fixed problem with backend service socket connection on some machines
|
45
|
+
|
data/{README → README.rdoc}
RENAMED
@@ -17,6 +17,10 @@ RText features include:
|
|
17
17
|
|
18
18
|
> gem install rtext
|
19
19
|
|
20
|
+
The RText service protocol requires the JSON gem:
|
21
|
+
|
22
|
+
> gem install json
|
23
|
+
|
20
24
|
|
21
25
|
= Documentation
|
22
26
|
|
@@ -26,4 +30,4 @@ RText features include:
|
|
26
30
|
|
27
31
|
= License
|
28
32
|
|
29
|
-
|
33
|
+
RText is released under the MIT license.
|
data/RText_Protocol
ADDED
@@ -0,0 +1,444 @@
|
|
1
|
+
= RText Protocol
|
2
|
+
|
3
|
+
RText frontend and backend pass messages containing JSON objects.
|
4
|
+
Normally the frontend invokes a backend command by means of a request message and the
|
5
|
+
backend will eventually reply with a response message.
|
6
|
+
|
7
|
+
|
8
|
+
== Encoding
|
9
|
+
|
10
|
+
As the RText protocol uses JSON, the encoding of messages is UTF-8 by definition.
|
11
|
+
However, the protocol further restricts UTF-8 to only 7-BIT-ASCII characters, which
|
12
|
+
effectively makes the protocol encoding 7-BIT-ASCII (or US-ASCII), which is valid UTF-8.
|
13
|
+
|
14
|
+
Frontends and backends should not apply any transcoding to the data found in RText files.
|
15
|
+
The reason is, that most often information about an input file's encoding is not reliable.
|
16
|
+
If the source encoding is wrong, transcoding just makes things worse: either data is
|
17
|
+
misinterpreted or information is lost due to character replacement.
|
18
|
+
|
19
|
+
Instead, data should be passed as is, or in other words: it should be interpreted as
|
20
|
+
"binary" data. Since the RText protocol is restricted to 7-BIT-ASCII, all non 7-BIT-ASCII
|
21
|
+
characters are escaped using the following pattern: Each byte with a value of 0x80 or higher
|
22
|
+
results in three 7-BIT-ASCII characters: a leading "%" and two hexadecimal figures in lower case.
|
23
|
+
In addition, the character "%" is escaped in the same way, i.e. "%" will always be escaped as
|
24
|
+
"%25" ("%" has byte value 0x25 in 7-BIT-ASCII).
|
25
|
+
|
26
|
+
Example:
|
27
|
+
|
28
|
+
The word "�bung" (german: exercise), encoded in ISO-8859-1 would result in the string:
|
29
|
+
"%dcbung" (the "�" Umlaut has a byte value of 0xdc is ISO-8859-1).
|
30
|
+
|
31
|
+
|
32
|
+
== Request Messages
|
33
|
+
|
34
|
+
Each request message contains a field ``command`` and a field ``invocation_id``.
|
35
|
+
The command field holds the name of the command to be invoked as a string.
|
36
|
+
The invocation id field holds an identifier which will be repeated by the backend's response.
|
37
|
+
|
38
|
+
{
|
39
|
+
"type": "request",
|
40
|
+
"command": <command>,
|
41
|
+
"invocation_id": <invocation_id>
|
42
|
+
...
|
43
|
+
}
|
44
|
+
|
45
|
+
== Response Messages
|
46
|
+
|
47
|
+
The response message repeats the invocation id of the request it replies to.
|
48
|
+
If the requested command is not known by the backend, it will respond with a
|
49
|
+
"unknonw_command_error" message instead (see below).
|
50
|
+
|
51
|
+
{
|
52
|
+
"type": "response",
|
53
|
+
"invocation_id": <invocation_id>
|
54
|
+
...
|
55
|
+
}
|
56
|
+
|
57
|
+
== Error Messages
|
58
|
+
|
59
|
+
There are a number of error message which may be sent in response to a request message.
|
60
|
+
|
61
|
+
=== Unknown Command Error
|
62
|
+
|
63
|
+
{
|
64
|
+
"type": "unknown_command_error",
|
65
|
+
"invocation_id": <invocation_id>,
|
66
|
+
"command": <unknown command [string]>
|
67
|
+
}
|
68
|
+
|
69
|
+
== Progress Information
|
70
|
+
|
71
|
+
Before the actual response is sent, the backend may send progress information.
|
72
|
+
This is useful whenever a command takes a longer time to complete.
|
73
|
+
Frontends should be prepared to receive progress information messages for any command.
|
74
|
+
They may however choose to ignore this information, i.e. not display any progress to the user.
|
75
|
+
|
76
|
+
{
|
77
|
+
"type": "progress",
|
78
|
+
"invocation_id": <invocation_id [integer]>,
|
79
|
+
"percentage": <percentage [integer][0..100]>,
|
80
|
+
"message": <message [string]>
|
81
|
+
}
|
82
|
+
|
83
|
+
The percentage and message fields are optional.
|
84
|
+
If percentage is present, the frontend should display a progress bar, otherwise it should just
|
85
|
+
indicate to the user that a job is ongoing.
|
86
|
+
The message field may carry information about the currently ongoing subtask.
|
87
|
+
|
88
|
+
== Commands
|
89
|
+
|
90
|
+
For each command, the layout of the request and response messages will be given below.
|
91
|
+
Note that the invocation id field is present in every request and response but is omitted for brevity.
|
92
|
+
|
93
|
+
|
94
|
+
=== Load Model
|
95
|
+
|
96
|
+
This command requests the backend to load or reload the model.
|
97
|
+
|
98
|
+
{
|
99
|
+
"type": "request",
|
100
|
+
"command": "load_model"
|
101
|
+
}
|
102
|
+
|
103
|
+
The response may indicate problems which were detected during loading.
|
104
|
+
In order to reduce the size of the response message, problems are grouped by file.
|
105
|
+
The ``total_problems`` field is used to indicate the total number of problems which may be lower
|
106
|
+
than the number actually returned. If the total number of problems is unknown, ``total_problems``
|
107
|
+
should be set to -1. This may be the case when problem detection is interrupted in order to
|
108
|
+
limit detection effort and/or response time.
|
109
|
+
|
110
|
+
{
|
111
|
+
"type": "response"
|
112
|
+
"total_problems": <number of total problems or -1 [integer]>,
|
113
|
+
"problems": [
|
114
|
+
{
|
115
|
+
"file": <fully qualifed file name [string]>,
|
116
|
+
"problems: [
|
117
|
+
{
|
118
|
+
"message": <message [string]>,
|
119
|
+
"severity": <['debug', 'info', 'warn', 'error', 'fatal']>,
|
120
|
+
"line": <line number [integer]>
|
121
|
+
}
|
122
|
+
...
|
123
|
+
]
|
124
|
+
}
|
125
|
+
...
|
126
|
+
]
|
127
|
+
}
|
128
|
+
|
129
|
+
=== Content Complete
|
130
|
+
|
131
|
+
This command is a request by the frontend in order to show content completion options at a given
|
132
|
+
location within a file. The location is expressed using a set of context lines and the cursor
|
133
|
+
column position in the current line. See section "Context Extraction" for details about how to
|
134
|
+
build the set of context lines in the frontend. Column number start at 1.
|
135
|
+
|
136
|
+
{
|
137
|
+
"type": "request",
|
138
|
+
"command": "content_complete",
|
139
|
+
"context": <context lines [array of string]>,
|
140
|
+
"column": <cursor column [integer]>
|
141
|
+
}
|
142
|
+
|
143
|
+
The backend replies with a list of completion options.
|
144
|
+
The field ``insert`` holds the text to be inserted if the completion option is chosen.
|
145
|
+
The field ``display`` contains the string which should be displayed to the user in some kind of
|
146
|
+
completion option menu. An optional description field may provide more information about a
|
147
|
+
particular option.
|
148
|
+
|
149
|
+
If there are no completion options, the backend may send an empty response.
|
150
|
+
|
151
|
+
{
|
152
|
+
"type": "response",
|
153
|
+
"options": [
|
154
|
+
{
|
155
|
+
"display": <text to display [string]>,
|
156
|
+
"insert": <text to be inserted [string]>,
|
157
|
+
"desc": <optional description [string]>
|
158
|
+
}
|
159
|
+
]
|
160
|
+
}
|
161
|
+
|
162
|
+
=== Link Targets
|
163
|
+
|
164
|
+
This command is issued by the frontend when the user tries to follow a hyperlink, e.g. following a model reference.
|
165
|
+
The frontend needs to send the context as described in section "Context Extraction" and the
|
166
|
+
column where the cursor (e.g. the mouse pointer, or an actual text cursor) is placed.
|
167
|
+
Column numbers start at 1.
|
168
|
+
|
169
|
+
{
|
170
|
+
"type": "request",
|
171
|
+
"command": "link_targets",
|
172
|
+
"context": <context lines [array of string]>,
|
173
|
+
"column": <cursor column [integer]>
|
174
|
+
}
|
175
|
+
|
176
|
+
The backend analyses the text at the cursor position in order to find out if a link is present.
|
177
|
+
If so, it sends back the columns of the beginning and the end of the link as well as the actual
|
178
|
+
link targets. Column positions are inclusive, i.e. the beginning and the end column are part of
|
179
|
+
the link. Link targets contain the string to be displayed to the user, the filename and the line
|
180
|
+
number within the file.
|
181
|
+
An optional description field may provide more information about a particular lin target.
|
182
|
+
|
183
|
+
If there is no link, the backend may send an empty response.
|
184
|
+
|
185
|
+
{
|
186
|
+
"type": "response",
|
187
|
+
"begin_column": <begin column of hyperlink [integer]>,
|
188
|
+
"end_column": <end column of hyperlink [integer]>,
|
189
|
+
"targets": [
|
190
|
+
{
|
191
|
+
"display": <display name [string]>,
|
192
|
+
"file": <fully qualified file name [string]>,
|
193
|
+
"line": <line number [integer]>,
|
194
|
+
"desc": <optional description [string]>
|
195
|
+
}
|
196
|
+
...
|
197
|
+
]
|
198
|
+
}
|
199
|
+
|
200
|
+
=== Find Elements
|
201
|
+
|
202
|
+
This command is used to search for elements within the model. The frontend should allow the user
|
203
|
+
to enter an arbitrary search pattern as a string. This search pattern is sent to the backend
|
204
|
+
which actually defines how the pattern is interpreted.
|
205
|
+
|
206
|
+
{
|
207
|
+
"type": "request",
|
208
|
+
"command": "find_elements",
|
209
|
+
"search_pattern": <search pattern [string]>
|
210
|
+
}
|
211
|
+
|
212
|
+
The backend responds by sending the total number of elements found and a list of elements
|
213
|
+
together with their file and line information.
|
214
|
+
Note that the number of elements actually returned may be lower than the total number.
|
215
|
+
If the total number of elements is unknown ``total_elements`` should be set to -1.
|
216
|
+
This allows to truncate large search result sets and still provide the user with the number of
|
217
|
+
elements which were omitted or at least with the fact that the result set has been truncated.
|
218
|
+
An optional description field my provide more information about a particular element.
|
219
|
+
|
220
|
+
If there are no search results, the backend may send an empty response.
|
221
|
+
|
222
|
+
{
|
223
|
+
"type": "response",
|
224
|
+
"total_elements": <total number of elements or -1 [string]>,
|
225
|
+
"elements": [
|
226
|
+
{
|
227
|
+
"display": <display name [string]>,
|
228
|
+
"file": <fully qualified file name [string]>,
|
229
|
+
"line": <line number [integer]>,
|
230
|
+
"desc": <optional description [string]>
|
231
|
+
}
|
232
|
+
...
|
233
|
+
]
|
234
|
+
}
|
235
|
+
|
236
|
+
=== Context Information
|
237
|
+
|
238
|
+
This command is used by the frontend to request information about a particular position in
|
239
|
+
a file. The frontend will send the current context (section "context extraction") and the cursor
|
240
|
+
column. If for example, the context information is to be shown as a hover at the position of the
|
241
|
+
mouse pointer, the context and column need to be calculated at the position of the mouse pointer,
|
242
|
+
not the position of the text cursor.
|
243
|
+
|
244
|
+
{
|
245
|
+
"type": "request",
|
246
|
+
"command": "context_info",
|
247
|
+
"context": <context lines>,
|
248
|
+
"column": <cursor column>
|
249
|
+
}
|
250
|
+
|
251
|
+
The backend reponds by sending the textual description to be shown to the user.
|
252
|
+
|
253
|
+
{
|
254
|
+
"type": "response",
|
255
|
+
"desc": <textual description>
|
256
|
+
}
|
257
|
+
|
258
|
+
=== Custom Commands
|
259
|
+
|
260
|
+
This command is used to retrieve a list of custom commands understood by the backend.
|
261
|
+
These commands may be available in a specific context only. If the frontend includes context
|
262
|
+
and column information in the request, the backend will list only commands applicable in that
|
263
|
+
context. If the context information is omitted, the backend will return all commands applicable
|
264
|
+
without any specific context.
|
265
|
+
|
266
|
+
Note that even for commands without context information, the frontend should repeat to request
|
267
|
+
the command list as the list of available commands may change. For example, the command list could
|
268
|
+
be requested everytime the user gets a chance to choose from a command menu.
|
269
|
+
|
270
|
+
{
|
271
|
+
"type": "request",
|
272
|
+
"command": "custom_commands"
|
273
|
+
"context": <context lines, optional>,
|
274
|
+
"column": <cursor column, optional>
|
275
|
+
}
|
276
|
+
|
277
|
+
The backend responds by a list of commands which may be categorized into a tree structure.
|
278
|
+
This information can be used to show the commands in a hierarchical menu, e.g. a context menu.
|
279
|
+
|
280
|
+
{
|
281
|
+
"type": "response",
|
282
|
+
"entries": [
|
283
|
+
{
|
284
|
+
"type": "category",
|
285
|
+
"name": <display name>,
|
286
|
+
"entries": [
|
287
|
+
{
|
288
|
+
"type": "command",
|
289
|
+
"name": <display name>,
|
290
|
+
"id": <command identifier>
|
291
|
+
}
|
292
|
+
...
|
293
|
+
]
|
294
|
+
}
|
295
|
+
...
|
296
|
+
]
|
297
|
+
}
|
298
|
+
|
299
|
+
=== Custom Command Invocation
|
300
|
+
|
301
|
+
Custom commands are invoked just like the predefined commands. The frontend uses the command
|
302
|
+
identifier returned by the "Custom Commands" request as the value of the ``command`` field in
|
303
|
+
the new invocation request. If the command id was returned by a "Costum Commands" request which
|
304
|
+
included context information, the frontend should send the same context information in the new
|
305
|
+
invocation request and all repeated requests (see below). If this information was not present
|
306
|
+
in command list request, it should not be send in the new command invocation.
|
307
|
+
|
308
|
+
{
|
309
|
+
"type": "request",
|
310
|
+
"command": <command id>
|
311
|
+
}
|
312
|
+
|
313
|
+
On first invocation, the frontend will not send any parameters with the command request.
|
314
|
+
Instead, the backend's response may ask for parameters to be included into the next request.
|
315
|
+
In this case, the frontend should will prompt the user to enter the required information and then
|
316
|
+
reinvoke the command with the parameters included. This process of asking for more parameters
|
317
|
+
may be repeated several times.
|
318
|
+
|
319
|
+
More generally, the backend will tell the frontend to display dialogs to the user.
|
320
|
+
Dialogs may contain input fields, output fields and hidden fields.
|
321
|
+
Fields may be marked to indicate that their value should be sent to the backend on next invocation.
|
322
|
+
|
323
|
+
The dialogs contain elements of certain types:
|
324
|
+
|
325
|
+
* text: text as a string
|
326
|
+
* text
|
327
|
+
* element_list field
|
328
|
+
* element_table field
|
329
|
+
|
330
|
+
Each dialog element has the following attributes:
|
331
|
+
|
332
|
+
* name: user visible title of the field [String]
|
333
|
+
* desc: an optional description of the field [String]
|
334
|
+
* editable: if the user can edit the field [Boolean]
|
335
|
+
* visible: if the field is visible, there may be hidden fields for internal purpose [Boolean]
|
336
|
+
* return: if the value should be sent back with the next request [Boolean]
|
337
|
+
* error: an error description, if present the field should be marked to have an error [String]
|
338
|
+
* value: the preset value of the field, type is field type specific
|
339
|
+
|
340
|
+
The frontend should keep repeating requests as long as the field ``repeat`` is set to ``true``.
|
341
|
+
|
342
|
+
{
|
343
|
+
"type": "response",
|
344
|
+
"repeat": <if command should be repeated>,
|
345
|
+
"dialog": {
|
346
|
+
"title": <dialog title>,
|
347
|
+
"desc": <description what to do>,
|
348
|
+
"elements": [
|
349
|
+
{
|
350
|
+
"type": "text_input",
|
351
|
+
"name": <parameter name>,
|
352
|
+
"value": <preset value>,
|
353
|
+
"error": <error text>
|
354
|
+
},
|
355
|
+
{
|
356
|
+
"type": "choice_input",
|
357
|
+
"num_min": <min number of elements to choose, default: 1>,
|
358
|
+
"num_max": <max mumber of elements to choose, default: 1>,
|
359
|
+
"choices": [
|
360
|
+
"display": <display name>,
|
361
|
+
"id": <identifier to be sent back>,
|
362
|
+
],
|
363
|
+
"value": [ <selected choice>, <selected choice>, ...],
|
364
|
+
"error": <error text>
|
365
|
+
},
|
366
|
+
{
|
367
|
+
"type": "element_input",
|
368
|
+
"num_min": <min number of elements to choose, default: 1>,
|
369
|
+
"num_max": <max mumber of elements to choose, default: 1>,
|
370
|
+
"choices": [
|
371
|
+
{
|
372
|
+
"display": <display name>,
|
373
|
+
"id": <identifier to be sent back>,
|
374
|
+
"file": <fully qualfied file name, optional>,
|
375
|
+
"line": <line number, optional>,
|
376
|
+
"ancestors": <parent hierarchy, optional",
|
377
|
+
}
|
378
|
+
...
|
379
|
+
],
|
380
|
+
"value": [ <selected choice>, <selected choice>, ...],
|
381
|
+
"error": <error text>
|
382
|
+
}
|
383
|
+
]
|
384
|
+
}
|
385
|
+
}
|
386
|
+
|
387
|
+
=== Stop Service
|
388
|
+
|
389
|
+
This command is normally invoked when the frontend terminates or otherwise needs to terminate
|
390
|
+
the backend service. When receiving this command, the backend should terminate.
|
391
|
+
|
392
|
+
{
|
393
|
+
"type": "request",
|
394
|
+
"command": "stop"
|
395
|
+
}
|
396
|
+
|
397
|
+
Before actually stopping, the backend should send an empty response.
|
398
|
+
|
399
|
+
{
|
400
|
+
"type": "response"
|
401
|
+
}
|
402
|
+
|
403
|
+
== Context Extraction
|
404
|
+
|
405
|
+
Context lines are lines from an RText file which contain a (context) command and all
|
406
|
+
the parent commands wrapped around it. Any sibling commands can be omitted as well as
|
407
|
+
any lines containing closing braces and brackets. The order of lines is the same as in the
|
408
|
+
RText file.
|
409
|
+
|
410
|
+
Here is an example. Consider the following RText file with the cursor in the line of "Command3"
|
411
|
+
at the time when the auto completion command is issued.
|
412
|
+
|
413
|
+
Command1 {
|
414
|
+
Command2 {
|
415
|
+
Command 3 {
|
416
|
+
Command 4
|
417
|
+
}
|
418
|
+
role1: [
|
419
|
+
Command5 <== cursor in this line
|
420
|
+
Command6
|
421
|
+
]
|
422
|
+
}
|
423
|
+
Command7
|
424
|
+
}
|
425
|
+
|
426
|
+
The context lines in this case would be the following.
|
427
|
+
|
428
|
+
Command1 {
|
429
|
+
Command2 {
|
430
|
+
role1: [
|
431
|
+
Command5
|
432
|
+
|
433
|
+
The current line is always the last of the context lines.
|
434
|
+
|
435
|
+
See RText::Frontend::Context::extract for a concise implementation of the required algorithm.
|
436
|
+
|
437
|
+
Note that all siblings of the command and parent commands have been stripped off, as well as
|
438
|
+
any closing braces or brackets.
|
439
|
+
|
440
|
+
The purpose of this special context line format is to keep the task of extracting the
|
441
|
+
context in the frontend simple and the amount of data transmitted to the backend low.
|
442
|
+
It's also a way to keep the parsing time of the context low in the backend and thus to minimize
|
443
|
+
the user noticable delay.
|
444
|
+
|