Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setting Out Dir Using Funcref and Subfiles #2888

Closed
Kayzels opened this issue Feb 24, 2024 · 8 comments
Closed

Setting Out Dir Using Funcref and Subfiles #2888

Kayzels opened this issue Feb 24, 2024 · 8 comments

Comments

@Kayzels
Copy link

Kayzels commented Feb 24, 2024

Is your feature request related to a problem? Please describe it.
I would like to save all my compiled files in a relative directory called out. Suppose I have the following files: document/main.tex, document/subs/sub1.tex, document/subs/sub2.tex. sub1 and sub2 are both subfiles, using the subfiles package. When compiled, I'd like to have the following result: document/out/main.pdf, document/out/subs/sub1.pdf, document/out/subs/sub2.pdf.

document
|-- main.tex
|-- subs 
    |-- sub1.tex
    |-- sub2.tex
|-- out
    |-- main.pdf
    |-- subs
        |-- sub1.pdf
        |-- sub2.pdf

That is, the out directory follows the same directory pattern as the original.

Describe what you've done/tested
My naive idea was to create a function that checks what the current file is, and then return either "./out" or "../out/subs" based on that. The issue with this is that the function uses the root directory for the compilation, so if I'm in a subfile, but compile, it will create it in the wrong directory - it will make an out directory that is a sibling to document!

So I realised I needed to rather get the root directory that vimtex was using. And then return the correct relative directory based on that. Unfortunately, this doesn't seem possible to do using a Funcref defined when vimtex is initialised, because the buffer variable is only defined later.

The code I currently have is:

local function GetOutputDir()
  local vimtex_var = vim.b.vimtex
  if vimtex_var == nil then
    print("vimtex was nil")
    return "./out/"
  end
  local vimtex_file = vimtex_var.tex -- Get file rather than directory
  local file_name = vim.fn.fnamemodify(vimtex_file, ":t") -- Get tail of file_name, only last part
  if file_name == "main.tex" then
    return "./out/"
  else
    return "../out/subs/"
  end
end

return {
  {
    "lervag/vimtex",
    init = function()
      vim.g.vimtex_compiler_latexmk = {
        out_dir = GetOutputDir,
      }
    end
  },
}

Describe the solution you'd like
I'm mainly looking for advice - sorry if this is the wrong place to ask. Is there any way to set it up like I want? Because of when it is set, the vimtex variable isn't set for the buffer yet, but any calls to compile the pdf won't reread the function, from what I can tell. At the moment, vim.g.vimtex_compiler_latexmk is being called from the init() function.

Another possible solution, if it would be usable in such a function, would be to be able to check whether the file being compiled is the main file or a subfile. Possibly a boolean check for that?

Describe alternatives you've considered
I tried defining the above function inside the after/ftplugin/tex.lua file, and setting

vim.g.vimtex_compiler_latexmk.out_dir = GetOutputDir

but this didn't seem to work at all. It seemed to be entirely ignored.

Another thing would be that I could just continue to use the file name, and just explicitly set it so that when I'm not in the main file, it compiles the subfile. So just calling VimtexToggleMain whenever I want to compile and am in the subfile.

Is what I want possible, and if so, what is the right way to go about it? I'm not sure if I'm overthinking or overcomplicating this.

@lervag
Copy link
Owner

lervag commented Feb 24, 2024

So I realised I needed to rather get the root directory that vimtex was using. And then return the correct relative directory based on that. Unfortunately, this doesn't seem possible to do using a Funcref defined when vimtex is initialised, because the buffer variable is only defined later.

Interesting use case. You are right, the out_dir option is "materialized" during VimTeX initialization before the b:vimtex variable is defined. I'm not sure, but I think changing this can be quite hard.

So, preferably, we should first see if we can find a different way of achieving what you want. I think one way is to instead inspect the current file's filename. Something like this:

local function GetOutputDir()
  local file_name = vim.fn.expand('%:p:t')
  if file_name == "main.tex" then
    return "./out/"
  else
    return "./out/subs/"
  end
end

Clearly, this is quite hacky and assumes that you generally open your main.tex first everytime (else the output dir is set to the wrong directory).

I think a good solution here does require that the VimTeX state is available.

Unless you could live with having everything put inside .out/? I think as a general rule, it is usually better to keep things simple and avoid too complex configurations.

@Kayzels
Copy link
Author

Kayzels commented Feb 24, 2024

That is actually the configuration I had originally. That was the issue when I compiled the main one while having a subfile buffer open. But you're right, it probably is the best solution.

The reason for that weird way of doing it is because I originally did the LaTeX files in VSCode, and that was the way output was handled. So there's a bunch of folders with that structure already. And if I kept it to the same form, that would mean I could still use both VSCode and Neovim.

I was just wondering if there was a way, but I realize it is quite convoluted.

lervag added a commit that referenced this issue Feb 25, 2024
This is a breaking change for anyone who uses a vimscript function for
the `out_dir` key of g:vimtex_compiler_latexmk (or similar), because the
function is now passed a single argument.

refer: #2888
@lervag
Copy link
Owner

lervag commented Feb 25, 2024

I've pushed an update that I believe may be what you need. Use the following example to inspect the passed argument:

vim.opt.runtimepath:prepend "~/.local/plugged/vimtex"
vim.opt.runtimepath:append "~/.local/plugged/vimtex/after"
vim.cmd[[filetype plugin indent on]]

vim.g.vimtex_view_method = "zathura"
vim.g.vimtex_cache_root = "."
vim.g.vimtex_cache_persistent = false

vim.keymap.set("n", "q", "<cmd>qall!<cr>")

local function GetOutputDir(args)
  print(vim.inspect(args))
  return "./out/"
end

vim.g.vimtex_compiler_latexmk = {
  out_dir = GetOutputDir,
}

vim.cmd.edit "test.tex"

@lervag
Copy link
Owner

lervag commented Feb 25, 2024

Also, please read the update docs for the aux_dir key in :help vimtex_compiler_latexmk.

@lervag lervag closed this as completed Feb 25, 2024
@lervag
Copy link
Owner

lervag commented Feb 25, 2024

I think you can now do something like this to achieve your original goal:

local function GetOutputDir(file_info)
  if file_info.target_basename == "main.tex" then
    return "./out/"
  else
    return "../out/subs/"
  end
end

You could also try to do some string matching on file_info.target, e.g. check if it contains /subs or similar.

@Kayzels
Copy link
Author

Kayzels commented Feb 26, 2024

Thank you so much! I'm so happy you made a solution - I honestly didn't expect it. And so quickly too.

This is what I have, and its worked perfectly:

---@alias FileInfo {jobname: string, root: string, target: string, target_basename: string, target_name: string }

---Create tex output directory
---@param file_info FileInfo
---@return string
local function GetOutputDir(file_info)
  if file_info.jobname == "main" then
    return "./out/"
  else
    local subdir_root = file_info.root
    ---@diagnostic disable-next-line: cast-local-type
    subdir_root = vim.fn.fnamemodify(subdir_root, ":t")
    return "../out/" .. subdir_root
  end
end

@Kayzels
Copy link
Author

Kayzels commented Feb 26, 2024

  print(vim.inspect(args))

Thanks for pointing this out, by the way. Pretty useful function that I hadn't encountered before.

@lervag
Copy link
Owner

lervag commented Feb 26, 2024

I realized (after some thought) that I could solve this issue relatively easily, and I'm glad to see it works and is useful to you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants