Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,67 @@
*/
package com.typesafe.config.ui.contentassist

import com.typesafe.config.ui.contentassist.AbstractHoconProposalProvider
import com.typesafe.config.hocon.HObject
import com.typesafe.config.hocon.Path
import com.typesafe.config.hocon.Root
import org.eclipse.emf.ecore.EObject
import org.eclipse.xtext.Assignment
import org.eclipse.xtext.EcoreUtil2
import org.eclipse.xtext.ui.editor.contentassist.ContentAssistContext
import org.eclipse.xtext.ui.editor.contentassist.ICompletionProposalAcceptor

/**
* See https://www.eclipse.org/Xtext/documentation/304_ide_concepts.html#content-assist
* on how to customize the content assistant.
*/
class HoconProposalProvider extends AbstractHoconProposalProvider {

/**
* Return the member object that corresponds to the given path. For example,
* if the path is `foo, bar` it will walk through the members of `owner` using
* the first key that matches `foo` and that is an HObject, and then the same for
* bar.
*
* @note Using `Root` instead of `HObject` because HObject is a subtype of Root,
* and we treat Root as an object anyway
* @return null if no such path is found
*/
private def Root findMemberObject(Root owner, Iterable<String> path) {
if (path.isEmpty)
owner
else {
val tmp = owner.members.findFirst [
(it.value instanceof Root) && (path.head == it.key.elements.get(0).name)
]
if (tmp != null)
findMemberObject(tmp.value as HObject, path.tail)
else
null
}
}

override def void completePath_Elements(EObject model, Assignment assignment, ContentAssistContext context,
ICompletionProposalAcceptor acceptor) {
super.completePath_Elements(model, assignment, context, acceptor)

val owner = switch prefix: model {
Path: {
val base = EcoreUtil2.getContainerOfType(prefix, typeof(Root))
val t = prefix.elements.map[it.name].toList
findMemberObject(base, t)
}
Root:
prefix
}

val keys = owner.members.map[it.key]

val names = keys.map [
val elems = it.elements
elems.drop(1).fold(elems.head.name)[p1, p2|p1 + '.' + p2.name]
]

for (n : names)
acceptor.accept(createCompletionProposal(n, context))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,18 @@ class HoconLabelProvider extends org.eclipse.xtext.ui.label.DefaultEObjectLabelP

// Labels and icons can be computed like this:

def text(Member ele) {
if (ele.value != null
&& ele.value.value.size == 1
&& ele.value.value.get(0) instanceof EString)
ele.name.getText + ": " + ele.value.value.get(0).getText
def text(Member m) {
// concatenate the path, in case there are several elements
// we know there is at least one element (from the grammar)
val elems = m.key.elements
val name = elems.drop(1).fold(elems.head.name) [p1, p2| p1 + '.' + p2.name]

if (m.value != null
&& m.value.value.size == 1
&& m.value.value.get(0) instanceof EString)
name + ": " + m.value.value.get(0).getText
else
ele.name.getText
name
}

def text(Array l) {
Expand Down
18 changes: 12 additions & 6 deletions com.typesafe.hocon/src/com/typesafe/config/Hocon.xtext
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,28 @@ import "http://www.eclipse.org/emf/2002/Ecore" as ecore

generate hocon "http://www.typesafe.com/config/Hocon"

// here it would be good to find a way to
// create a synthetic HObject around the members...
Root:
NL*
( Object
( HObject
| ( members += Member (',' | NL) NL* )*
)
NL*
;

Object:
HObject:
'{' NL*
( members+=Member (',' | NL) NL* )*
( members += Member (',' | NL) NL* )*
'}';


Member:
name = StringLiteral (':' NL* | '=' NL*)? value=Literal
key = Path (':' NL* | '=' NL*)? value = Literal
;

Literal:
(value += SimpleLiteral)+ | Object | Array ;
(value += SimpleLiteral)+ | HObject | Array ;

SimpleLiteral returns EString:
(StringLiteral | name = Boolean | Null | NumberLiteral)
Expand All @@ -46,11 +48,15 @@ Boolean:
Null:
{Null} 'null';

Path:
elements += StringLiteral ( '.' elements += StringLiteral)*
;

StringLiteral returns EString:
(name = STRING | name = UNQUOTED_STRING)
;


NumberLiteral:
value = Number
;
Expand All @@ -62,7 +68,7 @@ terminal UNQUOTED_STRING:
( !(' ' | '\n' |'\t' | '\\' | '"' | '$' |
'{' | '}' | '[' | ']' | ':' | '=' |
'|' | '+' | '#' | '`' | '^' | '?' |
'!' | '@' | '*' | '&' | '//'| ','
'!' | '@' | '*' | '&' | '//'| ',' | '.'
) )+
;

Expand Down