The classic development model for any application implies good documentation on the user interface and API, as well as, if necessary, good coverage of the source code with comments. In this case, the finalization of the system begins with a study of the documentation, then the code is directly changed and, finally, all the necessary information is updated.
However, one of the problems with this approach is that it significantly increases the cost and slows down the development process. What if all this is not? Then the IDE comes to the rescue, thanks to which you can study the current logic using bare code.
When we developed the
lsFusion platform with an embedded language, we had several options. Either reinvent the wheel, and write from scratch your own IDE, as 1C did at one time, or implement a plug-in for an existing one. We went the second way, and in this article I will show what happened.
Since the platform itself was developed in Java, we had two main options: Eclipse or IDEA. We settled on the last option and did not fail. When we made the decision, IDEA was still not popular enough, but since then, they have emerged as leaders in the market, and Eclipse is quietly behind.
It didn’t take a lot of time to develop the
plugin itself, since it was possible to use the code used directly during the execution of the platform in many ways. Thus, with minimal effort, we got a very powerful IDE, in many aspects significantly superior in functionality to the IDE of many other ERP platforms (both native and built on Eclipse).
The role of the IDE in development is hard to overestimate. Despite the fact that many developers still use vim and believe that it should be. This position has the right to life if one person develops and further supports this code. However, in larger projects where a large number of people participate, their interchangeability is very important. Employees get sick, go on vacation, leave in the end. In addition, the load on different projects is uneven, and sometimes it is required to connect more people to one of them in order to meet deadlines. At such moments, you have to connect new people to the improvements, who need to quickly figure out how the program is currently working, and make the necessary changes. And here comes the IDE.
First of all, we needed the following from the IDE:
- Syntax support . Keyword highlighting, auto-substitution, error highlighting.
- Navigation Go to the ad, search for uses, search by text string, file or name, etc.
- Analysis . The hierarchy of classes and calls, as well as the properties and actions of the class.
- Refactoring Renaming classes, properties, and actions.
- Visualization of forms . Display to the developer of the current design of a certain form.
- Metaprogramming . Ability to generate code based on metacodes on the fly .
- Debugger The ability to set breakpoints (including conditions), debug imperative logic, watch watches.
- Language Injection . Navigation, refactoring, auto-substitution and lsFusion syntax highlighting when used in other languages - Java and JasperReports XML.
Since we used the standard scheme embedded in IDEA for the plugin, the output from working with logic in lsFusion turned out to be almost identical to the development in Java. The same menu items, hot keys, transparent debugging, which can switch from lsFusion code to Java and vice versa, and so on.
Here are some simple examples to show how this works in practice.
Syntax support
The plugin can substitute valid keywords, possible properties, automatically detect various errors:
Navigation
Take the logic from
the Material Management example. Suppose we need to see where the Price property is declared. To do this, you need to hover over the heading of the column we need as a user with administrator rights:
In the window that appears, you can immediately see in which module this property is created (Shipment), what row number is in it (37), the table in which it is stored (_auto_Shipment_ShipmentDetail), and a number of other information.
To go directly to the property declaration, you need to start the file search and enter Shipment in the dialog that appears:
Then, using Navigate - Line / Column, go to the 37th line, where we see the property declaration:
By pressing CTRL + ALT + F7, while the cursor is on the desired property, you can quickly find all its uses for all projects:
In this case, the first use of the price is in the calculation of the amount per line. The last two are adding to the appropriate forms.
If necessary, you can enable search only by record in this property, if you remove the corresponding option:
Then only the entry in this property will remain in the list. To find out what specific value is written to it, you need to cursor on salePrice and click Go To Declaration or Usages. Next, go back through Navigation - Back and go to the declaration of the item property:
To summarize, we found where this property we needed was declared, in what cases it is used, and when the record goes there. On the video, I did all the actions with the mouse, although of course in practice only the keyboard is used. This technique allows you to quickly determine the current implemented system logic and make changes to it, having a complete understanding of where this will lead.
Refactoring
Often there are situations when you need to change the name of a property, class, form, or any other element in the system. To carry out such an action, you need to stand on this element and click Refactor - Rename:
Renaming an element automatically changes the source code in all places of its use. In addition, if the migration.script file is created, the corresponding entries will be added there. The server needs to know the name changes in order, for example, to automatically migrate data from one column to another. Otherwise, it is impossible to distinguish renaming from creating a new property with a different name.
Analysis
Before performing refactoring, it is often necessary to find out "what is happening" and "who are all these people."
To do this, IDEA, almost out of the box, allows you to view the structure of the selected class (properties and actions available for this class):
Also, if you need to get a general picture of what is happening, IDEA allows you to build various hierarchies:
- inheritance of the selected class
- Uses of the selected item (for example, properties or forms)
All the top features are provided by IDEA automatically (with minimal gestures) after the implementation of the search engine ads. The following set of features made the plugin tinker with a bit more, but still, IDEA provided a significant part of the infrastructure (of course, not without problems, but more on that later).
Form visualization
In lsFusion, the structure and design of forms is set in the same code as the domain logic using special constructions. Moreover, different parts of the form can be declared in different modules, and when the server starts, they will “merge” together, depending on the connected modules.
To see the resulting design, you can, of course, restart the server, and watch the result in the client. But server restart takes some time. The plugin can:
- Show the current design and hierarchical structure of the form in a special window
- Find elements in the shape structure
- Highlight selected shape element in design
Here's what it looks like in the IDE:
When building a form, only the current active module and everything on which it depends are taken into account.
It is not yet possible to visually change the current design, since the form is formed from several blocks of code. During modification, it is difficult to unambiguously determine in which place you need to make appropriate changes. In addition, the same element can be modified in several blocks of code, and the platform ensures that if one module depends on another, then its changes will be applied last. However, in the future we plan to add certain functionality for visual design changes.
Metaprogramming
Sometimes there is a need to create the same type of code for various tasks. LsFusion has a metacode mechanism that allows you to generate code based on some template. In this case, in case of a change in the metacode, the code will be automatically updated. In fact, this is an automatic copy / paste with the ability to replace certain identifiers with specified values.
To enable this mechanism, you must first activate it in the menu. After that, the IDE will automatically change the corresponding code.
When the server starts, only the generated code will be used. META templates themselves will not be taken into account when starting the server.
By the way, the implementation of the possibility of metaprogramming made us make another contribution to open-source (in this case, Intellij IDEA). The fact is that in ERP metacodes are used quite actively, and, accordingly, often there is a need to generate code / delete the generated code. This leads to a large number of asynchronous file changes, which, in turn, led to a very peculiar
bug . The problem is that they could not be played in JetBrains itself, so it all came down to the fact that we ourselves had to write a non
- working
unit test . This of course took several days, but indirectly helped us in implementing the following two possibilities.
Debugger
When the code is completely unclear what is happening, then you have to turn to the debugger. On any line of imperative logic (actions, events, restrictions), you can put a breakpoint. As soon as server execution reaches this point, it will be stopped and control will go to the debugger. At this point, you can watch watches, and also continue to execute line by line. On the left, a stack trace will be shown, along which you can navigate as you would when debugging a regular Java application.
When viewing current values, you can access both current objects (for example, Shipment s), and any other objects from the database (for example, Item i). However, the developer himself is responsible for adding data to the watches, the reading of which will take a lot of time or memory, and will lead to a drop in performance.
You can also set breakpoints on a specific property. Execution will stop anywhere when a record is made to it:
This is useful when you need to determine which event or action changes the value of a property.
To actually implement the debugger, we actually used the existing IDEA Java Debugger. That is, the platform is being debugged as a regular Java application, but for lsFusion actions we create proxy java methods and replace their display with our code (as I understand it in IDEA, this is done to support Scala and other wrappers over Java). And here was a funny moment. At some point, IDEA developers
made the constructor of their Java Debugger private. And if the situation with calling private methods can still be circumvented through Reflection, then how to inherit from a class with a private constructor is not clear. But just at that time there was a showdown with a bug from the upper section, and we “decided to barter” to ask the people from JetBrains to make this constructor back protected, to which they reacted very quickly (for which, of course, many thanks to them).
Language injection
One of the most unusual features of IDEA is the ability to provide support for your language in string constants of other languages. To do this, just tell IDEA exactly which string constants apply to your language, then IDEA itself automatically:
- generates a virtual file (or several files) with the given prefixes for each string constant
- creates in the editor of the source file for all constants a kind of “window” into this virtual file
- provides in this virtual file support for all the features of the “embedded” language, such as highlighting errors, switching to an ad, auto-completion, searching for uses and, most importantly, refactoring. That is, when renaming any element in one language, it is automatically renamed in all string constants that refer to this element in other languages. Thus, you are automatically protected from broken links.
Here in IDEA there was (and still is) a small
bug . When the virtual file is large, if the IDEA should go to the beginning of the “implementation window” when it proceeds to use it, it actually goes to the end of the previous “implementation window” (that is, for example, the previous use of the property in the Java file). Of course, there is a simple workaround for this bug - to create a separate virtual file for each string literal. But this approach slows down when there are more than 30 uses, so in this case you still have to use one large virtual file (on the other hand, when there are a lot of uses, it’s not so difficult to find the right one, that is, the following). We asked to fix this bug again in the framework of the “service exchange”, and the JetBrains developers kind of fixed it, but, as it turned out later, somehow it wasn’t (it was still visible by the commit, but we thought that it’s just not the end was understood). However, we have all been accustomed to this bug for a long time, since the situation with using more than 30 elements in one file is quite rare.
Conclusion
The article describes only the main use cases. It also has the ability to search for implementations of abstract properties and classes, visualize dependencies between modules and properties, automatically generate forms based on xml / json, and much more. And, of course, there is built-in integration with the main version control systems of Git and Subversion, as well as support for Maven and Ant.
Following the path of developing the IDEA plug-in, with little effort we got a very powerful free integrated development environment that surpasses competitors IDE in many respects.