REPL development vs script file paths

I’ve never worked out how to deal with the following situation where I’m developing a script which includes some other files in the REPL, but I also want the LSP to recognise any included functions.

Problem

.
├── scripts
│   └── example_script.jl
└── src
    └── utils.jl

# scripts/example_script.jl
include("src/utils.jl")

example_function() # imported from utils.jl

# src/utils.jl
example_function() = println("Hello")

If from the working directory ., I open up scripts/example_script.jl and the REPL, I can send code using Shift+Enter and it works fine. But the LSP does not recognise any of the definitions in src/utils.jl. I guess this is because when Julia runs a script all paths are treated as relative to the script’s own directory not the working directory that Julia is invoked from.

Solutions

  1. I can instead use include("../src/utils.jl") and change the REPL working directory to ./scripts. Is this what everyone else does? It is a little annoying because: (a) I always need to make sure the REPL working directory is the same as whatever script I’m working on; (b) if I move around/reorganize the scripts directory then I need to change all the relative paths; (c) it’s just less clear to me to have potentially a few layers of ../.. on all the paths than having all paths be relative to the main project directory.
  2. I can do something like the following, but it seems a little hacky?
if isinteractive()
    include("src/utils.jl")
else
    include("../src/utils.jl")
end
  1. Turn src/utils.jl into a package and add or dev it into the project environment. This seems overkill if the utilities are specific to this project and has the disadvantage of requiring a separate Git repo (I think?).

I’d be very grateful to hear what everyone else does! And sorry if this question has been asked before.

It doesn’t require a separate git repo. e.g. you can have one git repo & package MyStuff for the project, with MyStuff/src/MyStuff.jl for the re-usable parts of the project and MyStuff/scripts/ for scripts. Then your scripts can all just do using MyProject for the re-usable utilities.

(Later on you can always refactor into multiple packages/repos as things develop.)

In general, as soon as you start asking “how should I organize this code” you should be creating one or more packages. Packages are lightweight (need not be registered publicly), and benefit from a lot of tooling (e.g. Revise.jl). See also Best practise: organising code in Julia - #2 by stevengj

1 Like

Ah ok. This is simpler than expected. I tried this and it almost works: I can run the code either one line at a time in the REPL or as a script julia --project=@. scripts/example_script.jl and it works fine.

However it seems like the linter is not seeing any of the definitions and is showing a missing reference for the function call example_function() and the using MyStuff import.

In case anyone else comes across this problem with the LSP, I found a workaround here LSP "Missing reference" woes - #16 by ffevotte.

I think the problem is associated with this unresolved issue Hover help doesn't show for own modules · Issue #2719 · julia-vscode/julia-vscode · GitHub.