Skip to content

Fix hover tooltip for data structure subfields - #580

Merged
buzzia2001 merged 2 commits into
mainfrom
fix/hover-subfield-qualified-names
Aug 17, 2026
Merged

Fix hover tooltip for data structure subfields#580
buzzia2001 merged 2 commits into
mainfrom
fix/hover-subfield-qualified-names

Conversation

@bobcozzi

@bobcozzi bobcozzi commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Fixes #579

Summary

Hover tooltips did not appear for data structure subfields in multi-level DS (e.e., 3 or more levels deep) and only appeared when the subfield was also defined elsewhere as a stand-alone field. This PR fixes the lookup logic in the hover provider and improves hover performance.

Root Cause

The hover provider used only a word-based lookup (getWordRangeAtPositionfindDefinition). findDefinition routes through Cache.findSubfields() which deliberately skips QUALIFIED data structures — so subfields like dirty.Fruit were never found.

Changes

extension/server/src/providers/hover.ts

Three-tier symbol lookup (single backwards line-text walk shared between all tiers):

  1. Tier 1 — offset-based (Cache.referenceByOffset): fastest path, works when collectReferences has run
  2. Tier 2 — qualified name chain walk: walks left from cursor through dot-separated segments on the current line, resolves via findDefinition(parts[0]) then subItems.find at each level. Reference-tracking-independent, handles name collisions (same name as standalone variable and subfield), supports unlimited nesting depth (outerDS.innerDS.subfield)
  3. Tier 3 — plain word fallback (findDefinition(word))

Full qualified name in display: Extracts the complete dot-chain from line text (e.g. multiTear.stuff.count) and uses it verbatim as the symbol display name, replacing the previous one-level parent struct search.

Performance: Use parser.getParsedCache() instead of parser.getDocs() so hover never triggers a blocking re-parse. Cold cache returns immediately (no tooltip, no freeze); warm cache returns instantly.

Bug fix: word was declared inside if (!symbol) block scope but referenced outside it in the procedure branch — changed to word || ''.

tests/suite/references.test.ts

Added references_qualified_ds_subfield_hover — verifies that Cache.referenceByOffset returns the correct subitem Declaration at qualified subfield usage offsets, distinguishing it correctly from a standalone variable with the same name.

- Add three-tier symbol lookup to hover provider:
  1. Cache.referenceByOffset (offset-based, fastest path)
  2. Qualified name chain walk from line text (handles name collisions,
     unlimited nesting depth, reference-tracking-independent)
  3. Plain findDefinition fallback
- Display full qualified name from line text (e.g. myDS.inner.field)
  instead of one-level parent struct search
- Use parser.getParsedCache() instead of getDocs() so hover never
  triggers a blocking re-parse; returns immediately on cold cache
- Fix word variable scope bug in procedure hover branch
- Add regression test: references_qualified_ds_subfield_hover
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

👋 A new build is available for this PR based on c4b9ba8.

@buzzia2001 buzzia2001 added the bug Something isn't working label Aug 6, 2026
@buzzia2001

Copy link
Copy Markdown
Member

@bobcozzi if I put dcl-subf id doesn't work...

image image

@buzzia2001

Copy link
Copy Markdown
Member
// `dcl-subf inner_a likeds(inner_t)` is stored with NO keywords and NO
// subitems, so tier 2 walks an empty subItems array. Tier 3 cannot
// help either, because findSubfields() skips QUALIFIED structs.
// Note the contrast with inner_b, declared without `dcl-subf`, which
// the parser expands correctly - so this is a parser bug that the
// hover fix inherits, not only a hover bug.
// -------------------------------------------------------------------
dcl-ds inner_t qualified template;
  dcl-subf field varchar(10);
end-ds;

dcl-ds outer qualified;
  dcl-subf inner_a likeds(inner_t);
  inner_b likeds(inner_t);
end-ds;

@buzzia2001 buzzia2001 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check my 2 examples

@bobcozzi

bobcozzi commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

Ha! Okay, I'll look at that. Funny as dcl-subf was one of my test cases, but didn't use it in a nested Data Structure as you did.

@bobcozzi

bobcozzi commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator Author

Please check my 2 examples

we should be good now. have a look and then merge or let me know what else I need to look at.

@buzzia2001 buzzia2001 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now, works fine, thank you @bobcozzi! Let's wait @VertexGC in order to check it's ok for him...
If he doesn't answer I'll merge on monday

@bobcozzi

Copy link
Copy Markdown
Collaborator Author

Does this issue still persist or can we close/merge it and move on?

@VertexGC

Copy link
Copy Markdown

Behavior is still the same on my end. There have been no new extension version on since 3.0.12 so I don't think I have the latest version of the Dev Pack.

@buzzia2001

buzzia2001 commented Aug 14, 2026

Copy link
Copy Markdown
Member

Behavior is still the same on my end. There have been no new extension version on since 3.0.12 so I don't think I have the latest version of the Dev Pack.

Have you tried the code of the preview of rpg extension?
@bobcozzi could you please upload as a zip file?

@VertexGC

Copy link
Copy Markdown

How would I access the preview of rog extension? I usually just install what's available on the marketplace and I have everything set to AutoUpdate.

@bobcozzi

Copy link
Copy Markdown
Collaborator Author

You need the latest version of the RPGLE extension if you're not on the latest release, then you won't see the changes

@buzzia2001

Copy link
Copy Markdown
Member

Hi @VertexGC

you can install the vsix file in the attachments
vscode-rpgle-0.33.10-dev.0.vsix.zip

@VertexGC

VertexGC commented Aug 17, 2026

Copy link
Copy Markdown

It still isn't working for me. I don't know if this makes a difference or not. Almost 100% of our Data Structures are defined with LIKEDS(typeDataStructure) which is a template that is defined in another member pulled in with a /Copy statement. Could it be having an issue finding that copy member or something?

In the outline view, I can see the data structure defined as a property (with the wrench icon) but I can't drill into it to see any subfields.

I got it to work in one Source Member. The subfields displayed after keying the Data Structure name and a period (.). But in another one, it doesn't work. The program (where it doesn't work) compiles, so I know the code is correct. Very strange.

@bobcozzi

Copy link
Copy Markdown
Collaborator Author

Correct if it does not find the /copy member, the cross reference database is not populated with that subfield information. Make sure the include member is on your library list. I'd suggest we close this out as it isn't a bug so much as a feature base on the User comment. If it turns out adding it to your library list work, and you want something else, then go ahead and open a new issue ans request it as a feature. Thanks.

@bobcozzi bobcozzi closed this Aug 17, 2026
@buzzia2001 buzzia2001 reopened this Aug 17, 2026
@buzzia2001
buzzia2001 merged commit 54abdbf into main Aug 17, 2026
6 checks passed
@VertexGC

Copy link
Copy Markdown

My library list is correct, so it should be able to find the /copy. I've had 2 source files open at the same time. It works in one and not the other.

@bobcozzi

Copy link
Copy Markdown
Collaborator Author

My library list is correct, so it should be able to find the /copy. I've had 2 source files open at the same time. It works in one and not the other.

And excerpt of the code that works and that does not work would be helpful. Also your library list in both contexts would possibly be helpful too.

@VertexGC

VertexGC commented Aug 17, 2026

Copy link
Copy Markdown

I have 2 Data Structures being defined and used in the same program. One will show me subfields and the other will not. Both are defined in copybooks in the Same Source PF, different members.

