How can I see list of labels?

You could do something like this to filter bibliography entries:

(filter
(lambda u (not (string-starts? (car u) "bib-")))
(map cadr
  (map tree->stree
        (search-labels (buffer-tree))
            ))
))
1 Like

Inspired by the code in progs/link/ref-edit.scm, here is a function to find all labels starting with a string:

(tm-define (search-labels-starts t s)
(let* (
     (labs (search-labels t))
     (labl (map (lambda (lab) (tm->string (tm-ref lab 0))) labs))
     (filt (lambda (lab) (string-starts? lab s))))
(list-filter labl filt)))

This could be useful to implement a dialog that adapts while the user is typing the label or reference.

1 Like

This has the disadvantage that the user needs to remember that labels must not start with “bib-”. If it is possible, I would like a code that checks the tree above the label and filters the label out if it is in the bibliography.
I have tried, also looking at progs/link/ref-edit.scm,

(tm-define (label-context-b? t)
   (and (tree-in? t (label-tag-list)) (not (tree-is? (tree-outer (tree-outer t)) 'bibitem*))))

together with

(tree-search (buffer-tree) label-context-b?)

which did not work.

I am thinking how to insert in a widget the other piece of code you wrote.

The code to “execute function on any keypress in this field” could be in \progs\generic\search-widgets.scm, as matches get highlighted when one types in the box for text search, but I haven’t understood where yet.

It is a good idea. I use Latex for many years and can solve any problem with Latex by using the internet. There is huge information about Latex. The bad side of Texmacs is the need for knowledge of Scheme and there is a great problem with writing extensions. The collection (with an explanation if possible) of extensions will be good support for using Texmacs.

I do not have yet a grasp of this, but I realized, after a bit of time using it, that there is at least one good reason for using Scheme as an extension language: recursion (“natural” in Scheme) acts very well on the trees that represent TeXmacs documents.

It seems that if you do tree-outer twice you get a document containing multiple bibitem*s.

This seems to work:

(tree-is? (tree-ref t :previous) 'bibitem*)

I also notice that progs/database/bib-local.scm has a biblio-context? function:

(define (biblio-context? t)
  (or (tree-func? t 'bibliography 4)
    (tree-func? t 'bibliography* 5)))

Using this I think we could also do

(tm-define (in-biblio-context? t) (tree-search-upwards t biblio-context?))

and

(tm-define (search-doc-labels t)
  (tree-search t
    (lambda (s) (and (label-context? s) (not (in-biblio-context? s)))
  )))
1 Like

I still have to understand the details of how it works (e.g. what tree-func? does), but it is very nice. With my tree-outer construct I wanted to reach the bibliography tag, the one I posted is not rational (starting from the guess that tree-outer picks up the upper branch of the tree) as bibitem is at the same level as the label.

With this I think we are at version 0.99 of the widget :smirk:. There is one important property of the widget to implement: it must be either a modal window, or it must disappear once the user starts editing somewhere else. In the current state, one could end up with several widgets open at the same time.
Having it modal is probably the easiest of the two options, but the other could maybe work as well. I have made a very brief internet search (with the words modal widget texmacs) and I found two pages which seem relevant
https://lists.texmacs.org/wws/arc/texmacs-users/2013-10/msg00002.html
https://www.mail-archive.com/texmacs-dev@gnu.org/msg01978.html
but I will go through them later (and maybe search/read more).

So, for @JoePass, this is the current code for the initialization file

(define (biblio-context? t)
  (or (tree-func? t 'bibliography 4)
    (tree-func? t 'bibliography* 5)))

(tm-define (in-biblio-context? t) (tree-search-upwards t biblio-context?))


(tm-define (search-doc-labels t)
  "A function that finds all labels that are not part of the bibliography."
  (tree-search t
    (lambda (s) (and (label-context? s) (not (in-biblio-context? s)))
  )))


;; use search-doc-labels function instead of the standard search-labels, that
;; finds the labels defined in the bibliography as well
(tm-widget (enum-labels) 
  (enum (insert answer) 
(map cadr (map tree->stree (search-doc-labels (buffer-tree)))) "Choose label ..."
"24em"))


(tm-menu (texmacs-popup-menu)
  (:require (or
	     (and 
	      (tree-is? (focus-tree) 'reference) 
	      (tree-is? (tree-outer (focus-tree)) 'inactive))
	    (and
	     (tree-is? (focus-tree) 'label)
	     (tree-is? (tree-outer (focus-tree)) 'inactive))))
  (former)
  ---
  ("Show labels ..." (show enum-labels)))
1 Like

Excellent! Thank you for this code. I hope it will be useful for TeXmacs users. I changed a little bit your code. I added

(and 
	      (tree-is? (focus-tree) 'eqref) 
	      (tree-is? (tree-outer (focus-tree)) 'inactive))

The command \ref gives the number of the reference, only. The command \eqref makes reference with parentheses. The code now works with \ref and as well with \eqref. I’m not a user of Scheme and do not know this language, I made an analogy only.

I think, that it better to implement this code in TeXmacs itself.

3 Likes

(this post came out longer than what I thought it would )

In Scheme a function call is expressed as a list

(f a b c)

is the function f applied to the arguments a b and c.
a b and c can also be expressions, that Scheme needs to evaluate, and they are generally made in the same way:
either a function call that starts with a parenthesis and the name of the function, followed by arguments
or so-called “atoms”, i.e. elementary data, which are numbers, strings or symbols (more on symbols later).
Scheme then proceeds by substituting inside the parentheses the value of each expression in place of the expression, then evaluates the expression that results out of this, till it has evaluated everything up to the outer parentheses.

(or a b c ...) is the “or” statement that exists in every language (without entering into details that are not essential here), so you with your code you are adding a new clause to the or (a new case, which if true will make the whole expression true). and is “and” :slight_smile:

Both “or” and “and” are expressed with the operator first, like all Scheme functions (e.g. to do 1+1 in Scheme you must do (+ 1 1)).

The apostrophe before the words “eqref” and “inactive” tells Scheme not to evaluate those expressions, but to repeat them. So the result of the Scheme code 'eqref is the symbol eqref, and the result of 'inactive is the symbol inactive.
TeXmacs recognizes both symbols as part of its syntax.

The point of defining parts of trees with a symbol instead than with a string (e.g eqref instead of "eqref") is that by the way strings and symbols are defined it is quicker to check that two symbols are equal than to check that two strings are equal; I have a vague understanding of why this is the case, but so vague that I will not try to write it down … perhaps someone else will be able to explain it (it depends on how strings and symbols are saved in the computer’s memory as far as I understood).

With this I have not defined what is a symbol :-). I will do it when I will understand it :smirk:

Forward with the code.
I did not investigate what tree-outer and focus-tree do but I guessed it: (tree-outer t) returns the tree of which t is a branch. (focus-tree) returns the current focus :slight_smile:, which is determined by the cursor (just guesses, but in this code they worked).

I also guessed that tree-is? checks the first element of the tree (first argument) against the second argument. So (tree-is? (focus-tree) 'eqref) is the function tree-is? applied to the two arguments (focus-tree) and 'eqref.

Putting together a few pieces:
(tree-is? (focus-tree) 'eqref)

  • we have to evaluate the function tree-is? with the arguments (focus-tree) and 'eqref
  • as a first step we evaluate the arguments
  • (focus-tree) will give as a result a list (which represents the tree at focus, let us not enter into this), let us call it focus-list for our example
  • 'eqref evaluates to the symbol eqref, which Scheme knows how to handle, let us call it #eqref in this example to remind ourselves that it is an expression which is already evaluated and it gave a symbol as a result of the evaluation (regardless of whether this notation has already another meaning in Scheme, which I do not know)
  • now Scheme evaluates (tree-is? focus-list #eqref), and substitutes the result of the evaluation in its place

And so on, till the outer parentheses.

1 Like

Thank you for your explanation. I see that scheme is a very specific language. I have very little experience in Basic (student time) and Python (just for fun). It is difficult to make something for Texmacs without community.
I made code using an analogy in another post How do I change hotkeys?.

Perhaps you could like the online book by Dorai Sitaram (https://ds26gte.github.io/tyscheme/index.html) could interest you.
The first 6 chapters are the fundamental things, then chapter 7 (I/O), 8 (macros, of which I know very little and they are quite different from the ones of Common Lisp I read). Of the other chapters 13 (the so-called continuations) is still a general topic (I know almost nothing about this too, never had the occasion to use them, I know they are jumps, more powerful than the jumps of other languages (do not know why). The other chapters seem to me explanations on implementations of programming techniques.

I liked also Yet Another Scheme Tutorial (http://www.shido.info/lisp/idx_scm_e.html).

As far as I understand, the concepts to learn for an elementary grasp of Scheme (as a functional language) are

  • The substitution model for computation (I described it in the previous post)
  • Variable binding, that in a functional language is permanent—if I write (define x 1), x will have the value 1 for all the computation. Then there is the procedure set! that changes the value of x :smirk: but it is conceptually outside the domain of the functional language
  • Mapping. This is explained in the websites; I read (do not recall where) that once you know mapping, you will think that every language should implement it … and I agree.
  • Connected with mapping, passing functions as arguments to other functions. I map a function over a list to obtain an output list, so I pass to the map function both the function and the list. Another example: I have a procedure to find zeros of a function with the bisection method, and I pass to this procedure the function of which I want to find the zeros)
  • Recursion, that you have to use instead of looping (in fact in looping you change the value of the loop variable, and in functional programming bindings are permanent)

One advantage of functional programming that I see after having a bit of experience is that programs are easier to test than the ones written with imperative programming. The same input to a procedure should always give the same output (so-called “referential transparency”), and this is not true for programs written with other kinds of programming, because the behaviour of the procedure could depend on variables that you do not pass to the procedure through its interface.
And of course … one can use programming constructs outside the “functional” domain in Scheme too :smirk: (the procedure set! is imperative for example)

Step by step the community will grow, the forum, the mailing list and the blog all help to make it grow.

1 Like

Here is another attempt at something useful :slight_smile: It’s based on @mgubi’s suggestion on the mailing list to use balloons.

With this you can define a shortcut to show a list of existing labels in a pop-up when you are editing a reference or label

(define (biblio-context? t)
  (or (tree-func? t 'bibliography 4)
    (tree-func? t 'bibliography* 5)))

(tm-define (in-biblio-context? t) (tree-search-upwards t biblio-context?))

(tm-define (search-doc-labels t)
  "A function that finds all labels that are not part of the bibliography."
  (tree-search t
    (lambda (s) (and (label-context? s) (not (in-biblio-context? s)))
  )))

(tm-define (label-balloon)
  (let* ((ls (search-doc-labels (buffer-tree)))
         (ll (map (lambda (l) (tree->string (tm-ref l 0))) ls)))
    (display-balloon (cursor-tree) `(document ,@ll) "auto" "auto" "keyboard")
))

(kbd-map
(:require
       (and (inside? 'inactive)
            (or
             (reference-context? (focus-tree))
             (label-context? (focus-tree)))))
   ("C-F8" (label-balloon))
)

I have to say I’m enjoying this little collaboration. It’s a great learning experience :slight_smile:

I’m too. Could you please explain how to use this code?

Thank you for the message and the references. I shall try to read, as far as possible. I found another reference.

The code can be put into my-init-texmacs.scm and will define a shortcut (currently Ctrl+F8, but you can change this) that works only in inactive references and labels. It shows a pop-up with the labels in the main document.

It currently doesn’t allow to select a label as balloons are passive pop-ups. Perhaps we can cycle through the list when the shortcut is pressed repeatedely and insert the top item when for example the Enter key is pressed.

A further improvement would be to filter based on what text is already typed in the reference or label.

I understand this. But when I click C-F8 I see pop-up window completely black, without any info.

What version are you using? Some of these features are still in flux. Do the comment and preview-ref packages work on your system?

Again very nice. I had not realized that label-context? can be used again in the condition, and also that the “and” can be pulled out of each expression.
Here is the code with both widget and balloon.
I added filtering with respect to the current string to the balloon only, and filtered the list differently when in a label or when in a reference: when in a label, the current string is excluded (only the other labels are listed). I think that this corresponds to what one expects when seeing the list—one would like to check the current string with all of the other labels; I may be wrong.
In a reference, the current string must be kept because we are listing labels, not references.
When filtering out the current label, I am excluding from the list the first element that is equal to the current label; in this way, the resulting label list may be in the wrong order (the labels are listed in the same order they are in the document).
Perhaps one way to achieve the list in the correct order without the current element is to eliminate the current branch from the tree before searching the labels. I have to read the manual to see if and how it is possible.

(define (biblio-context? t)
  (or (tree-func? t 'bibliography 4)
    (tree-func? t 'bibliography* 5)))

(tm-define (in-biblio-context? t) (tree-search-upwards t biblio-context?))


(tm-define (search-doc-labels t)
  "A function that finds all labels that are not part of the bibliography."
  (tree-search t
    (lambda (s) (and (label-context? s) (not (in-biblio-context? s)))
  )))


(tm-define (filter-labels labels str)
  (filter (lambda (s) (string-contains s str 0))
	  labels))

;; delete from list lst the first element matching x
;; https://stackoverflow.com/questions/29273374/scheme-guile-remove-only-first-occurrence-from-a-list-non-destructive
;; https://stackoverflow.com/a/29281801
(define (delete-first x lst)
  (cond ((null? lst) '()) ; first base case
        ((equal? (car lst) x) (cdr lst)) ; second base case
        (else (cons (car lst) ; recursion
                    (delete-1st x (cdr lst))))))


;; use search-doc-labels function instead of the standard search-labels, that
;; finds the labels defined in the bibliography as well
(tm-widget (enum-labels)
  (enum (insert answer)
(map cadr (map tree->stree (search-doc-labels (buffer-tree)))) "Choose label ..."
"24em"))


;; http://forum.texmacs.cn/t/how-can-i-see-list-of-labels/335/33
;; if in label context, I am deleting the first label which is equal to the
;; current label; it would be better to delete the current label itself, so
;; maintaining the order of the list of labels: how does one do that?
;; A workaround is to set labels in alphabetical order, so that the original
;; order is lost
(tm-define (label-balloon)
  (let* ((focus-string (cadr (tree->stree (focus-tree))))
	 (ls (search-doc-labels (buffer-tree)))
         (ll (filter-labels
	      (map (lambda (l) (tree->string (tm-ref l 0))) ls)
	      focus-string))
	 (ll-filt (if (reference-context? (focus-tree)) ; delete current label
					; from list if in label
		      ll
		      (delete-first focus-string ll))))
    (display-balloon (cursor-tree) `(document ,@ll-filt) "auto" "auto" "keyboard")))

(define (inactive-reference-focus)
  (and (inside? 'inactive)
            (or
             (reference-context? (focus-tree))
             (label-context? (focus-tree)))))

(tm-menu (texmacs-popup-menu)
  (:require (inactive-reference-focus))
  (former)
  ---
  ("Show labels" (top-window enum-labels "Insert labels")))


(kbd-map
  (:require (inactive-reference-focus))
  ("C-l C-l" (label-balloon)))
1 Like

The combination of the three references could be a good way to learn Scheme. The book by Dybvig is probably more systematic than the other works (I am not a good judge)—I noticed that it places together basic and advanced topics, so one needs to find one’s own way through it I think.