Documentation of user-defined predicates in the prolog lsp-server

I’m using the prolog lsp server by @jamesnvc, and I find it works perfectly for going to definition, references, and quickly seeing documentation of builtin predicates. However, I can’t get it to display the documentation for my own predicates. Perusing the source of the lsp_server, I think they should be written as pldoc, so here’s an example module:

:- module(foo, [bar/2]).

%! foo(-X:atom) is nondet.
%
% Beautiful predicate.
foo(a).
foo(b).
foo(c).
foo(d).

%! bar(-X:atom) is nondet.
%
% Another predicate.
bar(X, X) :- foo(X), member(X, [a,b,c,d,e,f,g]).

however, if I try to get the documentation of foo, I get LSP :: no content at point. Does any of you have this feature working?

I tried this on one of my modules … interactively entering help(transform_kythe_fact) produced warning messages “Invalid mode declaration in PlDoc comment”, “PlDoc: failed to process structured comment”, and this:

ERROR: Failed to translate to HTML: \man_pages([transform_kythe_fact/2],[no_manual(fail),links(false),link_source(false),navtree(false),server(false)])

I had autoload set to true, if that makes a difference.

That’s the same error I get too, when I try to use help directly. However I think the lsp server should be able to take the pldoc documentation, based on this line in the source code. But you raise a good question: how can we have help see the pldoc documentation, without invoking the documentation server?

I assume you have invalid PlDoc comments. There is not a lot of syntax that is validated. Notably the mode lines have to satisfy the mode syntax though.

Without autoloading many of the development tools do not work.

I understand that the flag autoload is true by default, so should help work on a predicate in the example module I shared above? The pldocs seem in the right format to me (edit: I tried using a wrong pldoc format, and I get an error saying I’m wrong, which I don’t get in the module above).

Yeas, that should not be a problem. I don’t know whether the lsp server supports user defined predicates. @jamesnvc was quite enthusiastic about the new Emacs sweep mode by @oskardrums. If Emacs is your target, I’d try that.

1 Like

But the predicate whose help I was trying to output didn’t generate any warnings about invalid PlDoc syntax. So, I presume that there’s a problem with man_page//2 (possibly related to Text help fails in some cases ?)

Looks like there is an issue with this predicate. Possibly it fails if some of the predicates it tries to generate documentation for has wrong comments? Should be possible to debug with a spy point and a bit of tracing (although it isn’t the most beautiful piece of code :frowning: )

Challenge accepted. :slight_smile:

2 Likes

The problem is here: https://github.com/SWI-Prolog/packages-pldoc/blob/fe1a2ea7e75f4df65c797bb2c9f1de78ff80db2c/doc_man.pl#L567

This restricts help/1 to only predicates that are exported. This appears to be a design decision, so I’m not sure how best to fix it.

(In order to find this, I had a bit of fun removing the various “notrace” and generate_debug_info,false items)

1 Like

Does help work for exported predicates for you? In my case, it will say:

%@ Warning: No help for bar.
%@ Warning: Use ?- apropos(query). to search for candidates.
%@ true.

even when I have loaded the file and the predicate is exported.

I got the same result as you: “No help for <predicate name>.”

So, I’ll have to dig deeper (but probably not today).

1 Like

Correction: with the removal of private(Full,Options) from packages/pldoc/doc_man.pl, I get documentation on exported predicates (without this change, I get the “No help” message).

2 Likes

Amazing, is that something that could be done on the user side as an option, or does it require editing the source files?

I edited the source files (I routinely build and install from the github sources).
If you install in a user-writeable director, you can edit the source files. For example, my executable is ~/.local/bin/swipl and the file that I edited is ~/.local/lib/swipl/library/pldoc/doc_man.pl (if you install in the standard place, presumably you could edit /usr/lib/swipl/library/pldoc/doc_man.pl or something like that).

1 Like

Back to the original, the file defines bar/2, but documents bar/1. You easily see this when using

swipl --pldoc foo.pl

For me, as is, help(bar) results in telling me there is no help for bar and showing the close match var/1 as alternative. After fixing the comment it works as expected.

Does anyone has a reproducible example of main_page//2 failing when called from help/1?

This is my test file (it’s longer than it needs to be). It succeeds with the exported predicate and fails with the non-exported one. (I earlier reported that it failed with an exported predicate, but that was incorrect):

test file
:- module(pykythe, [pykythe_main/0]).

:- det(pykythe_main/0).
%! pykythe_main is det.
% By default, this is called as part of initialization. It sets up
% an error handler and some appropriate global limits, then calls
% pykythe_main2/0 to process the files according to the command line
% arguments.
pykythe_main =>
    set_prolog_flag(stack_limit, 1_610_612_736), % TODO: 1.5GB - default of 1GB might suffice

    % TODO: catch_with_backtrace/3 wrap might not be needed when the
    %       initialization/2 directive is enabled.
    catch_with_backtrace(pykythe_main2,
                         Error,
                         ( print_message(error, Error),
                           halt(1) )),
    log_if(true, 'End'),                         % TODO: delete
    halt(0).


:- det(transform_kythe_fact/2).
%! transform_kythe_fact(+Fact0, -Fact1) is det.
% TODO: Note that this also changes fact_value to base64 and has special
%       cases for symtab, text, colors
transform_kythe_fact(json{source:Source0, fact_name:FactName, fact_value:FactValue}, Fact1) =>
    Fact1 = json{source:Source1, fact_name:FactName, fact_value:FactValueBase64},
    % text is alread in base64 (from Meta.contents_base64)
    (   FactName == '/kythe/text'
    ->  FactValueBase64 = FactValue
    ;   FactName == '/pykythe/color_all'
    ->  FactValueBase64 = FactValue
    ;   base64_utf8(FactValue, FactValueBase64)
    ),
    transform_kythe_vname(Source0, Source1).

with output:

failure in man_page//2
$ ~/src/swipl-devel/build/src/swipl -O -l /tmp/mm.pl
Welcome to SWI-Prolog (threaded, 64 bits, version 9.1.17-2-g95852849b-DIRTY)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
Please run ?- license. for legal details.

    CMake built from "/home/peter/src/swipl-devel/build"

For online help and background, visit https://www.swi-prolog.org
For built-in help, use ?- help(Topic). or ?- apropos(Word).

?- help(transform_kythe_fact).
ERROR: Failed to translate to HTML: \man_pages([transform_kythe_fact/2],[no_manual(fail),links(false),link_source(false),navtree(false),server(false)])
true.

and when I remove the \\+ private(Full, Options) in man_page//2, I get:

success (after changing doc_man.pl)
?- help(transform_kythe_fact).
/tmp/mm.pl


 transform_kythe_fact(+Fact0, -Fact1) is det[private]
    TODO: Note that this also changes fact_value to  base64 and  has special
    cases for symtab, text, colors
true.
1 Like

Thanks. No time now, but I should investigate this. Just removing might give undesirable side effects.

Pushed a fix that

  • Preserves the module for user defined predicates, so we do mix up different predicates
  • Show matches, also when private by passing an extra option and checking this when deciding to hide some predicate or not.

Probably a step in the right direction, but whether it always does what it is supposed to …

I did a quick test … one thing I noticed is that when I simply removed the \+private(...) test, the output from help(transform_kythe_fact) was

 transform_kythe_fact(+Fact0, -Fact1) is det[private]
    ... other comments ...

but with the change a608a5a87f0f90f1851841d9cc0cf435a3e1a71a and ea19f38875a0fdb7a78dd14c52cc7e867bffa94b, the [private] was no longer there