rtext 0.4.0 → 0.5.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.
- 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
|
+
|