Project

General

Profile

Feature #11741

implement a fully featured Language Server Protocol (LSP) server for 4GL code

Added by Greg Shah 3 months ago. Updated about 9 hours ago.

Status:
WIP
Priority:
Normal
Target version:
-
Start date:
Due date:
% Done:

10%

billable:
No
vendor_id:
GCD
case_num:
version_reported:
version_resolved:
reviewer:
production:
No
env_name:
topics:

protocol_traffic.png (104 KB) Paula Păstrăguș, 09/11/2026 09:19 AM

Screencast from 09-14-2026 03_54_43 PM.webm (13.7 MB) Paula Păstrăguș, 09/14/2026 09:02 AM

Screencast from 09-16-2026 05_32_36 PM.webm (11.9 MB) Paula Păstrăguș, 09/16/2026 11:02 AM

case2.png (12.4 KB) Paula Păstrăguș, 09/18/2026 10:16 AM

case3.png (8.75 KB) Paula Păstrăguș, 09/18/2026 10:16 AM

case1.png (20.4 KB) Paula Păstrăguș, 09/18/2026 10:16 AM

Screencast from 09-21-2026 06_12_01 PM.webm (8.28 MB) Paula Păstrăguș, 09/21/2026 11:26 AM

11741.zip (22 MB) Paula Păstrăguș, 09/23/2026 04:28 AM

11741a_feature_list.md Magnifier (4.95 KB) Paula Păstrăguș, 10/21/2026 09:16 AM


Related issues

Related to Conversion Tools - Feature #3882: changes to allow the front-end to be used for 4GL syntax checking New
Related to Conversion Tools - Feature #3883: eclipse plug for developing 4GL code using FWD including editing, syntax checks, running conversion Pending
Related to Conversion Tools - Feature #11355: VSCode extension New
Related to Conversion Tools - Feature #11742: implement a fully featured Debug Adapter Protocol (DAP) server for 4GL code (in FWD) WIP
Related to Conversion Tools - Support #9919: discuss and define the MVP for the first pass at IDE support WIP
Related to Conversion Tools - Feature #11955: Preprocessor: enforce OpenEdge's 255-level include nesting limit Internal Test

History

#1 Updated by Greg Shah 3 months ago

  • Related to Feature #3882: changes to allow the front-end to be used for 4GL syntax checking added

#2 Updated by Greg Shah 3 months ago

  • Related to Feature #3883: eclipse plug for developing 4GL code using FWD including editing, syntax checks, running conversion added

#3 Updated by Greg Shah 3 months ago

#4 Updated by Greg Shah 3 months ago

The Language Server Protocol (LSP) is a platform independent protocol for creating language support that can work across various IDEs (VSCode, Eclipse IDE, Eclipse Che, vim...).

It is an open standard, originally created by Microsoft for their internal use and then made more generic through work with Eclipse, IBM and Red Hat.

Why You Should Know the Language Server Protocol

VSCode only works with LSP. Building the LSP is the first step in implementing the full extension in #11355.

#5 Updated by Greg Shah 3 months ago

  • Subject changed from implement a fully featured LSP for 4GL code to implement a fully featured Language Server Protocol (LSP) server for 4GL code

#6 Updated by Greg Shah 3 months ago

  • Related to Feature #11742: implement a fully featured Debug Adapter Protocol (DAP) server for 4GL code (in FWD) added

#8 Updated by Greg Shah about 2 months ago

  • Assignee set to Paula Păstrăguș

#9 Updated by Greg Shah about 2 months ago

  • Related to Support #9919: discuss and define the MVP for the first pass at IDE support added

#10 Updated by Greg Shah about 2 months ago

A good first step on this task would be to analyis technical approaches, particularly whether or not we should use something like LSP4J as a framework to reduce the effort on writing the LSP. One alternative is to write all the support ourselves, though my instict says that there should be some real value in a framework that handles the guts of the protocol.

  • The project needs to be open source and licensed in a manner that we can safely combine with our GPLv3 project. Permissive licenses like MIT, BSD or Apache 2.0 are all OK.
  • Is the project active?
  • How well supported is the project?
  • How mature is the project? (has it been stable for multiple years, is it used in well known projects)
  • Are there any critical technical limitations when implementing using this framework?

That last item is the biggest long term concern. If we can't achieve our objectives using it or if it will cause us to do a lot of unnatural implementation to get around its limits, then it isn't a good choice.

Post the analysis here and we can discuss.

#11 Updated by Greg Shah about 2 months ago

Once we have that decision, the next step is to plan the implementation of the LSP itself.

#12 Updated by Paula Păstrăguș about 2 months ago

Here is the framework analysis for #11741-10.

What we would be choosing is a binding: a library that handles the protocol layer, the JSON-RPC messages the editor and our server exchange over a pipe, plus the framing, request/response matching and cancellation that go with them, and gives us the protocol's ~300 message types as Java classes. It handles none of the 4GL. That part is ours either way.

The important result of the survey is that there is no real field to compare. Every JVM option is either LSP4J itself or something built on top of LSP4J:

Candidate License Status Assessment
LSP4J EPL-2.0 OR BSD-3-Clause v1.0.0 released 2026-02-10; last commit 2026-07-09 The only real candidate. Covers LSP 3.18.0. Used by the Eclipse JDT Language Server — the server behind Red Hat's Java extension for VS Code, which is the closest existing analogue to what we are building — and by Scala Metals, Apache Camel LS and Eclipse LemMinX
ls-api None declared Archived since 2016 The only binding that was ever an independent alternative. Its repository description is now literally "MOVED TO" and points at LSP4J: it did not lose to LSP4J, it became LSP4J
Xtext (from #9919-2) EPL-2.0 only Active Fails the license test. Also expects the language to be re-expressed as an Xtext grammar, and its LSP support is built on LSP4J anyway
MagpieBridge EPL-2.0 only Last commit 2025-07-14 Fails the license test. Built for surfacing an existing static analysis's findings in an editor, which is close to our diagnostics case, but it pins LSP4J 0.14.0 from 2022 and pulls in WALA, JavaParser and JGit
Generic JSON-RPC libraries, e.g. jsonrpc4j MIT Active Gives the JSON-RPC envelope but not LSP's own framing and none of the protocol's types. More work than LSP4J for a worse result
Writing it ourselves n/a n/a No license risk, no capability gained, and a protocol implementation to maintain forever

There are good bindings outside the JVM, Microsoft's own in TypeScript, pygls in Python, tower-lsp in Rust, and they are all ruled out by the same thing: our server has to call the conversion front end in-process. Using a non-JVM binding would mean either reimplementing the front end or inventing a second protocol between the binding and our Java, for no gain.

The license is what decides it. LSP4J is dual-licensed, and the second arm is a BSD-3-clause license, which is compatible with GPLv3, so we simply take it under that arm. This matters more than it first appears: EPL-2.0 on its own is not GPL-compatible, which is exactly why Xtext and MagpieBridge are out. LSP4J is the only option in the group that offers a permissive arm at all.

On the rest of the criteria it is clean. Its only runtime dependency is Gson, and FWD already ships the version it wants, so adopting it adds nothing new to our dependency set. It needs Java 11; we are on 17. And the same library implements the Debug Adapter Protocol, the companion protocol for debugging, so it will also serve #11742 instead of forcing a second decision later.

One genuine limitation, from LSP4J's own changelog: breaking API changes between versions are common. The cause is structural, the LSP spec is written in TypeScript, and its types do not map cleanly onto Java, so the binding periodically has to change a boolean to a Boolean, or wrap a plain type once the spec allows an alternative for it. This is an upgrade-time cost rather than a design constraint, and the answer is the one we already use for every FWD dependency: pin a version and upgrade deliberately. It does not force us into unnatural implementation, which was the real concern in #11741-10.

Recommendation

Use LSP4J, pinned at 1.0.0. It is the only candidate that passes the license gate, it is active and mature, its reference consumer is a Java language server driving a VS Code extension, it adds no dependencies, and it covers DAP for #11742 as well.

Worth noting for completeness: the difficulties we will actually hit are in our own front end, not in this library, and they would be identical under any of the alternatives. So this is a smaller decision than it looks, and the interesting work is in the implementation plan.

Two things to confirm and then I will move on to #11741-11: agreement on LSP4J, and on pinning 1.0.0.

#13 Updated by Paula Păstrăguș about 2 months ago

I've moved a bit ahead and experimented with the initial skeleton for the 4GL Language Server and have a working Phase 1 foundation up and running, using claude.

Current Progress: We have the TypeScript VS Code extension successfully spawning the Java com.goldencode.p2j.lsp server process. Communication is happening cleanly over JSON-RPC via stdin/stdout, and the server accurately tracks document states.

As shown in the screenshot below, the output panel is actively capturing protocol traffic. I've been running tests on my end, and the server is successfully receiving and logging didOpen, didChange,didClose and didSave events for OpenEdge ABL files (e.g., start.p in the dataset/abl directory, as well as include files like check-date.i).

#14 Updated by Greg Shah about 2 months ago

Two things to confirm and then I will move on to #11741-11: agreement on LSP4J, and on pinning 1.0.0.

Agreed on both.

#15 Updated by Paula Păstrăguș about 2 months ago

  • Status changed from New to WIP

Here's the progress from today: (I'll attach a video soon)

  • Syntax highlighting: keywords, strings, comments, preprocessor directives and macro references. The keyword list is generated from FWD's own lexer table, so the colouring matches what FWD actually accepts, and abbreviations are honoured: DISP colours the same as DISPLAY.
  • Error badge: a file that does not parse gets the red mark next to its name in the Explorer, a red tab, and an entry in the Problems panel. It appears while you type, before saving, and clears as soon as the code is valid again.
  • Schema-aware: checking uses the project's schema, so table and field references resolve properly. FOR EACH guest: DISPLAY guest.first-name. is accepted; a table that does not exist is reported.
  • Unsaved edits are what gets checked, both in the file itself and in any include file you have open and edited. Note what this does not cover: editing an include to introduce an error, even saving it, does not put a mark on the programs that include it. They are only re-checked when they are themselves edited. That matches OE, which does not propagate either. Should this scenario be improved, if yes, how?
  • Code folding on real 4GL structure: procedures, functions, methods, classes, and DO / FOR / CASE blocks, paired with their matching END. (by default the code folding depends on indentation)
  • Editor basics: comment toggling, bracket matching and auto-closing quotes.
  • Checking is debounced, so typing a statement runs one check when you pause, and not one per keystroke.
  • Nothing is written into the project while checking: no cache files, no ASTs, no hints. A check cannot interfere with a conversion's output.
  • The project is found automatically by looking for cfg/p2j.cfg.xml, so there is nothing to configure. A file outside a project is not checked and the reason is reported, though highlighting and folding still work on it.

