diff --git a/404.html b/404.html index f854ac3c..4cbd37be 100644 --- a/404.html +++ b/404.html @@ -16,7 +16,7 @@ - + @@ -24,13 +24,14 @@ - + - + + @@ -40,7 +41,7 @@ - + @@ -90,9 +91,6 @@
The editor shows that a method is virtual when isVirtual() is true. As a result, overriding methods of virtual methods are not necessarily having isVirtual=true, but will still display virtual.
isVirtual()
isVirtual=true
The rationale behind this may be that if one method overrides another one, both must be virtual. This information is needed so that the runtime will find out which of the two implementations to call. So we may read the isVirtual more precisely as "is explicitly virtual", and isVirtual() as "is effectively virtual".
isVirtual
myNode.detach
How can I ensure that all created nodes will have a non-null HTMLDescription? @@ -1872,7 +1994,7 @@ Initialization -Can you access the parent in the constructor? +Can you access the parent in the constructor? No, it doesn't work because the node is not attached yet (Concept constructors | MPS). Use a node factory ⧉ to access the parent instead. @@ -1893,7 +2015,7 @@ Initializationconcept/MyConcept/.new initialized instance() provides this behavior. +concept/MyConcept/.new initialized instance() provides this behavior. Bugs/Missing Features¶ Behavior methods can only partially be compared to ordinary Java methods. They don't support as many feature because the @@ -1916,16 +2038,33 @@ Bugs/Missing FeaturesMPS-22306 ⧉). - - - + + + + + + + + + + + + + - + + + + @@ -2046,13 +2185,14 @@ Comments + - - - Back to top - + + + Back to top + @@ -2063,7 +2203,7 @@ Comments - + answered by: @kbirken @@ -1864,11 +1994,11 @@ Scopes< I have a list of nodes and want to wrap them into a scope. How do you do that?
How can I ensure that all created nodes will have a non-null HTMLDescription?
HTMLDescription
Can you access the parent in the constructor?
No, it doesn't work because the node is not attached yet (Concept constructors | MPS). Use a node factory ⧉ to access the parent instead.
concept/MyConcept/.new initialized instance() provides this behavior.
concept/MyConcept/.new initialized instance()
Behavior methods can only partially be compared to ordinary Java methods. They don't support as many feature because the @@ -1916,16 +2038,33 @@
answered by: @kbirken
I have a list of nodes and want to wrap them into a scope. How do you do that?
You're probably looking for ListScope.forResolvableElements(sequence<node<>> elements).
ListScope.forResolvableElements(sequence<node<>> elements)
forResolvableElements -creates list scopes and also implements getName(child) like forNamedElements, yet returning the resolveInfo if the node is an IResolveInfo, the name if it is an INamedConcept ⧉ or else calls getPresentation().
getPresentation()
forNamedElements
I'm unsure about the use-case of ListScope.forNamedElements(sequence<node<>> elements), but it behaves unexpected in some cases:
ListScope.forNamedElements(sequence<node<>> elements)
Note that elements with a blank name are not part of the scope created by ListScope.forNamedElements(sequence<node<>> elements). They'll get silently omitted.
Note that elements that are not an instance of INamedConcept ⧉, will make forNamedElements throw an exception.
contributed by: @abstraktor
contributed by: @enikao
The second approach performs better because the highlighter runs asynchronously, not during the editor rebuild.
How can you set editor hints?
editorContext.getEditorComponent().getUpdater()
factory method(editorContext, node, cell)->EditorCell { - node<> n = node; - editorContext.getEditorComponent().getUpdater().addExplicitEditorHintsForNode(n.parent/.getReference(), concept editor hint/HintA/); - cell -} +5
factory method(editorContext, node, cell)->EditorCell { + node<> n = node; + editorContext.getEditorComponent().getUpdater().addExplicitEditorHintsForNode(n.parent/.getReference(), concept editor hint/HintA/); + cell +}
How can you open the inspector programmatically?
1
editorContext.openInspector() +1editorContext.openInspector() Opening Inspector automatically ⧉ (Specific Languages' blog) @@ -2183,7 +2325,7 @@ Open APIThis section contains answers to code-related questions. How can you open the editor for a node programmatically? -1NavigationSupport.getInstance().openNode() +1NavigationSupport.getInstance().openNode() @@ -2198,7 +2340,7 @@ Open API How can you get the font of an editor? -1jetbrains.mps.nodeEditor.EditorSettings#getDefaultEditorFont() +1jetbrains.mps.nodeEditor.EditorSettings#getDefaultEditorFont() @@ -2209,12 +2351,12 @@ Open API How do you get the text of an EditorCell programmatically? -1EditorCell.renderText().getText() +1EditorCell.renderText().getText() How can you find out if an editor cell is read-only? -1ReadOnlyUtil.isCellsReadOnlyInEditor(this.editorComponent, new singleton<EditorCell>(editorCell)) +1ReadOnlyUtil.isCellsReadOnlyInEditor(this.editorComponent, new singleton<EditorCell>(editorCell)) @@ -2222,8 +2364,8 @@ Open API1 2 3HeadlessEditorComponent component = new HeadlessEditorComponent(#project.getRepository()); -component.editNode(node); -return component; +component.editNode(node); +return component; @@ -2232,25 +2374,25 @@ Open API2 3 4 -5button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - Project mpsProject = UiUtils.getMpsProjectFromActionEvent(e); - } -} +5button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + Project mpsProject = UiUtils.getMpsProjectFromActionEvent(e); + } +} How do you get the selection/selected node in the editor? -1editorComponent.getSelectionManager().getSelection() / editorContext.getSelectedNode() +1editorComponent.getSelectionManager().getSelection() / editorContext.getSelectedNode() How can you select a node in the editor? 1 2 -3editorContext.getSelectionManager().setSelection/pushSelection -// or -editorContext.getNodeEditorComponent().selectNode() +3editorContext.getSelectionManager().setSelection/pushSelection +// or +editorContext.getNodeEditorComponent().selectNode() @@ -2262,10 +2404,10 @@ Open API1 2 3 -4 EditorCell_Collection.fold()/unfold() +4 EditorCell_Collection.fold()/unfold() -// You can check if it is foldable by calling: -EditorCell_Collection.isFoldable() +// You can check if it is foldable by calling: +EditorCell_Collection.isFoldable() @@ -2286,24 +2428,41 @@ Open API1 2 3 -4public void mark(SNode node, Color color, String messageText, EditorMessageOwner owner) { -if (node == null) return; -mark(new DefaultEditorMessage(node, color, messageText, owner)); -} +4public void mark(SNode node, Color color, String messageText, EditorMessageOwner owner) { +if (node == null) return; +mark(new DefaultEditorMessage(node, color, messageText, owner)); +} You must initialize your message object and supply it to the mark method. - - - + + + + + + + + + + + + + - + + + + @@ -2424,13 +2583,14 @@ Comments + - - - Back to top - + + + Back to top + @@ -2441,7 +2601,7 @@ Comments - + - - - Back to top - + + + Back to top + @@ -1878,7 +2010,7 @@ Comments - + new GenStatusUpdater(#project).getStatusManager().addGenerationStatusListener(new ModelGenerationStatusListener() { - void generatedFilesChanged(Collection<SModel> models) { - // do something - } -); +5new GenStatusUpdater(#project).getStatusManager().addGenerationStatusListener(new ModelGenerationStatusListener() { + void generatedFilesChanged(Collection<SModel> models) { + // do something + } +); @@ -2202,16 +2332,33 @@ Blog PostsIntroduction MPS Generators ⧉ (F1RE's blog) - - - + + + + + + + + + + + + + - + + + + @@ -2332,13 +2479,14 @@ Comments + - - - Back to top - + + + Back to top + @@ -2349,7 +2497,7 @@ Comments - + - - - Back to top - + + + Back to top + @@ -2042,7 +2182,7 @@ Comments - + Deleting the ball has left some dangling references _Show_, _Ignore_ -When clicking show, it will open a usage search for the deleted node using ConsoleUtil.show. This functionality is implemented with a model listener in a quite generic fashion. +When clicking show, it will open a usage search for the deleted node using ConsoleUtil.show. This functionality is implemented with a model listener in a quite generic fashion. Here are a few insights: Ensure not to show the node in the searched for part of the window. VCS will try to highlight it and run into an NPE (MPS 2019.3) @@ -1952,12 +2082,12 @@ Open API How can you open an MPS Editor Tab programmatically? -1EditorContext.getEditorPanelManager().openEditor(node); +1EditorContext.getEditorPanelManager().openEditor(node); Is it possible to associate an MPS-Aspect with more than one node? -1IConceptAspect.getBaseConceptCollection(); +1IConceptAspect.getBaseConceptCollection(); Troubleshooting¶ @@ -1972,22 +2102,39 @@ Troubleshooting The action isn't visible. You have to add it to an ActionGroup that can be attached to different places, such as the context or build menu. -All action context parameters marked as required must be available isApplicable block must +All action context parameters marked as required must be available isApplicable block must return true for an action to be visible. For debugging purposes, you can set always visible to true to check if your action is registered. You must have a standalone descriptor in your model. Set the solution kind to Other in the Java tab of the solution properties (Plugin | MPS ⧉). - - - + + + + + + + + + + + + + - + + + + @@ -2108,13 +2255,14 @@ Comments + - - - Back to top - + + + Back to top + @@ -2125,7 +2273,7 @@ Comments - + - - - Back to top - + + + Back to top + @@ -2012,7 +2148,7 @@ Comments - + - - - Back to top - + + + Back to top + @@ -1889,7 +2021,7 @@ Comments - + - - - Back to top - + + + Back to top + @@ -1874,7 +2006,7 @@ Comments - + @@ -1917,13 +2047,13 @@ 1 2 3((SReference)node.getReference(link)).getResolveInfo()` -// or -node.reference<ref>.resolveInfo +// or +node.reference<ref>.resolveInfo Can you use the resolve info to fix a broken reference? -1ResolverComponent.getInstance().resolve(...) +1ResolverComponent.getInstance().resolve(...) @@ -1931,8 +2061,8 @@ 1 2 3#instances<scope = global>(BaseConcept).where({~it => - it.references.any({~it => it.target == null; }); -}) + it.references.any({~it => it.target == null; }); +}) @@ -1941,16 +2071,33 @@ MPS deletes the name but doesn't remove the reference. When the cardinality is [0..1], it deletes the reference. You expect this behavior because the reference is mandatory in the first case; in the second case, it is optional. - - - + + + + + + + + + + + + + - + + + + @@ -2071,13 +2218,14 @@ Comments + - - - Back to top - + + + Back to top + @@ -2088,7 +2236,7 @@ Comments - + // true if running tests from inside the MPS process or from the command line -jetbrains.mps.RuntimeFlags.getTestMode().isInsideTestEnvironment() +5// true if running tests from inside the MPS process or from the command line +jetbrains.mps.RuntimeFlags.getTestMode().isInsideTestEnvironment() -// true if running tests from the command line only -jetbrains.mps.RuntimeFlags.isTestMode() +// true if running tests from the command line only +jetbrains.mps.RuntimeFlags.isTestMode() contributed by: @abstraktor @@ -1863,16 +1989,16 @@ GeneralMPS also isolates single tests within the same test case. For that, the check nodes are copied once for each test. Each test may then act on its copy. 1 -2// there is one data node in the model for each test -assert 2 equals data.model.nodes(Data).size +2// there is one data node in the model for each test +assert 2 equals data.model.nodes(Data).size All tests of a test case share their referenced nodes To save memory, these check nodes all lie in the same model for each test case. References to other nodes outside the test case will only need to be copied once and shared by all tests of that test case. As a result, the IDs of check nodes change, and non-check-node IDs are the same as in the original model. 1 -2// nodes in this model that are not check-nodes of this test case will only be there once -assert 1 equals dataRef.model.nodes(Chunk).size; +2// nodes in this model that are not check-nodes of this test case will only be there once +assert 1 equals dataRef.model.nodes(Chunk).size; Consequently, multiple tests of the same test case are only partially isolated. In the following example, both tests assert and do the same, yet test3 passes while test4 fails. The data element is now located in a separate chunk outside the test case, and the check node references it. As a result, test4 is running red because test3 already modified the referenced node. @@ -1935,7 +2061,7 @@ How to Test10 11 12 -13//case running from .jar +13//case running from .jar if (module instanceof AbstractModule && ((AbstractModule) module).getModuleSourceDir() != null) { AbstractModule s = ((AbstractModule) module); if (s.isPackaged() && s.getModuleSourceDir().getBundleHome() != null) { @@ -1944,10 +2070,10 @@ How to Test return bundleHome.getFileSystem().getFile(bundleHome.getPath() + "!" + path); } -// case: running from sources +// case: running from sources IFile relativePath = s.getModuleSourceDir().getDescendant(path); return relativePath; -} +} The easier solution is to place the tests in a separate solution and then invoke the make process for the solution that contains your input programmatically, so you can assert over the output. An example implementation of how the make process is invoked can be found in the mbeddr-c part ⧉. answered by: @coolya @@ -1973,13 +2099,13 @@ How to Test13 14 15 -16// Context assistants take some time to pop up, otherwise getActiveAssistant returns null +16// Context assistants take some time to pop up, otherwise getActiveAssistant returns null Thread.sleep(3000); ContextAssistantManager contextAssistantManager = editor component.getEditorContext().getContextAssistantManager(); final ContextAssistantController controller = ((ContextAssistantController) contextAssistantManager.getActiveAssistant()); foreach menuItem in contextAssistantManager.getActiveMenuItems() { if ("Item Name" :eq: ((ActionItemBase) menuItem).getLabelText("")) { - // Execute it in a BaseEditorTestBody context + // Execute it in a BaseEditorTestBody context final Project project = _this.[Project] <no instance>.getProject(); _this.[void] <no instance>.runUndoableInEDTAndWait(new Runnable() { @Override @@ -1988,7 +2114,7 @@ How to Test } }); } -} +} Note: - "Item Name" must be replaced with the name of the item as shown in the editor @@ -2023,10 +2149,10 @@ How to Test1 2 3 -4SubstituteInfo si = (editor component.getSelectedCell()).getSubstituteInfo(); -list<SubstituteAction> actions = si.getMatchingActions("", false); -assert actions.size == 4; -assert actions.any({ it => it.getMatchingText().equals("something"); }); +4SubstituteInfo si = (editor component.getSelectedCell()).getSubstituteInfo(); +list<SubstituteAction> actions = si.getMatchingActions("", false); +assert actions.size == 4; +assert actions.any({ it => it.getMatchingText().equals("something"); }); @@ -2042,26 +2168,26 @@ How to Testeditor component.getEditorContext().getRepository().getModelAccess().runReadAction({ => assert true DeletionApproverUtil.isApprovedForDeletion(editor component.getEditorContext(), node) ; }); invoke action -> Delete assert true DeletionApproverUtil.isApprovedForDeletion(editor component.getEditorContext(), editor component.getSelectedNode()) ; -}, true) +}, true) How do you test with typing over existing text enabled? Example: -1EditorTestUtil.runWithTypeOverExistingText({ => type " final" }, false) +1EditorTestUtil.runWithTypeOverExistingText({ => type " final" }, false) How do you test that you use a language? Example: -1UsedLanguagesUtils.assertLanguageUsed(editor component, language/jetbrains.mps.lang.editor.menus.extras.testLanguage/) +1UsedLanguagesUtils.assertLanguageUsed(editor component, language/jetbrains.mps.lang.editor.menus.extras.testLanguage/) How do you access the error cells in the inspector? 1 -2EditorComponent inspector = project.getComponent(InspectorTool.class).getInspector(); -Set<EditorCell> errorCells = inspector.getCellTracker().getErrorCells(); +2EditorComponent inspector = project.getComponent(InspectorTool.class).getInspector(); +Set<EditorCell> errorCells = inspector.getCellTracker().getErrorCells(); @@ -2072,7 +2198,7 @@ How to Test How can you test that code completion works? You can use a scope test to check if all items are visible in the menu. To check the number of actions in the menu, call: -1assert true editor component.getNodeSubstituteChooser().isVisible() && editor component.getNodeSubstituteChooser().getNumberOfActions() == n; +1assert true editor component.getNodeSubstituteChooser().isVisible() && editor component.getNodeSubstituteChooser().getNumberOfActions() == n; @@ -2080,9 +2206,9 @@ How to TestExample code: 1 2 -3IChecker<SNode, NodeReportItem> structureChecker = new StructureChecker(); -IAbstractChecker<ModelCheckerBuilder.ItemsToCheck, IssueKindReportItem> checker = new ModelCheckerBuilder(false).createChecker(new arraylist<IChecker<?, ? extends IssueKindReportItem>>{structureChecker, new SuppressErrorsChecker()}); -checker.check(ModelCheckerBuilder.ItemsToCheck.forSingleModel(modelToCheck), modelToCheck/.getRepository(), new CollectConsumer<IssueKindReportItem>(), new EmptyProgressMonitor()); +3IChecker<SNode, NodeReportItem> structureChecker = new StructureChecker(); +IAbstractChecker<ModelCheckerBuilder.ItemsToCheck, IssueKindReportItem> checker = new ModelCheckerBuilder(false).createChecker(new arraylist<IChecker<?, ? extends IssueKindReportItem>>{structureChecker, new SuppressErrorsChecker()}); +checker.check(ModelCheckerBuilder.ItemsToCheck.forSingleModel(modelToCheck), modelToCheck/.getRepository(), new CollectConsumer<IssueKindReportItem>(), new EmptyProgressMonitor()); @@ -2092,9 +2218,9 @@ How to Test2 3 4command process<jUnit> process = jUnit(project = project,tests = allTests, - virtualMachineParameter = vmParams - workingDirectory = workingDir); -int exitcode = process.startAndWait(TimeUnit.MINUTES.toMillis(1)); + virtualMachineParameter = vmParams + workingDirectory = workingDir); +int exitcode = process.startAndWait(TimeUnit.MINUTES.toMillis(1)); @@ -2102,9 +2228,9 @@ How to TestFor internal tests in MPS, there is the following code: 1 2 -3list<TransformationMenuItem> items = MenuLoadingUtils.loadNamedMenu(editor component, node-ptr/WithExecutableAction/, "test location"); -ActionItem item = (ActionItem) items.get(0); -item.execute(""); +3list<TransformationMenuItem> items = MenuLoadingUtils.loadNamedMenu(editor component, node-ptr/WithExecutableAction/, "test location"); +ActionItem item = (ActionItem) items.get(0); +item.execute(""); MenuLoadingUtils.java and WithExecutableAction ⧉ are not public, so they have to be created manually. @@ -2129,22 +2255,22 @@ How to Test15 16 17@MPSLaunch -test case Test extends EnvironmentAwareTestCase { - <<members>> +test case Test extends EnvironmentAwareTestCase { + <<members>> - <<before test>> + <<before test>> - <<after test>> + <<after test>> - test test { - read action with MPSModuleRepository.getInstance() { - foreach module in MPSModuleRepository.getInstance().getModules() { - System.out.println(module.getModuleName()); - } - } - } -} + test test { + read action with MPSModuleRepository.getInstance() { + foreach module in MPSModuleRepository.getInstance().getModules() { + System.out.println(module.getModuleName()); + } + } + } +} Troubleshooting¶ @@ -2160,7 +2286,7 @@ Troubleshooting -Tests aren't running at all +Tests aren't running at all A test info node ⧉ has to be added to the model of the tests so that the tests can find the project's path. The project path also has to be set in this node. Ensure you create variables in this path in Preferences → Appearance & Behavior → Path Variables @@ -2176,7 +2302,7 @@ TroubleshootingWhy does my test fail when run from Ant but not when run from MPS? ⧉ (Specific Languages' blog) -Why does the test execution fail with "Test project '$…' is not opened. Aborted"? +Why does the test execution fail with "Test project '$…' is not opened. Aborted"? It happens because you didn't set the variable in the TestInfo. Go to File → Settings → Path Variables and create an entry for your variable with a path to the project location on your hard drive. @@ -2185,16 +2311,33 @@ TroubleshootingEDT thread or the dialog is not modal. - - - + + + + + + + + + + + + + - + + + + @@ -2315,13 +2458,14 @@ Comments + - - - Back to top - + + + Back to top + @@ -2332,7 +2476,7 @@ Comments - + public TextGenDescriptor getDescriptor(@NotNull SAbstractConcept concept) { - switch (myIndex.index(concept)) { - case LanguageConceptSwitch.Component: - return new Component_TextGen(); - case LanguageConceptSwitch.Contract: - return new Contract_TextGen(); - case LanguageConceptSwitch.EmptyContent: - return new EmptyContent_TextGen(); - case LanguageConceptSwitch.Library: - return new Library_TextGen(); - case LanguageConceptSwitch.Port: - return new Port_TextGen(); +12public TextGenDescriptor getDescriptor(@NotNull SAbstractConcept concept) { + switch (myIndex.index(concept)) { + case LanguageConceptSwitch.Component: + return new Component_TextGen(); + case LanguageConceptSwitch.Contract: + return new Contract_TextGen(); + case LanguageConceptSwitch.EmptyContent: + return new EmptyContent_TextGen(); + case LanguageConceptSwitch.Library: + return new Library_TextGen(); + case LanguageConceptSwitch.Port: + return new Port_TextGen(); Is it not possible to "override the textgen" this way? @@ -1780,16 +1894,33 @@ - + + + + + + + + + + + + + - + + + + @@ -1912,13 +2043,14 @@ Comments + - - - Back to top - + + + Back to top + @@ -1931,7 +2063,7 @@ Comments - + - - - Back to top - + + + Back to top + @@ -1987,7 +2123,7 @@ Comments - + inference rule typeof_Member { - applicable for concept = Member as member - applicable always - overrides false +9inference rule typeof_Member { + applicable for concept = Member as member + applicable always + overrides false - do { - typeof(member) :==: member.type.copy; - } -} + do { + typeof(member) :==: member.type.copy; + } +} Copy the type only if you want to use it in another type as a child. In the example, it should be fine to use member.type. But let's say you have a SetType concept in your language that contains an innerType as a child. If you want to construct an instance of that concept, write code like this: 1 2 -3node<SetType> result = new node<SetType>; -result.innerType = member.type.copy; -typeof(member) :==: result +3node<SetType> result = new node<SetType>; +result.innerType = member.type.copy; +typeof(member) :==: result If you omit the .copy in the code, you will attempt to "hijack" the member.type node from the member and break the model. MPS will complain. contributed by: @sergej-koscejev -How do you suppress errors? +How do you suppress errors? I have a piece of embedded demonstration code and don't want it to show warnings (e.g., on unused variables). How can I do that? @@ -1799,9 +1913,9 @@ 1 2 -3> nodeRef@50283.ancestors<concept = ISuppressErrors>.select({~it => [it, it.suppress(nodeRef@50283)]; }); +3> nodeRef@50283.ancestors<concept = ISuppressErrors>.select({~it => [it, it.suppress(nodeRef@50283)]; }); -[[dummy, false], [<no name>[LiteralProgramFragment]: dummy():void, false], [Demo1, true]] +[[dummy, false], [<no name>[LiteralProgramFragment]: dummy():void, false], [Demo1, true]] The result is the path from that node to the top and will tell you which node is suppressing that error. The concept IAntisuppressErrors ⧉ also plays a role there, although it is deprecated (implement the suppress method instead). @@ -1816,13 +1930,13 @@ 4 5 6 -7
editorContext.openInspector()
How can you open the editor for a node programmatically?
NavigationSupport.getInstance().openNode() +1NavigationSupport.getInstance().openNode()
NavigationSupport.getInstance().openNode()
How can you get the font of an editor?
jetbrains.mps.nodeEditor.EditorSettings#getDefaultEditorFont() +1jetbrains.mps.nodeEditor.EditorSettings#getDefaultEditorFont()
jetbrains.mps.nodeEditor.EditorSettings#getDefaultEditorFont()
How do you get the text of an EditorCell programmatically?
EditorCell.renderText().getText() +1EditorCell.renderText().getText()
EditorCell.renderText().getText()
How can you find out if an editor cell is read-only?
ReadOnlyUtil.isCellsReadOnlyInEditor(this.editorComponent, new singleton<EditorCell>(editorCell)) +1ReadOnlyUtil.isCellsReadOnlyInEditor(this.editorComponent, new singleton<EditorCell>(editorCell))
ReadOnlyUtil.isCellsReadOnlyInEditor(this.editorComponent, new singleton<EditorCell>(editorCell))
1 2 3
HeadlessEditorComponent component = new HeadlessEditorComponent(#project.getRepository()); -component.editNode(node); -return component; +component.editNode(node); +return component;
button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - Project mpsProject = UiUtils.getMpsProjectFromActionEvent(e); - } -} +5
button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + Project mpsProject = UiUtils.getMpsProjectFromActionEvent(e); + } +}
How do you get the selection/selected node in the editor?
editorComponent.getSelectionManager().getSelection() / editorContext.getSelectedNode() +1editorComponent.getSelectionManager().getSelection() / editorContext.getSelectedNode()
editorComponent.getSelectionManager().getSelection() / editorContext.getSelectedNode()
How can you select a node in the editor?
1 2 -3
editorContext.getSelectionManager().setSelection/pushSelection -// or -editorContext.getNodeEditorComponent().selectNode() +3
editorContext.getSelectionManager().setSelection/pushSelection +// or +editorContext.getNodeEditorComponent().selectNode()
1 2 3 -4
EditorCell_Collection.fold()/unfold() +4
EditorCell_Collection.fold()/unfold() -// You can check if it is foldable by calling: -EditorCell_Collection.isFoldable() +// You can check if it is foldable by calling: +EditorCell_Collection.isFoldable()
public void mark(SNode node, Color color, String messageText, EditorMessageOwner owner) { -if (node == null) return; -mark(new DefaultEditorMessage(node, color, messageText, owner)); -} +4
public void mark(SNode node, Color color, String messageText, EditorMessageOwner owner) { +if (node == null) return; +mark(new DefaultEditorMessage(node, color, messageText, owner)); +}
You must initialize your message object and supply it to the mark method.
new GenStatusUpdater(#project).getStatusManager().addGenerationStatusListener(new ModelGenerationStatusListener() { - void generatedFilesChanged(Collection<SModel> models) { - // do something - } -); +5
new GenStatusUpdater(#project).getStatusManager().addGenerationStatusListener(new ModelGenerationStatusListener() { + void generatedFilesChanged(Collection<SModel> models) { + // do something + } +);
Deleting the ball has left some dangling references _Show_, _Ignore_
When clicking show, it will open a usage search for the deleted node using ConsoleUtil.show. This functionality is implemented with a model listener in a quite generic fashion.
ConsoleUtil.show
Here are a few insights:
How can you open an MPS Editor Tab programmatically?
EditorContext.getEditorPanelManager().openEditor(node); +1EditorContext.getEditorPanelManager().openEditor(node);
EditorContext.getEditorPanelManager().openEditor(node);
Is it possible to associate an MPS-Aspect with more than one node?
IConceptAspect.getBaseConceptCollection(); +1IConceptAspect.getBaseConceptCollection();
IConceptAspect.getBaseConceptCollection();
The action isn't visible.
You have to add it to an ActionGroup that can be attached to different places, such as the context or build menu. -All action context parameters marked as required must be available isApplicable block must +All action context parameters marked as required must be available isApplicable block must return true for an action to be visible. For debugging purposes, you can set always visible to true to check if your action is registered.
ActionGroup
isApplicable
You must have a standalone descriptor in your model. Set the solution kind to Other in the Java tab of the solution properties (Plugin | MPS ⧉).
((SReference)node.getReference(link)).getResolveInfo()` -// or -node.reference<ref>.resolveInfo +// or +node.reference<ref>.resolveInfo
Can you use the resolve info to fix a broken reference?
ResolverComponent.getInstance().resolve(...) +1ResolverComponent.getInstance().resolve(...)
ResolverComponent.getInstance().resolve(...)
#instances<scope = global>(BaseConcept).where({~it => - it.references.any({~it => it.target == null; }); -}) + it.references.any({~it => it.target == null; }); +})
// true if running tests from inside the MPS process or from the command line -jetbrains.mps.RuntimeFlags.getTestMode().isInsideTestEnvironment() +5
// true if running tests from inside the MPS process or from the command line +jetbrains.mps.RuntimeFlags.getTestMode().isInsideTestEnvironment() -// true if running tests from the command line only -jetbrains.mps.RuntimeFlags.isTestMode() +// true if running tests from the command line only +jetbrains.mps.RuntimeFlags.isTestMode()
1 -2
// there is one data node in the model for each test -assert 2 equals data.model.nodes(Data).size +2
// there is one data node in the model for each test +assert 2 equals data.model.nodes(Data).size
To save memory, these check nodes all lie in the same model for each test case. References to other nodes outside the test case will only need to be copied once and shared by all tests of that test case. As a result, the IDs of check nodes change, and non-check-node IDs are the same as in the original model.
// nodes in this model that are not check-nodes of this test case will only be there once -assert 1 equals dataRef.model.nodes(Chunk).size; +2
// nodes in this model that are not check-nodes of this test case will only be there once +assert 1 equals dataRef.model.nodes(Chunk).size;
Consequently, multiple tests of the same test case are only partially isolated. In the following example, both tests assert and do the same, yet test3 passes while test4 fails. The data element is now located in a separate chunk outside the test case, and the check node references it. As a result, test4 is running red because test3 already modified the referenced node.
//case running from .jar +13
//case running from .jar if (module instanceof AbstractModule && ((AbstractModule) module).getModuleSourceDir() != null) { AbstractModule s = ((AbstractModule) module); if (s.isPackaged() && s.getModuleSourceDir().getBundleHome() != null) { @@ -1944,10 +2070,10 @@ How to Test return bundleHome.getFileSystem().getFile(bundleHome.getPath() + "!" + path); } -// case: running from sources +// case: running from sources IFile relativePath = s.getModuleSourceDir().getDescendant(path); return relativePath; -} +}
The easier solution is to place the tests in a separate solution and then invoke the make process for the solution that contains your input programmatically, so you can assert over the output. An example implementation of how the make process is invoked can be found in the mbeddr-c part ⧉.
answered by: @coolya
// Context assistants take some time to pop up, otherwise getActiveAssistant returns null +16
// Context assistants take some time to pop up, otherwise getActiveAssistant returns null Thread.sleep(3000); ContextAssistantManager contextAssistantManager = editor component.getEditorContext().getContextAssistantManager(); final ContextAssistantController controller = ((ContextAssistantController) contextAssistantManager.getActiveAssistant()); foreach menuItem in contextAssistantManager.getActiveMenuItems() { if ("Item Name" :eq: ((ActionItemBase) menuItem).getLabelText("")) { - // Execute it in a BaseEditorTestBody context + // Execute it in a BaseEditorTestBody context final Project project = _this.[Project] <no instance>.getProject(); _this.[void] <no instance>.runUndoableInEDTAndWait(new Runnable() { @Override @@ -1988,7 +2114,7 @@ How to Test } }); } -} +}
Note: - "Item Name" must be replaced with the name of the item as shown in the editor @@ -2023,10 +2149,10 @@
SubstituteInfo si = (editor component.getSelectedCell()).getSubstituteInfo(); -list<SubstituteAction> actions = si.getMatchingActions("", false); -assert actions.size == 4; -assert actions.any({ it => it.getMatchingText().equals("something"); }); +4
SubstituteInfo si = (editor component.getSelectedCell()).getSubstituteInfo(); +list<SubstituteAction> actions = si.getMatchingActions("", false); +assert actions.size == 4; +assert actions.any({ it => it.getMatchingText().equals("something"); });
How do you test with typing over existing text enabled?
Example:
EditorTestUtil.runWithTypeOverExistingText({ => type " final" }, false) +1EditorTestUtil.runWithTypeOverExistingText({ => type " final" }, false)
EditorTestUtil.runWithTypeOverExistingText({ => type " final" }, false)
How do you test that you use a language?
UsedLanguagesUtils.assertLanguageUsed(editor component, language/jetbrains.mps.lang.editor.menus.extras.testLanguage/) +1UsedLanguagesUtils.assertLanguageUsed(editor component, language/jetbrains.mps.lang.editor.menus.extras.testLanguage/)
UsedLanguagesUtils.assertLanguageUsed(editor component, language/jetbrains.mps.lang.editor.menus.extras.testLanguage/)
How do you access the error cells in the inspector?
EditorComponent inspector = project.getComponent(InspectorTool.class).getInspector(); -Set<EditorCell> errorCells = inspector.getCellTracker().getErrorCells(); +2
EditorComponent inspector = project.getComponent(InspectorTool.class).getInspector(); +Set<EditorCell> errorCells = inspector.getCellTracker().getErrorCells();
How can you test that code completion works?
You can use a scope test to check if all items are visible in the menu. To check the number of actions in the menu, call:
assert true editor component.getNodeSubstituteChooser().isVisible() && editor component.getNodeSubstituteChooser().getNumberOfActions() == n; +1assert true editor component.getNodeSubstituteChooser().isVisible() && editor component.getNodeSubstituteChooser().getNumberOfActions() == n;
assert true editor component.getNodeSubstituteChooser().isVisible() && editor component.getNodeSubstituteChooser().getNumberOfActions() == n;
IChecker<SNode, NodeReportItem> structureChecker = new StructureChecker(); -IAbstractChecker<ModelCheckerBuilder.ItemsToCheck, IssueKindReportItem> checker = new ModelCheckerBuilder(false).createChecker(new arraylist<IChecker<?, ? extends IssueKindReportItem>>{structureChecker, new SuppressErrorsChecker()}); -checker.check(ModelCheckerBuilder.ItemsToCheck.forSingleModel(modelToCheck), modelToCheck/.getRepository(), new CollectConsumer<IssueKindReportItem>(), new EmptyProgressMonitor()); +3
IChecker<SNode, NodeReportItem> structureChecker = new StructureChecker(); +IAbstractChecker<ModelCheckerBuilder.ItemsToCheck, IssueKindReportItem> checker = new ModelCheckerBuilder(false).createChecker(new arraylist<IChecker<?, ? extends IssueKindReportItem>>{structureChecker, new SuppressErrorsChecker()}); +checker.check(ModelCheckerBuilder.ItemsToCheck.forSingleModel(modelToCheck), modelToCheck/.getRepository(), new CollectConsumer<IssueKindReportItem>(), new EmptyProgressMonitor());
command process<jUnit> process = jUnit(project = project,tests = allTests, - virtualMachineParameter = vmParams - workingDirectory = workingDir); -int exitcode = process.startAndWait(TimeUnit.MINUTES.toMillis(1)); + virtualMachineParameter = vmParams + workingDirectory = workingDir); +int exitcode = process.startAndWait(TimeUnit.MINUTES.toMillis(1));
list<TransformationMenuItem> items = MenuLoadingUtils.loadNamedMenu(editor component, node-ptr/WithExecutableAction/, "test location"); -ActionItem item = (ActionItem) items.get(0); -item.execute(""); +3
list<TransformationMenuItem> items = MenuLoadingUtils.loadNamedMenu(editor component, node-ptr/WithExecutableAction/, "test location"); +ActionItem item = (ActionItem) items.get(0); +item.execute("");
MenuLoadingUtils.java and WithExecutableAction ⧉ are not public, so they have to be created manually.
@MPSLaunch -test case Test extends EnvironmentAwareTestCase { - <<members>> +test case Test extends EnvironmentAwareTestCase { + <<members>> - <<before test>> + <<before test>> - <<after test>> + <<after test>> - test test { - read action with MPSModuleRepository.getInstance() { - foreach module in MPSModuleRepository.getInstance().getModules() { - System.out.println(module.getModuleName()); - } - } - } -} + test test { + read action with MPSModuleRepository.getInstance() { + foreach module in MPSModuleRepository.getInstance().getModules() { + System.out.println(module.getModuleName()); + } + } + } +}
Tests aren't running at all
A test info node ⧉ has to be added to the model of the tests so that the tests can find the project's path. The project path also has to be set in this node. Ensure you create variables in this path in Preferences → Appearance & Behavior → Path Variables @@ -2176,7 +2302,7 @@
Why does the test execution fail with "Test project '$…' is not opened. Aborted"?
It happens because you didn't set the variable in the TestInfo. Go to File → Settings → Path Variables and create an entry for your variable with a path to the project location on your hard drive.
TestInfo
public TextGenDescriptor getDescriptor(@NotNull SAbstractConcept concept) { - switch (myIndex.index(concept)) { - case LanguageConceptSwitch.Component: - return new Component_TextGen(); - case LanguageConceptSwitch.Contract: - return new Contract_TextGen(); - case LanguageConceptSwitch.EmptyContent: - return new EmptyContent_TextGen(); - case LanguageConceptSwitch.Library: - return new Library_TextGen(); - case LanguageConceptSwitch.Port: - return new Port_TextGen(); +12
public TextGenDescriptor getDescriptor(@NotNull SAbstractConcept concept) { + switch (myIndex.index(concept)) { + case LanguageConceptSwitch.Component: + return new Component_TextGen(); + case LanguageConceptSwitch.Contract: + return new Contract_TextGen(); + case LanguageConceptSwitch.EmptyContent: + return new EmptyContent_TextGen(); + case LanguageConceptSwitch.Library: + return new Library_TextGen(); + case LanguageConceptSwitch.Port: + return new Port_TextGen();
Is it not possible to "override the textgen" this way?
inference rule typeof_Member { - applicable for concept = Member as member - applicable always - overrides false +9
inference rule typeof_Member { + applicable for concept = Member as member + applicable always + overrides false - do { - typeof(member) :==: member.type.copy; - } -} + do { + typeof(member) :==: member.type.copy; + } +}
Copy the type only if you want to use it in another type as a child.
In the example, it should be fine to use member.type. But let's say you have a SetType concept in your language that contains an innerType as a child. If you want to construct an instance of that concept, write code like this:
member.type
SetType
innerType
node<SetType> result = new node<SetType>; -result.innerType = member.type.copy; -typeof(member) :==: result +3
node<SetType> result = new node<SetType>; +result.innerType = member.type.copy; +typeof(member) :==: result
If you omit the .copy in the code, you will attempt to "hijack" the member.type node from the member and break the model. MPS will complain.
.copy
member
contributed by: @sergej-koscejev
How do you suppress errors?
I have a piece of embedded demonstration code and don't want it to show warnings (e.g., on unused variables). How can I do that? @@ -1799,9 +1913,9 @@ 1 2 -3> nodeRef@50283.ancestors<concept = ISuppressErrors>.select({~it => [it, it.suppress(nodeRef@50283)]; }); +3> nodeRef@50283.ancestors<concept = ISuppressErrors>.select({~it => [it, it.suppress(nodeRef@50283)]; }); -[[dummy, false], [<no name>[LiteralProgramFragment]: dummy():void, false], [Demo1, true]] +[[dummy, false], [<no name>[LiteralProgramFragment]: dummy():void, false], [Demo1, true]]
I have a piece of embedded demonstration code and don't want it to show warnings (e.g., on unused variables). How can I do that?
> nodeRef@50283.ancestors<concept = ISuppressErrors>.select({~it => [it, it.suppress(nodeRef@50283)]; }); +3
> nodeRef@50283.ancestors<concept = ISuppressErrors>.select({~it => [it, it.suppress(nodeRef@50283)]; }); -[[dummy, false], [<no name>[LiteralProgramFragment]: dummy():void, false], [Demo1, true]] +[[dummy, false], [<no name>[LiteralProgramFragment]: dummy():void, false], [Demo1, true]]
The result is the path from that node to the top and will tell you which node is suppressing that error. The concept IAntisuppressErrors ⧉ also plays a role there, although it is deprecated (implement the suppress method instead).
suppress
var x; +7
var x; -foreach it in self.items { - infer x :>=: typeof(it) -} +foreach it in self.items { + infer x :>=: typeof(it) +} -typeof(self) :==: operation type(self, x, null); +typeof(self) :==: operation type(self, x, null);
Given that you need the operation type in the end. You probably need to change the implementation of the rules contributing to the operation type only to use "one side." If the type of the expression is simply the type of the variable x, you can omit it.
operation type
x
class MyCollection { - Iterator iterator() { - null; - } -} +12
class MyCollection { + Iterator iterator() { + null; + } +} -class MyList extends MyCollection { - @Override - *package* ListIterator iterator() { - null; - } -} +class MyList extends MyCollection { + @Override + *package* ListIterator iterator() { + null; + } +}
The class MyList is allowed to specify a different return type (ListIterator).
What's autoboxing in Base Language/Java?
In some cases, there is an automatic conversion between primitive and reference types called autoboxing/unboxing ⧉.
In some cases, there is an automatic conversion between primitive and reference types called [autoboxing/unboxing](https://docs.oracle.com/javase/tutorial/java/data/autoboxing.html). +
Integer z1 = 1; -int z2 = new Integer(1); +2
Integer z1 = 1; +int z2 = new Integer(1);
Autoboxing doesn't apply to array types ⧉, and the type checker behaves differently in Java (J) and Base Language (B):
1 @@ -1902,25 +2018,42 @@ 3 4 5 -6
Integer[] a = new int[]{1, 2}; // disallowed in both -Integer z = 1; // allowed in both -Integer[] b = {1}; // allowed in J, disallowed in B -int[] c = {new Integer(1)}; // allowed in J, disallowed in B -int[] d = new Integer[]{new Integer(1)}; // disallowed in both -Object d = new int[]{1}; // allowed in both +6
Integer[] a = new int[]{1, 2}; // disallowed in both +Integer z = 1; // allowed in both +Integer[] b = {1}; // allowed in J, disallowed in B +int[] c = {new Integer(1)}; // allowed in J, disallowed in B +int[] d = new Integer[]{new Integer(1)}; // disallowed in both +Object d = new int[]{1}; // allowed in both