Get line numbers from `xref_called/3`

I’m working on making a language server for Prolog, for nice IDE-like integrations in Emacs & any other editors that can act as clients. While the xref library is proving very useful thus far, a challenge I’ve run in to is that xref_called/3 doesn’t give the line number that the predicate was called from, just the containing predicate.

I’ve tried finding where the containing predicate was defined and was going to try searching from there for the sub-term, but that doesn’t always work, because the definition of the calling predicate could be broken up over multiple clauses. Is there a way to either get all of the definitions of a predicate so I can do the walking myself or (hopefully) to get xref to report the line number?

Essentially, given code like below, how can I find that other_term/1 is called on line 4?

1 caller(X) :- false.
2 caller(X) :- 
3    true,
4    other_term(X).
5 
6 other_term(A).

My current approach looks like this, which just reports the first line of the caller, which is not great when there are a bunch of definitions for caller.

1 Like

Hi James,

I wrote an lsp server for SWI-Prolog (in a few days, so it is far from complete and not bug free) I have used it for weeks now with neovim, and it works fine. It is missing many features and has some bugs, but it can really jump start your work since it is a working server. You can use the code as you wish. This is the link.

It uses stdio for the JSON exchange, so you just have to configure the server within the editor.

Hope it helps.

Oh, thanks a lot! Mine seems to be working well enough in Emacs now, but I’ll have a look over how you’re doing things & see which features you have that I don’t :grin:.

BTW, I use the SWI-Prolog compiler itself to give all the messages/errors/etc. See load_source_code/2.

1 Like

Great initiative! As is, I’m afraid this isn’t supported. A lot of the prerequisites are in the prolog_xref, prolog_clause and prolog_codewalk libraries. A ready made solution is lacking though.

We could consider to extend prolog_xref to keep track of all call locations using the line number of the clause. The proloag_clause primitives can than be used to find the real location. I’m into too many projects to provide this shortly, but I’m happy to give some pointers and handle pull requests. Call locations are indeed quite nice to have …

1 Like

I created a PR here. This gives information about the line the calling clause begins on; not the exact line number of the calling, but it works for my purposes.

I think the complete solution will involve passing subterm positions in to the various processing goals & move down through that structure in sync with moving through the term…as mentioned in the PR, I was kind of concerned that would be changing a lot of code though.

1 Like

If I can plug my own thing too, even the minimal LSP thing I have working now was actually pretty helpful for making the change! I was able to have the cursor on the call to xref_called/4 in my code, press M-., and get taken right to its definition in prolog_xref.pl :grin: Also very helpful for jumping around in the file itself, being able to get live docs & see the callers, etc.

Sicstus Prolog has a “layout” option for its reads – there’s an example here: https://sicstus.sics.se/sicstus/docs/latest3/html/sicstus/Term-I_002fO.html

Quintus Prolog has a subterm_positions option – see page 1255 of https://quintus.sics.se/isl/quintus/pdf/quintus.pdf

And I think that IBM Prolog had something similar, but I can’t find an online copy of the manual.

Great! I’ll have a look at the PR. Using the start line should be enough. The logic in library(prolog_clause) can be reused to find the real line and character position on demand. That probably requires a bit of additional hacking as this library relies on an actual clause, but most of the stuff should be there. Doing it on demand probably requires less changes and reduces the resources needed for the cross-referencing step.

@peter.ludemann SWI-Prolog has the Quintus subterm_positions in read_term/3, although with some extensions over the years. How else would the source level debugger work …

1 Like