That last point deserves a decision. A developer who writes a scratch .p on the Desktop would reasonably expect to be told whether it parses? today they are told nothing. The obstacle is that the front end cannot start without a project.

#16 Updated by Paula Păstrăguș about 2 months ago

I attached a video showing the syntax highlighting, code folding, and the filename displayed in red when an error is reported.

Also, as in OE, if an include file contains an error, the super file (the file that includes it) is not marked as incorrectly parsed (i.e., in red) until it is edited. As I mentioned before, if a different behavior is desired, please let me know.

#17 Updated by Greg Shah about 2 months ago

This is very cool!

Syntax highlighting: keywords, strings, comments, preprocessor directives and macro references.

Please make sure all literals are highlighted. We will want to expand from there to syntax recognized by the parsr, not just the lexer. For example:

  • resource names (variables, streams, widgets, frames, ... colored by resource type, linked to their definition)
  • program structure (names/references of methods, procedures, functions...)
  • data types
  • punctuation and block structure
  • language statements

The OE devstudio took some short cuts and we probably have a richer AST to leverage here.

The project is found automatically by looking for cfg/p2j.cfg.xml, so there is nothing to configure. A file outside a project is not checked and the reason is reported, though highlighting and folding still work on it.

For now, this is OK. Later, we can put in a "default project cfg" that can work with non-project files. But don't worry about this now.

Also, as in OE, if an include file contains an error, the super file (the file that includes it) is not marked as incorrectly parsed (i.e., in red) until it is edited. As I mentioned before, if a different behavior is desired, please let me know.

Yes, I think this should be different. Every open (in the editor) procedure or class that depends upon a broken include file should report the error, instead of just reporting it at the include.

#18 Updated by Paula Păstrăguș about 2 months ago

The colors for highlighting should be the same as in OE? At least for data types I would not consider red, because it makes you think that you just got an error, I would go with green. Also, I don't have access to a dark theme for Open Edge to check the colors due to my old version. Maybe newer versions of OE have this feature?

#19 Updated by Paula Păstrăguș about 2 months ago

Also, claude just built a working Outline view for Procedures and Functions, but it relies on a pretty strange regex. It's definitely fast, but I'd rather change it to an AST traversal since our AST already has all the rich context we need. What's your input on this? Should the Outline view be changed in order to gather info from the AST itself, or should we keep using regex's?

#20 Updated by Paula Păstrăguș about 2 months ago

Aaand, I'm just exploring some ideas and wanted to also ask, is there any possibility of using the call graph or our reports to gather the Outline view data more easily? Let me know if you think this is an approach worth exploring..

Also, I recall you mentioning a button for incremental project conversion that would display the 4GL and Java views side-by-side. Should we include this feature in the scope of this current task? If so, could you please provide a few more details on how it should look?

#21 Updated by Greg Shah about 2 months ago

The color scheme should be our own, we don't have to follow OE. Let's make it better.

Also, claude just built a working Outline view for Procedures and Functions, but it relies on a pretty strange regex. It's definitely fast, but I'd rather change it to an AST traversal since our AST already has all the rich context we need. What's your input on this? Should the Outline view be changed in order to gather info from the AST itself, or should we keep using regex's?

Do NOT use regexes for anything. We drive it all via AST. That is the point to all of this work. We can do better because we implement a richer AST.

Aaand, I'm just exploring some ideas and wanted to also ask, is there any possibility of using the call graph or our reports to gather the Outline view data more easily? Let me know if you think this is an approach worth exploring..

Not in an initial implementation. We will integrate both facilities though call graph needs configuration hints that will have to be build before it is useful.

Also, I recall you mentioning a button for incremental project conversion that would display the 4GL and Java views side-by-side. Should we include this feature in the scope of this current task? If so, could you please provide a few more details on how it should look?

Not exactly a button. The idea is that every procedure or class will have the ability to have a companion view in Java of its converted code. These will be cross linked so that moving either direction is easy. This isn't just about incremental conversion, but about conversion in general.

But for now, don't focus on this yet.

#22 Updated by Paula Păstrăguș about 2 months ago

Greg Shah wrote:

Do NOT use regexes for anything. We drive it all via AST. That is the point to all of this work. We can do better because we

Unfortunately, we can't avoid regexes for syntax highlighting, because VS Code depends on TextMate grammars to apply colors the millisecond a file is opened. TextMate evaluates regular expressions line-by-line, completely independent of our Language Server.
This is actually why syntax highlighting works even if you open a random scratch file that isn't tied to a project.

#23 Updated by Paula Păstrăguș about 2 months ago

The initial implementation and today's changes were committed as 16755 and 16756 / 11741a.

I attached a video with the progress.

#24 Updated by Paula Păstrăguș about 2 months ago

