Hyperlinks in terminal

Yes, it it quite useful and practical. I was glad to find out about it.

I think the flag would be a good idea, there may be some cases in which it is needed, but I think should be turned on by default, since most modern terminal emulators work fine with it.

Also, as I read, it is a good idea to include the hostname in the file:// url to handle things like ssh when you click on a remote file.

EDIT: this can also be used to make clickable links to a website, like the prolog documentation.

1 Like

I’ve pushed some changes to deal with hyperlinks. I’ve got it all working now on Ubuntu 21.10 using the terminator terminal emulator and PceEmacs. It is a bit of a mess though. This is what happened:

  • Added message line element url(Location), where Location is one of a File, File:Line or File:Line:Column. Also added url(URL, Label) to create arbitrary hyperlinks.
  • Use the message hooking mechanism to extend library(ansi_term) to deal with these things. The option is controlled by the Prolog flag hyperlink_term, which is now by default false. Probably becomes true once we are more convinced.
  • Introduce the url(Location) in all system messages.

This easily makes files from messages clickable. The problem comes when trying to deal with positions in the file. Without that this is barely useful. It took me a while to figure out how this works in Linux. Terminals seem to rely on opening a URL as the desktop environment does. See xdg-open. On Gnome systems this calls gio open URL or the library equivalent. This uses the mime database to figure out the mimetype of the file, mapping *.pl to application/x-perl :frowning: Next, mimeapps.list is used to map this to a *.desktop application that deals with opening the target. Now, most editors seem to be using %f to specify the file, causing gio to translate the file:// URL into a filename. Thus, the fragment indication holding the line is lost :frowning:

I’ve created my own *.desktop entry using %u, which causes the full URL to be passed. That is now handled by a Prolog script that talks to PceEmacs if one is running and calls emacs otherwise. That works. The script is here Not sure when and whether I’ll add that and the desktop to the standard installation. Ideas?

The other issue is that there seems no established standard how to provide the line number. The GIST you point at suggests #Line, #LLine, #position=Line with several hints on how to deal with columns. For now, I use #LLine[:Column], e.g.

 file:///home/..../myfile.pl#L10:5

If this really starts working I think it is a great improvement for the usability. I guess the difficulty dealing with line positions is also why gcc didn’t implement this for error messages. If anyone is aware of popular software such as grep implementing this, please share. That should hint at a clear direction what will become standard.

Ideas welcome!

This looks great from the prolog API point of view. It is quite a usability improvement to just click on the message and go directly to the line in the file.

From what I have seen, many terminals don’t support fragments out of the box as you found out; but often this can be resolved by changing the configuration or adding custom scripts.

The kitty terminal emulator supports fragments and uses it for ripgrep using the

 file:///home/..../myfile.pl#10

format. Kitty also supports clicking on remote files along with ls --hyperlink=auto. I have found it a joy just to click on the :Line and go straight to the file. The developer seems to be quite in touch with developers of other terminal emulators, so I would think that the #<Line> format will slowly be more common. I would definitely suggest removing the L from the url, as no one seems to be using that format.

Possible way forward

Probably the simplest and safest way to go is to allow different values for
the hyperlink_term flag. This will support all kinds of different scenarios in the future, with ia format-like value in the hyperlink_term flag, something like

set_prolog_flag(hyperlink_term,custom('file:///~f#~l:~c'))   % ~f for path, ~l for <line>, ~c for column
set_prolog_flag(hyperlink_term,custom('file://~h/~f#~l:~c'))   % ~h for host, to allow remote ssh files

which would be a format-like specification to produce the hyperlink. This may be the safest way to go forward (perhaps with some preset values as shown below), as it would be a matter of the user changing the flag depending on the terminal emulator capabilities. We could provide the following preset values:

  • false – to disable hyperlinking
  • line – same as custom('file://~h/~f#~l')
  • line_column – same as custom('file://~h/~f#~l:~c')
  • file_only – no fragment produced, same as custom('file://~h/~f')

If things change in the future, and the terminal emulators start adopting something different, we can use different values for the hyperlink_term flag.

Yes, I think this is yet to be improved in the terminal emulators. The good news is that many of them allow either configuration options, or shell scripts to customize the behaviour. I think the best we can do is to be as flexible as possible (with the above hyperlkink_term flag) so that the user can configure his terminal emulator in a way that would be easiest for him.

One thing to consider.

Not just a link to a single line but a link to a contiguous group of lines, e.g. (apply.pl#L60-L64)

This seems to be something I get for free with Emacs compilation mode. It uses customizable regexps for detecting error messages and actions such as “next error” and “jump to source”.

Yes, the use case here is different. It is for running swipl in the terminal, not within an ide-like environment which will generally support this. I often run swipl in the terminal outside of any ide-like environment.

1 Like

Thanks. Done.

I’m not yet sure. For now I’ve added a hook to library(ansi_term) to deal with anything one can imagine and keep a simple boolean. Note that we probably do want to determine whether or not to use this dynamically (e.g., demand the system to be interactive as we also do to decide whether or not to use color). Also, whether or not to include lines and columns depends both on the capabilities of the terminal and the thing it calls and on whether or not we have this info. I will probably include the host.

That is one of the things suggested in the GIST. According to @swi this hasn’t become very popular and I don’t see that much reason (and we do not keep track of the end line of a clause, so we need to compute it).

Good thinking; I missed thinking about interactive mode, terminal capabilities, etc. All in all, I think a hook is much better than what I was proposing. Adding the host is quite useful for remote sessions.

Thanks for your work on this! :grinning:

There doesn’t seem to be a strong use case for this, since normally you just want to look at the text/code and having the editor move to the proper line is enough. I really can’t think of a use case where it would be useful to have a group of lines for clicking on the terminal to open an editor.

However it is quite useful in other scenarios, like quoting code from github as you showed, but this is a different scenario.

There is one thing that comes to mind. It would be nice if we can link back to the same Prolog instance. Think for example about the manual using help/1 and apropos/1. If we can realise the internal page links this way it would be really nice. Also for normal source links I would like to have them handled by the running instance if this runs the built-in editor. Only if not, I’d like them handled by another editor. Right now I have them handled by PceEmacs if one is running or GNU-Emacs otherwise.

How do we link to the current process though? I guess the most obvious solution is to encode that at the location of the host in the file URL, possibly combined with the real host. Would there be any convention to do this?