Can Iet a function auto run at the end of document?

I have a function

#let auto_include(include_list) = {
  for i in include_list [
    #if i != "" [
      #include i
    ]
  ]
}

include_list will change, can I let this function run at the end of document?

Have you tried it? The function worked just fine for me when I tested it. You will have to call it manually at the end of your document, and you will have to provide the list for include_list.

I try use a template to do it:

#let template(doc, include_list) = {
  doc
  auto_include(include_list)
}

It dosen’t work when I add it at the header of the document.


Another function:

#let include_add(file, describe: "", label: "", include_list: ("",), root: "") = {
  for i in include_list {
    if i == root + file {()} else {
      include_list = ((root + file, ) + include_list)
    }
  }
  let output = ()
  if label =="" {
    output = context{link(root + file, describe)}
  } else {
    output = context{link(label, describe)}
  }
  (output, include_list)
}

It will give your a link and the include_list. I use it to link to another file, and make sure no same include to use less space.

For working with a template, this setup works:

///Template.typ
#let auto_include(include_list) = {
  for i in include_list [
    #if i != "" [
      #include i
    ]
  ]
}

#let template(include_list, doc) = [
  #doc

  #auto_include(include_list)
]
//Main.typ
#import "Template.typ": template

#let include_list = ("SubA.typ", "SubB.typ")

#show: template.with(include_list)
//SubA.typ
= A
//SubB.typ
= B

As for the include_add() function, is there something wrong with it? I haven’t tested it at all.

include_add() function, is fine.
Can I check is the document have included another document. I think code

#let (add, include_list) = auto_include_add("2.typ",  describe: "11", label: "2s")
#add
#show: template.with(include_list)

can be a way to auto add correct link.

For some reason all of your code blocks are unformatted (no coloring).

Can you please enter code like this:

```typ
#show: template.with(include_list)
```

It will then show up like this:

#show: template.with(include_list)

For checking if a file has been included:

//Add this to Template.typ
#let file-is-included(label) = {query(label) != ()}
//Add a label to each file
#[]<fileA>
= A
//Include the function in a file, then use it
#import "Template.typ": file-is-included
A Included? #context file-is-included(<fileA>)

Note that #context is required here.

As for creating a link you can do it this way:

#link(<fileA>, "File A description")

But I guess you are looking for something more automatic.

I want the programe can check included file

#let check_included(label) = {query(label) != ()}

#let include_add(file, describe: "", label_in: "", include_list: ("",), root: "") = {
  for i in include_list {
    if i == root + file {()} else {
      include_list = ((root + file, ) + include_list)
    }
  }
  let output = ()
  if label_in =="" {
    output = context{link(root + file, describe)}
  } else {
    output = context{link(label(label_in), describe)}
  }
  (output, include_list)
}

#let auto_include(include_list) = {
  context {
    for i in include_list [
      #if i != "" [
        #if false == check_included(label(i)) [
        #[]#label(i)
        #include i
        ]
      ]
    ]
  }
}

But query needs in a context block.
This makes

#context{link(label(label_in), describe)}

can’t use lables in included file.

1 Like

You could create a function that checks first if the label is found, then create the link if the file is there:

#let link-to-file(label, description) = {
  if check_included(label) {
    //File is in document, create link
    link(label, description)
  } else [
    //Decide what to do if file not included
    //Here there is some text placed in the document
    _"#description" is not included in the document_
  ]
}
//Usage:
#context link-to-file(<fileA>, "A file description")

There still are some thing wrong.
My document is

#import "../lib/common_header.typ": *

#let template(include_list, doc) = [
  #doc
  #auto_include(include_list)
]

#let include_list = ("/test/2.typ", "/test/3.typ")

#show: template.with(include_list)
#let (al, il) = include_add(file: "/test/2.typ", description: "777")
#al
#il
//#context{check_included(label("777"))}

//SubA.typ
= AW
//SubB.typ
= B<2s>

(“/test/2.typ”, “/test/3.typ” don’t have problem)
“…/lib/common_header.typ”:

// Check is a file has been included
#let check_included(label) = {query(label) != ()}

#let link-to-file(label, description) = {
  if check_included(label) {
    link(label, description)
  } else [
    _"#description" is not included in the document_
  ]
}

#let include_add(file: "", description: "", label_in: "", include_list: ("",), root: "") = {
  if file == ""  and label_in == ""{
    panic("No file, No lable. What do you want to do?")
  }
  for i in include_list {
    if i == root + file {()} else {
      include_list = ((root + file, ) + include_list)
    }
  }
  let output = ()
  if label_in =="" {
    output = context{link-to-file(label(root + file), description)}
  } else {
    output = context{link-to-file(label(label_in), description)}
  }
  (output, include_list)
}

#let auto_include(include_list) = {
  //context {
    for i in include_list [
      #if i != "" [
        //#if false == check_included(label(i)) [
        #[]#label(i)
        #include i
       // ]
      ]
    ]
  //}
}

The gray in auto_include used to make sure no same include. But when I use them, they will make the error.

layout did not converge within 5 attempts
Hint: check if any states or queries are updating themselves

After setting up this document structure, I am not getting an error about convergence. The document compiles for me. Is the code you posted the exact same as the code you are using?

lib/
├─ common_header.typ
test/
├─ 2.typ
├─ 3.typ
├─ Main.typ
Typst Files

Main.typ

#import "../lib/common_header.typ": *

#let template(include_list, doc) = [
  #doc
  #auto_include(include_list)
]

#let include_list = ("../test/2.typ", "../test/3.typ")

#show: template.with(include_list)
#let (al, il) = include_add(file: "../test/2.typ", description: "777")
#al\
#il
//#context{check_included(label("777"))}