Here are some thoughts / features we might implement (it also contains ideas from yesterday's meeting):

  • Synchronized Cursor Navigation: When a developer switches tabs between the main source file (.p / .w) and the preprocessed cache file, the cursor should automatically jump to the equivalent location in the target file.
  • Convert on Save: A project setting that, when enabled, physically regenerates and saves the cache file to disk every time the main program is saved.
  • Stale Cache Warning / No Artifacts: If the "Convert on Save" setting is set to false, we shouldn't clutter the project with generated files. However, if the developer opens an outdated cache file, the IDE should display a clear warning badge indicating that the source has been modified and the cache is out of sync.
  • Smart Include Refresh Prompt: When saving an include (.i) file, the IDE should detect open parent programs that depend on it and prompt the user: "You have open programs that depend on this include file. Do you want to refresh them?"
  • Include-Aware Error Reporting: If a compilation or syntax error originates inside an include file, the error marker (squiggly line) should be placed on the {include.i} reference in the parent file. Since an include can be called multiple times in the same file (sometimes with different parameters), the system should flag all occurrences where that include is called and breaks the build.
  • Go to Preprocessor Definition: Ctrl+Click on a {&NAME} reference should navigate directly to its corresponding &SCOPED-DEFINE or &GLOBAL-DEFINE.
  • Resolved Preprocessor Peek (Hover): Hovering over a preprocessor reference (like {&WINDOW-NAME}) should display a tooltip showing its final resolved value, saving the developer from having to search for the definition manually.
  • Read-Only Cache View with Source Redirection: The preprocessed view should ideally reject direct edits (since they would be overwritten on the next compile). If a developer attempts to type in the cache file, the IDE could offer a quick action to jump to the actual source line that generated that code.
  • Peek Expansion (Inline View): Alt+Clicking an include reference should open the expanded text inline (in a peek window) without leaving the file. This provides quick context without forcing the developer to actually open the include file in another TAB.
  • Expansion Weight Lens: A small CodeLens annotation above each include reference reporting what it brings in (e.g., "adm2/support.i · 412 lines · 3 nested includes"). This helps developers instantly gauge how much scaffolding a file relies on.
  • Origin Ribbon: In the preprocessed view, a colored stripe in the gutter should mark which include each run of lines came from (with darker shades for deeper nesting). The developer's own code would remain unshaded, making it instantly stand out.
  • Fold to Source: A command in the preprocessed view that collapses every include-derived region. The file then reads exactly like the original source, allowing the developer to expand only the specific include they are currently investigating.
  • Unresolved Include Explained: When an include cannot be found, the error shouldn't just say "File not found." It should explicitly list the PROPATH directories that were searched, turning a dead-end error into an actionable configuration fix.
  • Include Path Completion: Typing { should trigger autocomplete suggestions for files based on the PROPATH, filtering the list as the user types to prevent hard-to-spot typos.
  • Preprocessor Name Completion: Inside a {& block, the IDE should offer auto-completion for the names actually defined and in-scope at that exact point, along with their values.
  • Include Rename and Move Refactoring: Renaming or moving an .i file should trigger a refactoring tool that updates every {...} reference to it across the entire project (this can also be applied for .w / .p files / variable names, procedures and more).
  • Circular and Duplicate Include Warnings: The IDE should flag warnings if an include file pulls itself in (circular dependency) or if it is pulled in twice along the same chain, preventing redundant outlines and unexpected compilation errors.

#25 Updated by Greg Shah about 2 months ago

I love all the new ideas! Let's GO!

Are any of these present in OEPDS?

#26 Updated by Paula Păstrăguș about 2 months ago

Yes, two of these features are already present in OpenEdge: hovering over a preprocessor reference displays its resolved value, and auto-completion is supported for {&.

#27 Updated by Paula Păstrăguș about 2 months ago

I have attached three test cases demonstrating the new preprocessor reference hover behavior. Unlike the native OpenEdge hover, which only resolves local defines and completely ignores typos and inherited files (aka defines from includes), this implementation covers those gaps.
  • Case 1: Undefined or Misspelled References

    Hovering over an unresolved reference explicitly warns the user that the value is not defined anywhere the preprocessor looked.
  • Case 2: Definitions from Include Files

    Hovering over a reference defined externally reveals its resolved value and explicitly states that it originates from an include file. As a future enhancement, we could improve this further by explicitly displaying the include filename in the hover tooltip.
  • Case 3: Local Definitions

    Hovering over a locally defined reference displays its value alongside the exact line number where the &SCOPED-DEFINE or &GLOBAL-DEFINE is located. For this scenario, Ctrl+Click takes you straight to the definition.

#28 Updated by Greg Shah about 2 months ago

As a future enhancement, we could improve this further by explicitly displaying the include filename in the hover tooltip.

Yes, we absolutely should do that. Showing exactly where the definition exists is a very helpful feature.

For this scenario, Ctrl+Click takes you straight to the definition.

We could implement this for the non-local defintion too. That seems like a good idea to me.

#29 Updated by Paula Păstrăguș about 2 months ago

Absolutely, both would be very useful for a developer.

#30 Updated by Paula Păstrăguș about 1 month ago

Committed today's work as rev 16759. It contains the followings:

  • Added re-checking of the programs which use an include when that include is modified, controlled by a setting: always, ask first, or never. An error inside an include is now reported at every {include} reference, in the programs and in the include itself.
  • Errors inside an include are now shown on every place the include is used.
  • Added an error for an include which is not found on the PROPATH.
  • Added completion of include paths after { and of preprocessor names after {&.
  • Added a read-only view of a program's preprocessed source, opened beside it on request. The view shows the conversion's cache file, and its tab is green when the file is up to date and yellow when it is stale.
  • Added a "write on save" setting, on by default, which writes the cache file and the preprocessor hints when a program is saved.

I have also attached a video to illustrate each newly added feature.

#31 Updated by Paula Păstrăguș about 1 month ago

Yesterday committed rev 16760 contains the followings:
  • Added the Regenerate Cache File ‘button’ to the preprocessed source view, which writes the cache file on request whatever the write-on-save setting says.
  • Added the origin ribbon to the preprocessed source view: the lines each include produced are tinted and named, and Fold to Source collapses them so the view reads as the program.
  • Added a lens above each include reference saying how many lines the include has and how many includes it references itself.
  • Added an error for an include which pulls itself in, directly or through others, since the preprocessor would never return from it, and a warning for an include pulled in more than once.
  • Added the DB Structure view: the project's databases with their tables, system tables, columns, indexes, triggers and sequences, with the definition details on hover.
  • Added the schema to the editor: table and field names are coloured orange as in OE, hovering one shows its definition, and completion offers a table's fields after its dot and the tables after a record keyword.

#32 Updated by Paula Păstrăguș about 1 month ago

As promised, I've attached a zip file containing a video that demonstrates the features.

#33 Updated by Greg Shah about 1 month ago

Added an error for an include which pulls itself in, directly or through others, since the preprocessor would never return from it, and a warning for an include pulled in more than once.

We have to be careful with this. It is perfectly legal to recursively include the same include file (directly or indirectly). Yes, if done poorly it can be a problem. But there are plenty of examples of existing projects which have this "feature" in use. We should not report an error if it is working correctly.

#34 Updated by Paula Păstrăguș about 1 month ago

I think you're right. I actually saw this used in hotel_gui and was surprised it could be a valid case. I tried setting up the project on my VM to compare our IDE's behavior with native OE, but hit a DB connection snag. I'll fix that today. In the meantime, if it's too tricky to distinguish valid from invalid circular includes, we could just issue a warning or info note for now.

#35 Updated by Paula Păstrăguș about 1 month ago

Committed rev 16761 contains the followings:
  • Added buffers and temp-tables to the schema features: a reference through a buffer or a temp-table is coloured, hovered and completed like a table reference, and the declared name itself is coloured too.
  • Added go to definition and Find References for the names a file declares, placed by their declaring statement so the jump lands on the name.
  • Added Rename Symbol for a name the file declares, telling scopes apart through the identities the symbol resolver leaves in the tree, so two variables of one name in two procedures are renamed separately.
  • Added following a renamed or moved include in every reference across the project, always, after a prompt or never, as a setting says.
  • Added opening the program a RUN names, a warning when the PROPATH does not hold it, and completion of the program path after RUN.
Committed rev 16762 contains the followings:
  • Added document highlight, which lights up only the mentions of the same variable, buffer or field in the current scope.
  • Added a status bar item showing whether the current program is warming up, being checked, clean, or has errors and warnings.
  • Added code snippets for common 4GL statements and blocks.
  • Added completion of attributes and methods after a colon, methods first and then attributes, as OpenEdge lists them.
  • Added a setting that also offers the FWD-specific attributes and methods, listed last and marked as FWD.
  • Added hover on variables and parameters showing their type and mode, including function parameters.

#36 Updated by Paula Păstrăguș about 1 month ago

Committed rev 16763 contains the followings:

  • Allowed an include to pull itself in when a preprocessor condition ends the recursion, as OpenEdge does, and stopped the preprocessor at OpenEdge's limit of 255 nesting levels.
  • Reported the preprocessor's own failures in the editor, where they were silently ignored before.
  • Added a warning for a variable that is defined but never used.
  • Added a note for a database read with no lock mode, with quick fixes that add NO-LOCK or EXCLUSIVE-LOCK, and a setting to turn it off.
  • Added the Tasks view, which lists TODO, FIXME and XXX comments in any case by priority, refreshes on save, and removes a finished task's comment.
  • Added colouring of TODO, FIXME and XXX in comments, with marks in the scroll bar.
  • Fixed the status bar counting a note as an error, and added the number of notes after the errors and warnings.

#37 Updated by Greg Shah about 1 month ago

In the meantime, if it's too tricky to distinguish valid from invalid circular includes, we could just issue a warning or info note for now.

I don't want anything that will fill up the lists of problems when this is only an issue for an infinite inclusion case.

Added code snippets for common 4GL statements and blocks.
Added completion of attributes and methods after a colon, methods first and then attributes, as OpenEdge lists them.

Where are you taking the content for these from? The knowledge base isn't ready yet.

#38 Updated by Paula Păstrăguș about 1 month ago

Colon completion comes from FWD itself. The list of attributes and methods is ProgressParser.getAttributesAndMethods().
The snippets were written by hand. The 32 snippets in tools/vscode-4gl/snippets/abl.json were written from general 4GL syntax. Once the knowledge base exists, they could be generated from it or reviewed against it.

#39 Updated by Paula Păstrăguș about 1 month ago

Greg Shah wrote:

In the meantime, if it's too tricky to distinguish valid from invalid circular includes, we could just issue a warning or info note for now.

I don't want anything that will fill up the lists of problems when this is only an issue for an infinite inclusion case.

Okay, the idea is that OE allows up to 254 levels of nested includes (i.e., an include that directly or indirectly includes itself), but not more than 255.

#40 Updated by Greg Shah about 1 month ago

Paula Păstrăguș wrote:

Greg Shah wrote:

In the meantime, if it's too tricky to distinguish valid from invalid circular includes, we could just issue a warning or info note for now.

I don't want anything that will fill up the lists of problems when this is only an issue for an infinite inclusion case.

Okay, the idea is that OE allows up to 254 levels of nested includes (i.e., an include that directly or indirectly includes itself), but not more than 255.

This should be something the preprocessor implements and we should have testcases for it. I don't want the IDE support implementing the limit.

#41 Updated by Paula Păstrăguș 26 days ago

  • Related to Feature #11955: Preprocessor: enforce OpenEdge's 255-level include nesting limit added

#42 Updated by Paula Păstrăguș 13 days ago

I attached the feature list.

#43 Updated by Greg Shah 13 days ago

Is this list meant to be the full list of all features to be implemented? Or is this meant to be what is complete by Oct 30?

For each item in the list, please record the source:

  • A feature from the original OEPDS.
  • A feature we imagined.
  • A standard feature of other extensions in VSCode.
  • ...

Some feedback on the list:

For hover and completion cases, we need to be supporting the complete range of language features. For example, all language statements need support for both features.

An include's own declarations are left out of the program's outline.

This is OK as an option, but generally we want all of the declaration for a program in the outline.

#44 Updated by Paula Păstrăguș 12 days ago

This list is what branch 11741a implements today (rev 16772). It is not the full list of features to be implemented. What is still to be done is in the second part, "Requirements from this task".

The Source column says where each feature comes from:

  • OEPDS: present in OpenEdge Progress Developer Studio, checked in the OEPDS I have. Where ours does more, the difference is given.
  • VS Code: a standard feature of language extensions in VS Code (a protocol feature or a common convention).
  • Ours: a feature we imagined in this task.

Implemented in 11741a

The language server runs the FWD conversion front end over the editor's unsaved text. The VS Code extension starts it.

Editor and highlighting

Feature Source
Opens .p, .cls, .i, .f, .h and .w files as Progress 4GL. A program's .cache file opens as its preprocessed view. OEPDS, VS Code
Syntax colouring generated from FWD's own lexer keyword table, including every legal keyword abbreviation. OEPDS (colouring); Ours (generated from the FWD lexer)
Own colour scheme for includes, preprocessor names, constants, tables and fields, in light and dark themes. Ours
Colouring of TODO, FIXME and XXX in comments, with marks in the scroll bar. VS Code (via extensions). OEPDS does not colour them.
Block folding from the parse: every block (procedures, functions, methods, triggers, DO/FOR/REPEAT and so on) folds down to its END, and a DEFINE FRAME over several lines down to its period. Comments over several lines fold as FWD's lexer reads them. Code in an inactive &IF branch does not fold. While you type, the folds follow the edit. OEPDS (blocks; a DEFINE FRAME over several lines; a FORM does not fold), VS Code; Ours (from the parse)
Comment toggling, bracket matching, auto-closing of brackets and quotes. OEPDS, VS Code
32 snippets: procedures, functions, classes, definitions, data access, blocks, RUN, messages, preprocessor directives and a program header. OEPDS (templates), VS Code

Diagnostics (as you type)

Feature Source
Checks the open file on every edit, unsaved text included, after a short pause. VS Code. OEPDS shows errors only after a save.
Lexer, parser and preprocessor errors at their source position, including an unterminated string or comment, a bad directive and a missing &ENDIF. OEPDS (compile errors)
A class named otherwise than its file: the error is on the class name, with quick fixes to rename the class or the file. OEPDS (error 12629); Ours (the quick fixes)
An error inside an include is reported in the include and on each {include} reference in the program. Ours
Include not found on the PROPATH, listing the directories searched. Ours
RUN target program not found on the PROPATH. Ours
Variable defined but never used (faded). VS Code
Database read with no lock mode (default SHARE-LOCK), can be turned off. Ours
Abbreviated field name. Ours
The lock and abbreviation notes stay while the file has a syntax error. Ours
Open programs that use an include are re-checked when the include is saved or edited (always, ask or never). Ours

Navigation

Feature Source
Go to Definition on an {include} reference, a RUN target and a declared name (variable, parameter, procedure, function, method, buffer, temp-table, field). A name declared in an include opens the include. OEPDS (Ctrl+Click), VS Code. OEPDS puts the cursor at the start of the declaring line, opens an include at its first line whatever the declaration's line, and does not open an include written inside an &IF. Ours lands on the name, also inside an include.
Go to Definition on a {&NAME} reference opens the &GLOBAL-DEFINE or &SCOPED-DEFINE the preprocessor used for that reference, in the program or in the include (nested too) that made it. OEPDS (Ctrl+Click), VS Code
Find References and Document Highlight in the same file, scope-aware. OEPDS (Find References), VS Code
Rename Symbol for a name declared in the file, scope-aware. It refuses, with the reason, a new name already declared in the same scope, a reserved keyword or an abbreviation of one, a name an include also uses, and a name of an enclosing or nested scope where a mention would change meaning (a setting lets the last through). A trigger block is a scope. VS Code. OEPDS has no rename of a variable.
Rename of a class from its name, constructor or destructor renames all three, and offers the file rename as a change to confirm. VS Code; Ours (the file)
Renaming or moving an include rewrites every {include} reference in the project (always, ask or never). Ours

Hover

Feature Source
{&NAME}: the value the preprocessor resolved for that reference, and the file and line of the definition it used, in an include too. A name with no definition above the reference says it is not defined at that point. OEPDS (local defines only); Ours (defines from includes, the definition's place, undefined names)
Tables and fields: database, type and schema properties (format, label, help, validation and so on). OEPDS (a table's name, a field's name, type and format, and not for every field); Ours (the other schema properties)
A buffer for a database table: what it is for, and its fields. A temp-table's or work-table's name, and a buffer for one, have no hover for now. OEPDS (the name only); Ours (what it is for, its fields)
Variables, parameters and properties: name, mode and type. A temp-table field: name and type, as a variable (f2 - CHARACTER EXTENT 3). OEPDS (name and type), VS Code
A statement's keyword: the short text of its knowledge base topic, with More..., which shows the long text in the same hover, then Less and Open to the side. An abbreviation finds its keyword (DISP finds DISPLAY). Only the DISPLAY topic exists so far. Ours (from the FWD knowledge base)

Completion and signature help

Feature Source
Include paths after {, directory by directory along the PROPATH. Ours
Preprocessor names after {&, with their values, only those defined above the caret: this file's below their definition, an include's globals below its reference. OEPDS
Program paths after RUN, and the internal procedures the caret sees. Ours (paths); OEPDS (internal procedures)
The names the caret sees, from the last check: variables, parameters, properties, events, widgets and frames, queries, streams, datasets, data-sources, procedures, functions and methods. A procedure's names only inside it, a hidden name once, the innermost. A variable is offered only below its definition; a procedure or method anywhere. Names declared in includes are offered too. The parameters of a declaration with no body (a FORWARD or IN SUPER function, an abstract method, an event's signature) are not. OEPDS (Ctrl+Space). OEPDS also offers a variable defined below the caret, and does not offer the names declared in includes.
Preprocessor directives after &, anywhere in a line: the templates &FR ({&FRAME-NAME}), &GL and &SC, then the 11 directives, taken from the preprocessor lexer. Nothing inside braces, where & starts a preprocessor name or an include's named argument. OEPDS
Fields after record., records after a record keyword, and record names as you type. OEPDS (content assist)
Attributes and methods after :, from the parser's own member table, optionally with FWD-only members. OEPDS (content assist); Ours (FWD-only members)
Signature help for procedures and functions declared in the file. VS Code

Outline

Feature Source
Outline, breadcrumbs and Go to Symbol, grouped: procedures, functions, variables, parameters, temp-tables, buffers, queries, streams, datasets, widgets, triggers and classes. OEPDS (Outline view), VS Code (breadcrumbs, Go to Symbol)
Variables and parameters nested under their procedure or function, fields under their temp-table. OEPDS (fields under their temp-table; it lists a procedure's variables and parameters at the top level, and a function's parameters not at all); Ours (the nesting under procedures and functions)
A class file laid out as in OEPDS: the USING declarations, then the class with its variables, properties and methods, the constructor and destructor among them. OEPDS
AppBuilder "Definitions Block" and "Main Block" entries, and an "Include files" group. OEPDS
An include's own declarations are left out of the program's outline. Ours (to change, see below). OEPDS shows them, greyed, and a click opens the include at its first line.

Code actions

Feature Source
Quick fixes Add NO-LOCK and Add EXCLUSIVE-LOCK on a read with no lock mode. Ours
Quick fixes Rename class and Rename file for a class named otherwise than its file. Ours
Expand an abbreviated keyword or field name, one at a time or all in the file (source action "Expand abbreviations"). OEPDS (keywords, whole file); Ours (field names, and one at a time)
Correct case: keywords in upper case, names and file names left as written (source action). OEPDS

Includes and the preprocessed (cache) view

Feature Source
A lens above each include reference: what it brings in (lines, nested includes), and "Show expansion", which shows in a hover the text the reference expanded to at the last check: arguments and preprocessor names substituted, nested includes expanded, skipped branches left out. Ours
Show Preprocessed Source opens the program's .cache file itself, read-only and coloured, marked up to date or stale. It is stale when it is older than the program or any of its includes. Ours
Cursor sync: the line that matches the cursor is highlighted in the other editor, in both directions (setting). Ours
Go to Counterpart (Alt+O) moves to the matching place in the other editor. From the view it goes into the include the text came from. Where there is no place it says why: a stale cache file, unsaved edits, an include, which has no view of its own. Ours
Show Source returns to the program at the line. Ours
The cache and .pphints files are written on save (setting) and on Regenerate, only from text that matches the disk. Ours
Include regions tinted by nesting depth, labelled with the include name, and foldable (Fold to Source). Ours

DB Structure view

Feature Source
Databases, tables, system tables and sequences, with each table's columns, indexes and triggers and their properties. OEPDS (DB Structure view)
A click on a database opens its .df, on a table, field, index or sequence the .df at the line which adds it, and on a trigger its procedure. Ours. OEPDS lists the triggers but does not open them.
Table and field names coloured in the code. OEPDS

Tasks view

Feature Source
TODO, FIXME and XXX comments of the whole project or the current file, grouped by priority and file. OEPDS (Tasks view, which also lets you add and remove tasks), VS Code (via extensions)
Configurable tags and priorities, owner in brackets. OEPDS (Eclipse task tags); Ours (owner)
Complete Task removes the comment. Ours

Status bar

Feature Source
Check state of the current file: warming up, checking, clean, or the counts of errors, warnings and notes. Clicking opens the Problems panel. VS Code

Server

Feature Source
Started by the extension, which finds the project (cfg/p2j.cfg.xml) and the FWD build itself, or uses the configured classpath. VS Code
Lazy start-up: the warm-up loads the configuration and the schema only, and nothing is parsed until a document needs it. Long work runs off the message thread. The DB Structure view does not wait for a running check. Ours
Positions come from the preprocessor's origin hints (#3881), so a position in the preprocessed output resolves to the original file, line and column. Ours
A check writes nothing into the project. Ours
The class file listing no longer follows symbolic links inside a PROPATH directory, so a project's p2j link no longer makes it list the whole FWD tree (hotel_gui: 55 s down to about 3 s). This applies to conversion too. Ours
17 settings, 12 commands, and the custom requests fwd4gl/preprocessed, fwd4gl/regenerate, fwd4gl/tasks, fwd4gl/schema, fwd4gl/cachePosition, fwd4gl/sourcePosition, fwd4gl/includeExpansion and fwd4gl/kbTopic. Ours

Requirements from this task

These are the requirements and agreed ideas from the notes of this task, with their state in 11741a.

Requirement State
Hover and completion support the complete range of language features: every language statement gets both. To do. The hover of a statement's keyword is in place, fed by the knowledge base; only the DISPLAY topic exists so far. Completion offers the names in scope and the preprocessor directives, besides include paths, preprocessor names, RUN paths, records, fields and attributes/methods. Offering the keywords that can follow the caret needs the parser to say what may come next, and waits for the ANTLR 4 parser.
The outline shows all of a program's declarations, those from its includes too. Leaving out an include's declarations is only an option. To do: the default has to change and the option has to be added. OEPDS shows them, greyed.
Record the source of each feature in the list. Done in the list above.
All literals are highlighted. Done: strings, numbers, dates, TRUE/FALSE/YES/NO and ?.
Colouring from the parser, not only the lexer: resource names (variables, streams, widgets, frames and so on) coloured by resource type and linked to their definition, program structure (names and references of methods, procedures, functions), data types, punctuation and block structure, language statements. To do. Only table and field names are coloured from the server today.
Our own colour scheme, not OpenEdge's. Done.
No regexes: everything is driven by the AST. The TextMate grammar is the only exception. Partly done. No regex is left in the server. Block folding now comes from the parse. Still to move to the AST: the colouring of table and field names, which scans the text for schema names. The list of FWD-only attributes and methods is still kept by hand; it was checked against the members progress.g marks as FWD's, and 47 missing ones were added.
Every open program that depends on a broken include reports the error. Done.
A default project configuration so that files outside a project can be checked. Later, as agreed.
Outline from the call graph or the reports. Later, as agreed: the call graph needs configuration hints first.
A companion Java view of each procedure or class, cross-linked with the 4GL source. Later, in #11742: it syncs the .cache file with the Java code, and this task syncs the program with its .cache file, so the two chain from the 4GL source to the Java code.
Hover on {&NAME} names the include file where the name is defined. Done: the file and the line.
Ctrl+Click on {&NAME} goes to a definition in an include too. Done.
A recursive include is legal and reports nothing, unless the inclusion is infinite. Nothing is added to the Problems list for it. Done: the circular and duplicate include warnings were removed.
The 255 nesting levels limit is implemented in the preprocessor, with testcases, not in the IDE. Moved to #11955.
Snippets and the attribute/method lists are checked against the knowledge base when it is ready. Waiting for the knowledge base. Attributes and methods already come from ProgressParser.getAttributesAndMethods(). The snippets were written by hand.
Synchronized cursor between the program and its preprocessed view. Done: the matching line is highlighted, and Alt+O goes to it, both ways.
Convert on save, and a stale cache warning when the cache is not written. Done.
Prompt to re-check the open programs when an include is saved. Done.
Errors inside an include placed on every {include} reference. Done.
Go to the definition of {&NAME}, and its resolved value on hover. Done, in the file and in includes.
Read-only preprocessed view, with a jump to the source line that produced the code. Done: Show Source goes to the line, Alt+O to the exact place, into the include when the text came from one.
Peek the expanded include inline (Alt+Click on the reference). Done as a "Show expansion" lens above the reference, which shows the expansion in a hover. Alt+Click is VS Code's multi-cursor gesture.
Lens above each include reference with what it brings in. Done.
Origin ribbon and Fold to Source in the preprocessed view. Done.
Unresolved include explained, with the PROPATH directories searched. Done.
Completion of include paths and of preprocessor names. Done.
Renaming or moving an include updates every reference in the project. Done.
Circular and duplicate include warnings. Dropped: a recursive include is legal.

#45 Updated by Paula Păstrăguș 5 days ago

I updated #11741-44 note with the latest improvements.

#46 Updated by Paula Păstrăguș 5 days ago

Committed revs 16764 to 16772 on 11741a. The commit message of each is under Show more:

16764 (2026-09-28)

16765 (2026-10-12)

16766 (2026-10-21)

16767 (2026-10-23)

16768 (2026-10-23)

16769 (2026-10-26)

16770 (2026-10-28)

16771 (2026-10-29)

16772 (2026-10-29)

#47 Updated by Paula Păstrăguș 4 days ago

Committed revs 16773 to 16793 on 11741a: the ide package layout, then the debug adapter work of this task, ported from its git repository (71 commits on trunk r16810) onto 11741a (trunk r16754) in 20 revisions, so the language server, the debug adapter and the VS Code extension are now in one branch, under com.goldencode.p2j.ide (lsp, dap, dbgmap, vscode). The commit message of each is under Show more:

16773

16774

16775

16776

16777

16778

16779

16780

16781

16782

16783

16784

16785

16786

16787

16788

16789

16790

16791

16792

16793

I will continue with more thorough testing of the integration of the language server, the debug adapter and the extension.

#48 Updated by Paula Păstrăguș 4 days ago

Rev 16794:

#49 Updated by Paula Păstrăguș about 9 hours ago

Rev 16795: (2026-11-02)

Also available in: Atom PDF