# Julia developing workflow with vim and terminal

**URL:** <https://discourse.julialang.org/t/julia-developing-workflow-with-vim-and-terminal/126242>\
**Category:** New to Julia\
**Created:** [February 24, 2025, 8:11am UTC](https://discourse.julialang.org/t/julia-developing-workflow-with-vim-and-terminal/126242 "2025-02-24T08:11:11Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![danieleavitabile](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danieleavitabile/32/31966_2.png) [@danieleavitabile](https://discourse.julialang.org/u/danieleavitabile)\
**Post date:** [February 24, 2025, 8:11am UTC](https://discourse.julialang.org/t/julia-developing-workflow-with-vim-and-terminal/126242/1 "2025-02-24T08:11:11Z")

</div>

Dear All,

I edit files using vim, and run Julia in a terminal split, next to my editor. I am sure I won’t be the first one who adopts this setup.

My workflow is currently the following:

- I edit a `driver.jl` file that starts with `module driver`
- I move to Julia in the other terminal split, and run `include("driver.jl")`

I am interested in what people with a similar setup do when developing. I am really new to Julia, and this is what I find awkward:

- When prototyping, I continuously have to issue the `display()` command, to see the output of my computations; this is presumably because I am using `module`. A similar thing is said for plots and figures.
- If I don’t wrap my code in a module, I still have old variables in the cache (I don’t know how to clean variables) between runs.

I would be generally interested in what people do to address the points above, but more generally what is their workflow.

Thank you!

---

<div class="post-metadata">

**Author:** ![Yuan-Ru-Lin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yuan-ru-lin/32/46068_2.png) [@Yuan-Ru-Lin](https://discourse.julialang.org/u/Yuan-Ru-Lin)\
**Post date:** [February 24, 2025, 8:26am UTC](https://discourse.julialang.org/t/julia-developing-workflow-with-vim-and-terminal/126242/2 "2025-02-24T08:26:44Z")

</div>

Have you checked out [Modern Julia Workflows](https://modernjuliaworkflows.org)?

---

<div class="post-metadata">

**Author:** ![danieleavitabile](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danieleavitabile/32/31966_2.png) [@danieleavitabile](https://discourse.julialang.org/u/danieleavitabile)\
**Post date:** [February 24, 2025, 8:37am UTC](https://discourse.julialang.org/t/julia-developing-workflow-with-vim-and-terminal/126242/3 "2025-02-24T08:37:09Z")

</div>

Thanks, that’s useful @Yuan-Ru-Lin, what workflow do you use? Note I don’t use VSCode, but another editor

---

<div class="post-metadata">

**Author:** ![Eliassj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/eliassj/32/204117_2.png) [@Eliassj](https://discourse.julialang.org/u/Eliassj)\
**Post date:** [February 24, 2025, 8:58am UTC](https://discourse.julialang.org/t/julia-developing-workflow-with-vim-and-terminal/126242/4 "2025-02-24T08:58:51Z")

</div>

I’ve used both vim and Emacs along with a terminal window next to the editor. My workflow looks like:

1. Use Revise.jl which keeps track of changes in your code and reloads it for you.
2. Always develop code as part of a package. I usually use PkgTemplates.jl to initialize a package from a template I have saved. This helps you with setting up documentation, git, CI etc. You can also go into the Pkg mode and type `generate Foo` which quickly generates a barebones package if you don’t need the bells and whistles of PkgTemplates.jl.
3. For complex functions, make a function within your package to test it and initialize all parameters within the function. This avoids cluttering the global environment with variables.

This allows you to immediately run functions from the code you’re developing without using `include` or `display`.

---

<div class="post-metadata">

**Author:** ![Yuan-Ru-Lin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yuan-ru-lin/32/46068_2.png) [@Yuan-Ru-Lin](https://discourse.julialang.org/u/Yuan-Ru-Lin)\
**Post date:** [February 24, 2025, 9:50am UTC](https://discourse.julialang.org/t/julia-developing-workflow-with-vim-and-terminal/126242/5 "2025-02-24T09:50:15Z")

</div>

I use NeoVim and plain REPL. When I prototype, I start by opening REPL and working on a PoC. If I could make it work, I would copy the code I typed in the REPL, create a script by calling `edit("script.jl")` in the REPL and then paste the code in the script file (rudimentary, I know!). [1]

What happens next depends on how winding the snippet is. If it’s really twisted, then I would stay in the editor and try simplifying it. If it’s simple, then I just close the file, get back to REPL and continue to get more PoC working.

At some point, I would start having functions and a set of calls to those functions; that’s when I split the file into something like `lib.jl` and `script.jl`. With these two files, I could start another session of work by

1. loading functions by `includet("lib.jl")`,
2. loading calls by `include("script.jl")`,
3. continue to work in REPL.

Sometimes, when I want to refactor code, I would run

```julia
entr(["lib.jl"]) do
    # some calls yanked from script.jl [1]
    # possibly prefixed by some `@time` to catch degradation
end

```

and then proceed to modify `lib.jl` and see if anything goes bad.

I rarely wrap my code in a module unless I want to make it a package. Surely, variables that would have been kept local in a module would hit me sometimes, but having code factored out as above means it’s common (but easy) to reload everything so bugs will be caught early.

* * *

[1] For the record, I have found [lemonade](https://github.com/lemonade-command/lemonade) incredibly useful. I could yank lines from a file on a remote machine with `"+y` and be able to paste them on my local machine with `"+p` (and vice versa) as if they were the same machine. This is especially important since I work mostly on remote machines in a HPC system.

* * *

EDIT1: add a footnote

---

<div class="post-metadata">

**Author:** ![danieleavitabile](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danieleavitabile/32/31966_2.png) [@danieleavitabile](https://discourse.julialang.org/u/danieleavitabile)\
**Post date:** [February 24, 2025, 10:55am UTC](https://discourse.julialang.org/t/julia-developing-workflow-with-vim-and-terminal/126242/6 "2025-02-24T10:55:18Z")

</div>

Thank you both @Yuan-Ru-Lin and @Eliassj this is very helpful!

---

<div class="post-metadata">

**Author:** ![aerappa](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aerappa/32/212281_2.png) [@aerappa](https://discourse.julialang.org/u/aerappa)\
**Post date:** [February 25, 2025, 8:05am UTC](https://discourse.julialang.org/t/julia-developing-workflow-with-vim-and-terminal/126242/7 "2025-02-25T08:05:52Z")

</div>

I would just add that I also use neovim + Tmux and [slime](https://github.com/jpalardy/vim-slime) is quite useful for highlighting a snippet and executing it directly in the REPL. It works with a bunch of other multiplexers (screen is the default). [super fingers](https://github.com/artemave/tmux_super_fingers) which is tmux specific afaik, is also very useful for jumping directly to an error in a stacktrace.

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [February 25, 2025, 10:20am UTC](https://discourse.julialang.org/t/julia-developing-workflow-with-vim-and-terminal/126242/8 "2025-02-25T10:20:54Z")

</div>

I would add, as a vim user, that VSCode has as vim emulation plugin which, although not perfect, is reasonable enough, and for me justified the use of VScode with all it’s other features.

I still use vim in various situations, but a good IDE can really make our life easier most of the time.

---

<div class="post-metadata">

**Author:** ![Pedro](https://avatars.discourse-cdn.com/v4/letter/p/919ad9/32.png) [@Pedro](https://discourse.julialang.org/u/Pedro)\
**Post date:** [February 25, 2025, 11:04am UTC](https://discourse.julialang.org/t/julia-developing-workflow-with-vim-and-terminal/126242/9 "2025-02-25T11:04:06Z")

</div>

Check out [iron.nvim](https://github.com/Vigemus/iron.nvim) plugin for interactive REPL inside neovim.

---

<div class="post-metadata">

**Author:** ![caleb-allen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/caleb-allen/32/14054_2.png) [@caleb-allen](https://discourse.julialang.org/u/caleb-allen)\
**Post date:** [February 25, 2025, 4:01pm UTC](https://discourse.julialang.org/t/julia-developing-workflow-with-vim-and-terminal/126242/10 "2025-02-25T16:01:32Z")

</div>

I primarily write code in vscode with the aforementioned vim emulator plugin, and then and iteratively test the functions as I build them out in the REPL.

As for the terminal side of things, I couldn’t find anything to give me a vim-like experience while in the REPL which led me to write VimBindings.jl, which gives you the vim basics in the REPL. It not a complete vim implementation but it covers enough for me to use day to day.

---

<div class="post-metadata">

**Author:** ![danieleavitabile](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danieleavitabile/32/31966_2.png) [@danieleavitabile](https://discourse.julialang.org/u/danieleavitabile)\
**Post date:** [February 26, 2025, 11:54am UTC](https://discourse.julialang.org/t/julia-developing-workflow-with-vim-and-terminal/126242/11 "2025-02-26T11:54:23Z")

</div>

Thank you Pedro, this seems very useful _once one knows what to do with the REPL_. I guess my main point here is that using the REPL with the workflow I have forces me to litter the file containing the main portion of the code with ‘display’. So my question to you is: how do you normally visualise things in the REPL? You give up using modules?

---

<div class="post-metadata">

**Author:** ![danieleavitabile](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danieleavitabile/32/31966_2.png) [@danieleavitabile](https://discourse.julialang.org/u/danieleavitabile)\
**Post date:** [February 26, 2025, 12:03pm UTC](https://discourse.julialang.org/t/julia-developing-workflow-with-vim-and-terminal/126242/12 "2025-02-26T12:03:59Z")

</div>

Thank you @caleb-allen. It would help a lot if you could elaborate on your statement ‘and then iteratively test the functions as I build them out in the REPL’. Say you have just added the line line `y = exp(x)` on the following file, named `simple.jl`

```julia
module simple
x = 0
y = exp(x)
end

```

You have the REPL open. What do you do to check that y has been correctly assigned the value 1.0? One way to do that is to add the extra line `display(y)` and then invoke `include("simple.jl")`. This solution forces me to litter the code with all the ‘display’ statements.

Another one is to yank the two inner lines, and send them to the REPL. This to me is also a bit strange, because I feel I have little control on what is stored in the REPL (maybe I am overwriting a variable x that will be important in one of the later evaluations I have).

I know these are very elementary questions, and may be dictated by my being a Matlab user transitioning to Julia, but either of these workflows seem slightly unsatisfactory. Do you go for the second option? Is this what you do, or am I missing something?

---

<div class="post-metadata">

**Author:** ![caleb-allen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/caleb-allen/32/14054_2.png) [@caleb-allen](https://discourse.julialang.org/u/caleb-allen)\
**Post date:** [February 26, 2025, 2:06pm UTC](https://discourse.julialang.org/t/julia-developing-workflow-with-vim-and-terminal/126242/13 "2025-02-26T14:06:59Z")

</div>

No worries, these are all great questions and certainly ones which are best to have answered at the beginning of your Julia journey, it’s a wonderful paradigm that I think you will enjoy. And regarding your experience so far, I certainly agree that littering the code with `display`\[1\] is overly tedious, hopefully the information below can help you get to a smoother workflow.

> It would help a lot if you could elaborate on your statement ‘and then iteratively test the functions as I build them out in the REPL’.

Of course. And before I get going, I’ll say that this discussion is as much about how your code is organized (as files and modules) as it is about the REPL itself; the REPL is the place where evaluation actually happens, and the variety of workflows people have usually fall out from that fact. In other words, despite the many different approaches you’ve already seen (and will inevitably see), you will find that they almost always boil down to some variation of “edit there, evaluate here” where “there” is a text editor of some sort and “here” is the REPL.

> Say you have just added the line line `y = exp(x)` on the following file, named `simple.jl`
> 
> ```julia
> module simple
> x = 0
> y = exp(x)
> end
> 
> ```
> 
> You have the REPL open. What do you do to check that y has been correctly assigned the value 1.0? One way to do that is to add the extra line `display(y)` and then invoke `include("simple.jl")`. This solution forces me to litter the code with all the ‘display’ statements.

Modules are a useful abstraction to aggregate related features under one umbrella, but what you likely want here is a function:

```julia
# simple.jl
function simple()
    x = 0
    y = exp(x)
end

```

Then, from the REPL, you can call the function `simple` directly; the function is evaluated and its last expression is returned, in this case `y = exp(x)`.

```julia
julia> include("simple.jl")
simple (generic function with 1 method)

julia> simple()
1.0

```

Let’s say you want to edit the function in the file, for example changing `x = 5`:

```julia
# simple.jl
function simple()
    x = 5
    y = exp(x)
end

```

Once that is saved, `include` the file again and the changes are applied:

```julia
julia> include("simple.jl")
simple (generic function with 1 method)

julia> simple()
148.4131591025766

```

From this you can build significant complexity while interactively checking the functions one by one, writing new functions which use your previously defined functions, etc. This is the essence of the REPL workflow.

Lastly, it can become tedious to call `include` over and over again, and this is where the package Revise comes in. When you use Revise’s alternate function `includet` instead of the built-in `include`, the REPL actually determines which files have changed and automatically `include`s them. Then, your workflow is to edit and save the functions, and the next time you run them the REPL uses your newest version, no loading necessary.

* * *

1. If you find yourself needing to do this in large functions, check out Julia’s `@show x`, it’s very convenient to place wherever you want without altering an expression’s behavior

---

<div class="post-metadata">

**Author:** ![danieleavitabile](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danieleavitabile/32/31966_2.png) [@danieleavitabile](https://discourse.julialang.org/u/danieleavitabile)\
**Post date:** [February 26, 2025, 2:21pm UTC](https://discourse.julialang.org/t/julia-developing-workflow-with-vim-and-terminal/126242/14 "2025-02-26T14:21:50Z")

</div>

This is wonderful, I get it, thank you so much @caleb-allen!

---

<div class="post-metadata">

**Author:** ![rveltz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rveltz/32/2707_2.png) [@rveltz](https://discourse.julialang.org/u/rveltz)\
**Post date:** [February 26, 2025, 2:28pm UTC](https://discourse.julialang.org/t/julia-developing-workflow-with-vim-and-terminal/126242/15 "2025-02-26T14:28:00Z")

</div>

Exactly, I concur with @caleb-allen that `includet("simple.jl")` is a very useful framework, even more useful when you have a module that includes many files which you modify.

I would go as far as saying that developing large codebase without `Revise` is quite cumbersome but that’s my point of vue.

---

<div class="post-metadata">

**Author:** ![danieleavitabile](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danieleavitabile/32/31966_2.png) [@danieleavitabile](https://discourse.julialang.org/u/danieleavitabile)\
**Post date:** [February 26, 2025, 2:41pm UTC](https://discourse.julialang.org/t/julia-developing-workflow-with-vim-and-terminal/126242/16 "2025-02-26T14:41:00Z")

</div>

Thank you @rveltz this is much appreciated.

---

<div class="post-metadata">

**Author:** ![nsslh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsslh/32/27447_2.png) [@nsslh](https://discourse.julialang.org/u/nsslh)\
**Post date:** [February 28, 2025, 10:08am UTC](https://discourse.julialang.org/t/julia-developing-workflow-with-vim-and-terminal/126242/17 "2025-02-28T10:08:02Z")

</div>

In the section on Testing (under Sharing), your readers might appreciate reference to

- The official [Workflow for Testing Packages](https://docs.julialang.org/en/v1/stdlib/Test/#Workflow-for-Testing-Packages).
- The brilliant [Julia Test Running: Best Practices](https://erikexplores.substack.com/p/julia-testing-best-pratice)

I found the _Test Running_ guidance so useful that I built [SimpleTestRunner.jl](https://juliahub.com/ui/Packages/General/SimpleTestRunner) to help my colleagues embrace the workflows. But I think other workflow concerns are holding them back, and I think your Modern Julia Workflows will really help with that.

Thank you!

---

<div class="post-metadata">

**Author:** ![Xing\_Shi\_Cai](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xing_shi_cai/32/14128_2.png) [@Xing\_Shi\_Cai](https://discourse.julialang.org/u/Xing_Shi_Cai)\
**Post date:** [March 2, 2025, 2:33pm UTC](https://discourse.julialang.org/t/julia-developing-workflow-with-vim-and-terminal/126242/18 "2025-03-02T14:33:26Z")

</div>

Besides Tmux, there are other ways slime can work. For example, you can use kitty, [kitty](https://sw.kovidgoyal.net/kitty/), which I find a bit more convenient than Tmux in a tiling windows manager such as i3.

---

<div class="post-metadata">

**Author:** ![fredcallaway](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fredcallaway/32/20304_2.png) [@fredcallaway](https://discourse.julialang.org/u/fredcallaway)\
**Post date:** [March 3, 2025, 11:11pm UTC](https://discourse.julialang.org/t/julia-developing-workflow-with-vim-and-terminal/126242/19 "2025-03-03T23:11:09Z")

</div>

To give a bit more detail on the “send to repl” workflow (e.g. using vim-slime). The goal is to never type code into the REPL. Instead you always write from the comfort of your editor, and send lines/chunks of code straight to the REPL.

The workflow then looks like this:

- identify a function you want to implement
- create a temporary file (e.g. scratch.jl)
- define variables for the arguments to your function

```jl
list = [1,2,3,2,1]
window_size = 3

```

- work out the function line by line, executing each line as you go to check that intermediate results make sense
- often times, you will write code that would never be in the final function, for the purposes of looking inside complex data structures (you’ll delete this code when you’re done), or defining loop variables
- when you’re getting close, you can switch to executing the full file (set up a shortcut to send `include("scratch.jl")` to the REPL (or perhaps use the path of the active file). This should display the value of the last line (which will become your returned value).
- when it seems to work, wrap it all in a function and put it in a file for stable code
- at this point its easy to write a very simple regression test that just asserts the function produces the output you saw it produce for the simple inputs you’ve tried

The workflow becomes a lot more powerful if your send-to-REPL package supports sending full “cells” or “chunks”: blocks of code delimited by a marker, often `# %%`. Then you essentially have a notebook-like interface in your editor, which you can also use for developing library code. But you can always just select a block of code you want to send.

Debugging is also really easy with this workflow. If you get an error in a personal function, throw an `@infiltrate` line (see also `@infiltry`) in the function _before the error line_. You can then edit and run the downstream lines to try to fix the problem interactively, using the same workflow as you used to originally develop the function. This is much faster than using Debugger.jl because everything runs in compiled mode. Not to mention, when you’ve “figured it out” in the debuggin session, your file _already contains the fixed code!_.

I also sometimes use `@infiltrate` to get my REPL into the loop of code I’m currently developing in scratch.jl

---

<div class="post-metadata">

**Author:** ![DoktorMike](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/doktormike/32/2736_2.png) [@DoktorMike](https://discourse.julialang.org/u/DoktorMike)\
**Post date:** [March 4, 2025, 6:59pm UTC](https://discourse.julialang.org/t/julia-developing-workflow-with-vim-and-terminal/126242/20 "2025-03-04T18:59:44Z")

</div>

This is also my workflow. It works really well as long as you don’t need to redefine structs in that development (requires a restart of the REPL). But overall I’m pretty happy. Neovim plus tmux plus slime = joy.

[Next page](https://discourse.julialang.org/t/julia-developing-workflow-with-vim-and-terminal/126242.md?page=2)