//SubA.typ
= AW
//SubB.typ
= B<2s>

common_header.typ

// Check is a file has been included
#let check_included(label) = {query(label) != ()}

#let link-to-file(label, description) = {
  if check_included(label) {
    link(label, description)
  } else [
    _"#description" is not included in the document_
  ]
}

#let include_add(
  file: "",
  description: "",
  label_in: "",
  include_list: ("",),
  root: ""
) = {
  if file == ""  and label_in == ""{
    panic("No file, No lable. What do you want to do?")
  }
  for i in include_list {
    if i == root + file {()} else {
      include_list = ((root + file, ) + include_list)
    }
  }
  let output = ()
  if label_in =="" {
    output = context{link-to-file(label(root + file), description)}
  } else {
    output = context{link-to-file(label(label_in), description)}
  }
  (output, include_list)
}

#let auto_include(include_list) = {
  //context {
    for i in include_list [
      #if i != "" [
        //#if false == check_included(label(i)) [
        #[]#label(i)
        #include i
       // ]
      ]
    ]
  //}
}

2.typ

= 2.typ

3.typ

= 3.typ

If you are working in the Typst web app, you can make your project public and share the link here. But if there is sensitive data in the project you should not make it public.

I mean when the code is

#let include_add(file: "", description: "", label_in: "", include_list: ("",), root: "") = {
  if file == ""  and label_in == ""{
    panic("No file, No lable. What do you want to do?")
  }
  for i in include_list {
    if i == root + file {()} else {
      include_list = ((root + file, ) + include_list)
    }
  }
  let output = ()
  if label_in =="" {
    output = context{link-to-file(label(root + file), description)}
  } else {
    output = context{link-to-file(label(label_in), description)}
  }
  (output, include_list)
}

#let auto_include(include_list) = {
  context {
    for i in include_list [
      #if i != "" [
        #if false == check_included(label(i)) [
        #[]#label(i)
        #include i
       ]
      ]
    ]
  }
}

The error

layout did not converge within 5 attempts
Hint: check if any states or queries are updating themselves

will happen.

It comes from calling check_included() from within auto_include(). If you remove this and simply include the file regardless then it will work:

#let auto_include(include_list) = {
    for i in include_list {
      if i != "" [
        []label(i)
        #include i
      ]
  }
}

I’m starting to lose the thread here and am no longer sure what you want.
You are trying to make a system where you can, at any point in the main document, include another file. And also create links that point to any of those files.
Is it really not possible to have the list of all included files defined at one point?

Also, the way Main.typ is using the template function seems incorrect to me. That template function should be in the template file.

1 Like

Maybe this is the best way to solve this problem. Another reason is query need context.

Hi @LRoInT, thanks for your question! I have tried reading through this, but each time you get a response, it seems like you are adding something else that was missing from your requirements. Please try to describe your use case completely, so we can actually help you.

It may also help if you try whether you can adapt a proposed solution to your needs. From what I can tell, @gezepi’s answers seem very helpful. Maybe it’s easier to start there and add what you already had, than start with your code and try to add what gezepi has outlined.

Using query requires context, but that is generally not a reason that query can’t be used to solve a problem. If you tell us what you need, we can show you how to use query and context for that purpose.

I realize there may be a language barrier involved, but without a complete problem description, any help we can give will be incomplete.

Yeah, this question add to much other questions.


All the code you see is now I am using in my project

FIrst, I want to make a link to other doucument, but typst cant’t do this, so I need to include it, so I use two function, creat_link and append_include.

//  I want to use this to make sure no same document
#let check_included(label) = {query(label) != ()}

#let check_link(label, description) = {
    if check_included(label) {
      // If the file is included
      link(label, description)
    } else [
      // If the file isn't included
      #description
  ]
}

#let check_path(path) = {
  // Don't care this
  // Make same path same
  let path_list = path.split("/")
  let output=(path_list.at(0), )
  for i in range(1, path_list.len()) {
    if path_list.at(i) == ".."{
      let temp = output.pop()
    } else if path_list.at(i) == "."{
    } else {
      output.push(path_list.at(i))
    }
  }
  output.join("/")
}

#let creat_link(file: "", description: "", label_in: "", need_include: ("", ), include_list: ("", ), root: "") = {
  // This is fine, but  I think typst should change it too
  // use the path that the file uses this function
  // but not which save this function
  // If I save this function in another file, the link functon will use the file's path
  // For example, I save this function in `/lib/header/t.typ`, 
  // and I use it in `/files/1.typ` and I want to import `/files/2.typ`.
  // If I use this function like #creat_link(file "2.typ"). Please see the image
  // I will get a error like `file not found (searched at /.../lib/header/2.typ)`
  if file == ""  and label_in == ""{
    panic("No file, No lable. What do you want to do?")
  }
  let real_path = check_path(root + file)
  if real_path in include_list {
    // include_list = move_item(include_list, real_path)
  } else {
    need_include = ((real_path, ) + need_include)
  }
  let output = context {check_link(label(real_path), description)}
  if label_in != "" {
    output = context {check_link(label(label_in), description)}
  }
  if file == "" {
    output
  } else {
    (output, need_include)
  }
}

#let include_item(include_list) = {
  // The function use to include other document
  for i in include_list [
   // I want to add the check_include here
   // But as you see, context make it doesn't work
    #if i != "" [
      #[]#label(i)
      #include i
    ]
  ]
}

#let append_include(include_list, doc) = [
  // This is fine
  #doc
  #include_item(include_list)
]

Now I am using it in 1.typ. There may will be some document import twice(or more) in the document, so I want use query to make sure this won’t happen.

Another thing is 1.typ will be included by other document too.

Please reply at here