Java code completion in MPS

Hi all,

I'm new to MPS and I'm still trying to figure out how it'll help me do what I need.

I'm creating a DSL with references to java structures (hierarchy of POJOs), and I need to implement auto-completion based on the java classes.

In essence, it is like an editor of "simple xpath": I have java classes generated by JAXB from XML schema, and the DSL editor should help me navigate the data structure to define a path.

I've looked at the Shapes tutorial and saw how the Color reference could be mapped to the AWT library. My need is similar, yet different :)

  • for one, the link to the java library of structures would not be hardcoded in the language, but rather imported by the user (he'll generate his jaxb classes and then set his classpath for the DSL editor)

  • then, the editor will need to dynamically generate a scope based on the chosen structure and the current position in the path definition.

for instance:
my XML is like
<A>
  <B>
    <C/>
    <D/>
  </B>
</A>

my classes are like
class A {
  B b;
}
class B {
  C c;
  D d;
}
class C {}
class D {}

The DSL editor would have a classpath pointing to the location of the classes and provide autocompletion for a path like "A.B.C", suggesting possible values at each "." (much like typical java completion)

I'm guessing this is precisely the kind of job for MPS, yet I'm confused about the various types of references and how would one go about implementing this kind of thing.

Maybe there are existing tutorials or documentation about this?

Thanks a lot.
6 comments
Hi

First of all, you'll need to lift classes to the model level, to be able to reference them in your programs. For that we have java class 'stubs'. They are called stubs because they lift only the structure part of java classes (declarations of classes, methods, fields). So, they read class files and setup models/nodes corresponding to classes/methods/etc. The nodes are in baseLanguage (our implementation of java).

Then you'll need a construct in your language for your path expressions.
E.g.
ClassRef has reference to Classifier
Path has multiple child role ClassRef[1..n]

You provide scope for references in ClassRef that computes the list of elements in this way: take the previous ClassRef, take it target class and enumerate its nested classes.
0
Daniil,

Thanks a lot for pointing the Classifier reference, that's the kind of answer I was looking for :)

Then you're saying I should add a Constraint on ClassRef, and define the scope in the "reference constraints" section. Something like:

link {clazz}
  referent set handler:<none>
  scope:
    (exists, referenceNode, contextNode, containingLink, linkTarget, operationContext, enclosingNode, model, position, contextRole)->Scope {
      // .. the logic here based on referenceNode.clazz
    }
  presentation :
    <no presentation>
;

I'll play around with the parameters and API and see if I get somewhere.

The lifting part is still a bit abstract for me; is the Classifier the same as the stub or is it something else?
How should the user make his classes available to the editor?
0
link {clazz}
  referent set handler:<none>
  scope:
    ...
I'll play around with the parameters and API and see if I get somewhere.

Yes, exactly there

The lifting part is still a bit abstract for me; is the Classifier the same as the stub or is it something else?

Yes, I think I just expressed myself a bit vaguely. Those Classifier nodes are stub nodes. Their containing models are stub models.

How should the user make his classes available to the editor?

It's done in MPS IDE. You go to the module settings and add another 'model root'. It's a source of models. You select kind: java classes and point it to directories with classes or jar files.

So, currently it's supposed to be used at design time, when you set up your project, add dependencies, libs, etc. I'm trying to think how you should set it all up so that your users can access it at runtime... (note: users using your language is runtime for mps, while it's definitely design time for your toolchain). That's another question, depends on how your app could be bundled.
0
Hi Daniil,

I'm not sure if i'm doing this right (rather the contrary in fact!) but I right-clicked on constraints and chose "Get Model Contents From Source", then pointed to the root of some JAXB generated classes.

After a while I got this "IDE Fatal Error" :

org.jdom.IllegalDataException : The data " D" is not legal for a JDOM attribute: 0x11 is not a legal XML character.: The data " D" is not legal for a JDOM attribute: 0x11 is not a legal XML character.
org.jdom.IllegalDataException: The data "□D" is not legal for a JDOM attribute: 0x11 is not a legal XML character.
org.jdom.IllegalDataException: The data "□D" is not legal for a JDOM attribute: 0x11 is not a legal XML character.
 at org.jdom.Attribute.setValue(Attribute.java:491)
 at org.jdom.Attribute.<init>(Attribute.java:228)
 at org.jdom.Attribute.<init>(Attribute.java:251)
 at org.jdom.Element.setAttribute(Element.java:1160)
 at jetbrains.mps.smodel.persistence.def.DocUtil.setNotNullAttribute(DocUtil.java:32)
 at jetbrains.mps.smodel.persistence.def.v7.ModelWriter7.saveNode(ModelWriter7.java:146)
 at jetbrains.mps.smodel.persistence.def.v7.ModelWriter7.saveNode(ModelWriter7.java:162)
 at jetbrains.mps.smodel.persistence.def.v7.ModelWriter7.saveNode(ModelWriter7.java:162)
 at jetbrains.mps.smodel.persistence.def.v7.ModelWriter7.saveNode(ModelWriter7.java:162)
 at jetbrains.mps.smodel.persistence.def.v7.ModelWriter7.saveNode(ModelWriter7.java:162)
 at jetbrains.mps.smodel.persistence.def.v7.ModelWriter7.saveNode(ModelWriter7.java:162)
 at jetbrains.mps.smodel.persistence.def.v8.ModelWriter8.saveModelNodes(ModelWriter8.java:52)
 at jetbrains.mps.smodel.persistence.def.v7.ModelWriter7.saveModel(ModelWriter7.java:52)
 at jetbrains.mps.smodel.persistence.def.ModelPersistence.saveModel(ModelPersistence.java:308)
 at jetbrains.mps.smodel.persistence.def.ModelPersistence.saveModel(ModelPersistence.java:276)
 at jetbrains.mps.smodel.persistence.def.ModelPersistence.saveModel(ModelPersistence.java:249)
 at jetbrains.mps.smodel.DefaultSModelDescriptor.saveModel(DefaultSModelDescriptor.java:143)
 at jetbrains.mps.extapi.model.EditableSModelBase.save(EditableSModelBase.java:189)
 at jetbrains.mps.smodel.SModelRepository.saveAll(SModelRepository.java:206)
 at jetbrains.mps.smodel.MPSModuleRepository.saveAll(MPSModuleRepository.java:302)
 at jetbrains.mps.ide.save.MPSFilesSaver$1$1.run(MPSFilesSaver.java:56)
 at jetbrains.mps.ide.smodel.WorkbenchModelAccess$4.run(WorkbenchModelAccess.java:163)
 at com.intellij.openapi.application.impl.ApplicationImpl.runWriteAction(ApplicationImpl.java:984)
 at jetbrains.mps.ide.smodel.WorkbenchModelAccess.runWriteAction(WorkbenchModelAccess.java:172)
 at jetbrains.mps.ide.save.MPSFilesSaver$1.beforeAllDocumentsSaving(MPSFilesSaver.java:63)
 at sun.reflect.GeneratedMethodAccessor259.invoke(Unknown Source)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.lang.reflect.Method.invoke(Method.java:606)
 at com.intellij.util.messages.impl.MessageBusConnectionImpl.deliverMessage(MessageBusConnectionImpl.java:114)
 at com.intellij.util.messages.impl.MessageBusImpl.doPumpMessages(MessageBusImpl.java:228)
 at com.intellij.util.messages.impl.MessageBusImpl.pumpMessages(MessageBusImpl.java:219)
 at com.intellij.util.messages.impl.MessageBusImpl.sendMessage(MessageBusImpl.java:209)
 at com.intellij.util.messages.impl.MessageBusImpl.access$000(MessageBusImpl.java:43)
 at com.intellij.util.messages.impl.MessageBusImpl$1.invoke(MessageBusImpl.java:131)
 at com.sun.proxy.$Proxy20.beforeAllDocumentsSaving(Unknown Source)
 at sun.reflect.GeneratedMethodAccessor259.invoke(Unknown Source)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.lang.reflect.Method.invoke(Method.java:606)
 at com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl.multiCast(FileDocumentManagerImpl.java:127)
 at com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl.access$000(FileDocumentManagerImpl.java:87)
 at com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl$1.invoke(FileDocumentManagerImpl.java:115)
 at com.sun.proxy.$Proxy20.beforeAllDocumentsSaving(Unknown Source)
 at com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl.saveAllDocuments(FileDocumentManagerImpl.java:307)
 at com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl.saveAllDocuments(FileDocumentManagerImpl.java:298)
 at com.intellij.ide.SaveAndSyncHandlerImpl.saveProjectsAndDocuments(SaveAndSyncHandlerImpl.java:142)
 at com.intellij.ide.SaveAndSyncHandlerImpl$3.onFrameDeactivated(SaveAndSyncHandlerImpl.java:103)
 at com.intellij.ide.FrameStateManagerImpl.fireDeactivationEvent(FrameStateManagerImpl.java:104)
 at com.intellij.ide.FrameStateManagerImpl.access$500(FrameStateManagerImpl.java:34)
 at com.intellij.ide.FrameStateManagerImpl$2$1.run(FrameStateManagerImpl.java:74)
 at com.intellij.util.concurrency.QueueProcessor.runSafely(QueueProcessor.java:238)
 at com.intellij.util.Alarm$Request$1.run(Alarm.java:327)
 at com.intellij.openapi.application.impl.LaterInvocator$FlushQueue.run(LaterInvocator.java:319)
 at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:251)
 at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:733)
 at java.awt.EventQueue.access$200(EventQueue.java:103)
 at java.awt.EventQueue$3.run(EventQueue.java:694)
 at java.awt.EventQueue$3.run(EventQueue.java:692)
 at java.security.AccessController.doPrivileged(Native Method)
 at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
 at java.awt.EventQueue.dispatchEvent(EventQueue.java:703)
 at com.intellij.ide.IdeEventQueue.defaultDispatchEvent(IdeEventQueue.java:697)
 at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:524)
 at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:335)
 at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)
 at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
 at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)
 at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146)
 at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
 at java.awt.EventDispatchThread.run(EventDispatchThread.java:91)


Anyway, as you mentionned, I really need this at runtime time rather than design time.. but at least I was trying to make a "demo version" bound at design time.

wdyt?
0
Note: i do understand it's not directly related to MPS; i'm just unfortunate to have some invalid character in my JAXB classes..
0
Hi!
Hm, that doesn't look clear to me. The exception has to do with MPS trying to save a model in its default xml format... Well, maybe the illegal character, indeed, made it all the way from java source into our model, and then couldn't be saved.

Btw, you say you right-click on constraints, do you mean the constraints aspect of a language? If yes, that might not be a good idea, although it may work. You better should right-click on the module and do 'get models from sources'. That will create new models corresponding to packages you're importing.

Try that, and if you still get the same error then I could take those java sources (if it's acceptable) and see what's wrong with them or with us.
0

Please sign in to leave a comment.