The source PF is in my Library list in VSCode (I'm using a profile).

Example 1 - Subfields show for this one.

  /if defined (typeServiceAgreementDS_APSSVCAGR)
  //�=======================================================================
  //�- Sub-defined (nested) Type Definition: Service Agreement Attributes
  //�=======================================================================
 D typeServiceAgreementDS_APSSVCAGR...
 D                 DS                  template
 D  saServiceAgreementId...
 D                                     like(typeServiceAgreementId)
 D  saAccountId                        like(typeAccountId)
 D  saServiceId                        like(typeServiceId)
 D  saStartDate                        like(typeDate)
 D  saHasEndDate                   N
 D  saEndDate                          like(typeDate)
 D  saHasStartServiceOrderId...
 D                                 N
 D  saStartServiceOrderId...
 D                                     like(typeServiceOrderId)
 D  saHasEndServiceOrderId...
 D                                 N
 D  saEndServiceOrderId...
 D                                     like(typeServiceOrderId)
 D  saHasRateScheduleType...
 D                                 N
 D  saRateScheduleType...
 D                                     like(typeRateScheduleType)
 D  saHasClosingTransactionId...
 D                                 N
 D  saClosingTransactionId...
 D                                     like(typeTransactionId)
 D  saClosedAgreement...
 D                                     like(typeClosedAgreement)
 D  saHasServiceMeterId...
 D                                 N
 D  saServiceMeterId...
 D                                     like(typeServiceMeterId)
  /undefine typeServiceAgreementDS_APSSVCAGR
  /endif                                                             

From the Copy book...

Copy statements from the RPG Program ...
 //�Copy Member APSSVCAGR :  Service Agreement Object-Level Type Definitions
  /define typeDefinitions_APSSVCAGR
  /define typeServiceAgreementDS_APSSVCAGR
  /copy *libl/QPROTODEF,APSSVCAGR

 Definition in the RPG...  Subfields show perfectly
 dcl-ds ds#ServiceAgr       likeds(typeServiceAgreementDS_APSSVCAGR)  inz;

**Example 2 from the same RPG Program - Subfields are not showing **
Data structure in the copybook ...

  /if defined (typeServiceDS_APSSERVICE)
  //�=======================================================================
  //�- Sub-defined (nested) Type Definition: Service Attributes
  //�=======================================================================
 D typeServiceDS_APSSERVICE...
 D                 DS                  template
 D  svcServiceId                       like(typeServiceId)
 D  svcPremisesId                      like(typePremisesId)
 D  svcServiceType...
 D                                     like(typeServiceType)
 D  svcHasServiceSizeType...
 D                                 N
 D  svcServiceSizeType...
 D                                     like(typeServiceSizeType)
 D  svcHasPhysicalConnectDate...
 D                                 N
 D  svcPhysicalConnectDate...
 D                                     like(typeDate)
 D  svcHasPhysicalDisconnectDate...
 D                                 N
 D  svcPhysicalDisconnectDate...
 D                                     like(typeDate)
 D  svcHasReadingScheduleType...
 D                                 N
 D  svcReadingScheduleType...
 D                                     like(typeMeterReadScheduleType)
 D  svcHasOverrideBillUnitOfMeasure...
 D                                 N
 D  svcOverrideBillUnitOfMeasure...
 D                                     like(typeUnitOfMeasureType)
 D  svcCycleMinimumAmount...
 D                                     like(typeAmountDollar)
 D  svcHasMaximumUsage...
 D                                 N
 D  svcMaximumUsage...
 D                                     like(typeMeterUsage)
 D  svcBaseUsage                       like(typeMeterUsage)
 D  svcReadingMultiplier...
 D                                     like(typeMeterMultiplier)
 D  svcMeterBehindLockedAccess...
 D                                 N
 D  svcHasMeterReadingRouteId...
 D                                 N
 D  svcMeterReadingRouteId...
 D                                     like(typeRouteId)
 D  svcHasMeterReadingStopNumber...
 D                                 N
 D  svcMeterReadingStopNumber...
 D                                     like(typeSQLInteger)
 D  svcUsageFactor...
 D                                     like(typeUsageFactor)
 D  svcHasSummerPeakUsage...
 D                                 N
 D  svcSummerPeakUsage...
 D                                     like(typeMeterUsage)
 D  svcHasWinterPeakUsage...
 D                                 N
 D  svcWinterPeakUsage...
 D                                     like(typeMeterUsage)
 D  svcHasGPSLatitude...
 D                                 N
 D  svcGPSLatitude...
 D                                     like(typeGPSCoordinate)
 D  svcHasGPSLongitude...
 D                                 N
 D  svcGPSLongitude...
 D                                     like(typeGPSCoordinate)
 D  svcHasInstallationId...
 D                                 N
 D  svcInstallationId...
 D                                     like(typeInstallationId)
 D  svcHasFacilitiesChargeAmount...
 D                                 N
 D  svcFacilitiesChargeAmount...
 D                                     like(typeAmountDollar)
 D  svcHasFacilitiesChargeDescriptionType...
 D                                 N
 D  svcFacilitiesChargeDescriptionType...
 D                                     like(typeBillDescriptionType)
 D  svcLockedService...
 D                                 N
 D  svcHasLockDate...
 D                                 N
 D  svcLockDate                        like(typeDate)
 D  svcHasDescription...
 D                                 N
 D  svcDescription...
 D                                     like(typeServiceDescription)
  /undefine typeServiceDS_APSSERVICE
  /endif

  /undefine typeDefinitions_APSSERVICE
  /endif                                         

 Copy Statements from the RPG Program 

  /define typeDefinitions_APSSERVICE
  /define typeServiceDS_APSSERVICE
  /define getServiceAttributes_APSSERVICE
  /copy *libl/QPROTODEF,APSSERVICE

 Data structure definition in the RPG Program.   Subfields are not showing.   
   dcl-ds ds#Service          likeds(typeServiceDS_APSSERVICE)          inz;

@bobcozzi

Copy link
Copy Markdown
Collaborator Author

When you said it works in one and not the other, regarding open tabs, does it always work in the same source member and not the other, or does that change depending on the order in which they are opened in the editor?
Second question: Does the first /copy member also use LIKE to define most of the fields in the template like you do for the /copy *libl/QPROTODEF,APSSVCAGR member?

@VertexGC

Copy link
Copy Markdown

See my more detailed comments above explaining that 2 different one in the same program. I gave the full definitions of the data structures in the copybooks and all of the compiler and definitions from the RPG program (service program).

@bobcozzi

Copy link
Copy Markdown
Collaborator Author

Sure, but the code isn't formatted.
Try to enclose any cut/paste code in three back-quotes. The character on the same key as the ~ on the numeric pad on the top of the keyboard. For example:

 dcl-s hello varchar(10);

@VertexGC

Copy link
Copy Markdown

I thought I'd just send you the actual source code members and a compile listing in a ZIP file.

APSSVCAGR3 is the service program. The procedure example is getServiceAgreementRouteId.
The 2 data structures behaving differently are ds#Service and ds#ServiceAgr.

ds#Service is defined in the APSSERVICE copybook and DS#ServiceAgr is defined in APSSVCAGR.

Data Structure code Completion Members.zip

@bobcozzi

Copy link
Copy Markdown
Collaborator Author

Thanks I got the ZIP now. I'll open it and look at the issue sometime this week.

@VertexGC

VertexGC commented Aug 18, 2026

Copy link
Copy Markdown

Thanks Bob. I noticed one thing. The /Copy statements for the one that works are global in SrvPgm and the /copy statements for the other are local in the procedure, but both Data Structures are defined locally.

So I moved the local /copy statements for the local one to the top and made them global, but that didn't change the behavior of the editor.

@bobcozzi

Copy link
Copy Markdown
Collaborator Author

I have tested your examples and in both data structures the hover text does not appear for subfields.
Can you open a separate issue as this isn't related to the fix that this PR corrected and we've already merged and close this one anyway. That way I can review and track it properly. Thanks. Feel free to include your ZIP file in the new issue.
Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hover Text for Field Properties does not recognize Data Structure Subfields

3 participants