Indenting multiline values on serialization [message #1742148] |
Wed, 31 August 2016 18:36 |
Ernesto Posse Messages: 438 Registered: March 2011 |
Senior Member |
|
|
Hi. I have a language that contains some elements that are supposed to be "opaque" expressions, i.e. expressions in another language. These opaque expressions are not to be parsed by Xtext, but simply left as Strings.
An example is a state-machine DSL with actions on transitions, where those actions are in some external language, so you could write something like this
transition onTimeout from pinger_running to pinger_running
triggers on timerPort:timeout
action
{|#
std::cout << getName() << ": timeout, sending ping( 0 )" << std::endl;
pingPort.ping().send();
#|}
The text between the {|# and #|} delimiters is the external code, i.e. the opaque expression.
Originally my grammar used Strings to encode these, instead of these delimiters, but that poses problems, such as the need to escape quotes inside the expression, or the fact that if the String object is modified externally, then, when Xtext serializes it, it will write the escape characters explicitly, e.g. "cout << \"something\";\n", and that is not good for my DSL.
I managed to solve the problem, adding the {|# and #|} delimiters in my grammar as follows:
TransitionAction returns TransitionAction:
{TransitionAction}
source=ActionCodeSource
;
ActionCodeSource returns ecore::EString:
'{' ACTION_CODE_SOURCE '}'
;
terminal ACTION_CODE_SOURCE returns ecore::EString:
'|#' -> '#|'
;
The problem I have now is serialization. If an external program produces a model where the 'source' of a 'TransitionAction' is a multi-line String that has its own formatting and spacing, then on serialization, that exact formatting is preserved, but unfortunately this doesn't yield a good result, because it can lead to lines whose indentation do not match that of the context, for example, the sample code above is serialized as
transition onTimeout from pinger_running to pinger_running
triggers on timerPort:timeout
action
{|#
std::cout << getName() << ": timeout, sending ping( 0 )" << std::endl;
pingPort.ping().send();
#|}
So the question is how to serialize such a value so that it is indented according to its context.
My idea was to create a custom value converter, like this:
class ActionCodeConverterImpl implements IValueConverter<String> {
val CODE_START = "{|#"
val CODE_END = "#|}"
val NEW_LINE = "\n"
override toString(String value) throws ValueConverterException {
val rawString = value ?: ""
if (rawString.contains(NEW_LINE)) {
CODE_START + NEW_LINE + indentAllLines(rawString, ?) + CODE_END
} else {
CODE_START + rawString + CODE_END
}
}
override toValue(String string, INode node) throws ValueConverterException {
if (string !== null) {
var startIndex = 0
var endIndex = string.length
if (string.startsWith(CODE_START)) {
startIndex = CODE_START.length
}
if (string.endsWith(CODE_END)) {
endIndex = string.length - CODE_END.length
}
string.substring(startIndex, endIndex)
} else {
""
}
}
}
Indentation could be added in the "toString" method to the "rawString". The problem is that I don't know how much to indent (the ? in the indentAllLines(rawString, ?) call). We do not have the context in which the value is being serialized, as the toString method only receives the value.
Is there any way to get the context in which a value is being serialized?
Or is there another solution?
Thanks
|
|
|
|
Re: Indenting multiline values on serialization [message #1742223 is a reply to message #1742220] |
Wed, 31 August 2016 20:19 |
Ernesto Posse Messages: 438 Registered: March 2011 |
Senior Member |
|
|
Thanks Christian.
I actually tried the formatter, although I think I'm using the old one. But it doesn't seem to work.
Here's what I've tried
public class UmlrtFormatter extends AbstractDeclarativeFormatter {
@Inject extension UmlrtGrammarAccess
override protected configureFormatting(FormattingConfig c) {
c.setLinewrap(1).around(getActionCodeSourceRule)
c.setIndentationIncrement.before(getActionCodeSourceRule)
c.setIndentationDecrement.after(getActionCodeSourceRule)
c.setLinewrap(1).around(ACTION_CODE_SOURCERule)
c.setIndentationIncrement.before(ACTION_CODE_SOURCERule)
c.setIndentationDecrement.after(ACTION_CODE_SOURCERule)
for(pair: findKeywordPairs('{|#', '#|}')) {
c.setIndentation(pair.first, pair.second)
c.setLinewrap(1).around(pair.first)
c.setLinewrap(1).around(pair.second)
}
for(pair: findKeywordPairs('|#', '#|')) {
c.setIndentation(pair.first, pair.second)
c.setLinewrap(1).around(pair.first)
c.setLinewrap(1).around(pair.second)
}
for(pair: findKeywordPairs('{', '}')) {
c.setIndentation(pair.first, pair.second)
c.setLinewrap(1).around(pair.first)
c.setLinewrap(1).around(pair.second)
}
for(comma: findKeywords(',')) {
c.setNoLinewrap().before(comma)
c.setNoSpace().before(comma)
c.setLinewrap().after(comma)
}
c.setLinewrap(0, 1, 2).before(SL_COMMENTRule)
c.setLinewrap(0, 1, 2).before(ML_COMMENTRule)
c.setLinewrap(0, 1, 1).after(ML_COMMENTRule)
}
}
None of the "setIndentation*" methods seem to have any effect for the string contained inside the terminal ACTION_CODE_SOURCE.
Is there something in that formatter's API that would allow me to indent the string inside the terminal?
|
|
|
|
Powered by
FUDForum. Page generated in 0.02269 seconds