# @run macro?

**URL:** https://discourse.julialang.org/t/run-macro/133861
**Category:** General Usage
**Tags:** question, macros, repl
**Created:** [November 14, 2025, 3:29am UTC](https://discourse.julialang.org/t/run-macro/133861 "2025-11-14T03:29:45Z")
**Posts on this page:** 10
**Page:** 1

<div class="post-metadata">

### Author: ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)
#### Post date: [November 14, 2025, 3:29am UTC](https://discourse.julialang.org/t/run-macro/133861/1 "2025-11-14T03:29:45Z")

</div>

How could a run macro be defined that executes a script?

It should just include the file. Would something like:

```julia-auto
@run myscript.jl

```

be possible as replacement for

```julia-auto
include("myscript.jl")

```

?

That would save 7 keystrokes in the REPL.

---

<div class="post-metadata">

### Author: ![technocrat](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/technocrat/32/220947_2.png) [@technocrat](https://discourse.julialang.org/u/technocrat)
#### Post date: [November 14, 2025, 4:14am UTC](https://discourse.julialang.org/t/run-macro/133861/2 "2025-11-14T04:14:05Z")

</div>

```julia-auto
macro run(script)
    return :(include($script))
end
@run "scripts/tmp.jl"

```

I don’t see a way of avoiding the quotation marks in the invocation, however.

---

<div class="post-metadata">

### Author: ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)
#### Post date: [November 14, 2025, 4:18am UTC](https://discourse.julialang.org/t/run-macro/133861/3 "2025-11-14T04:18:32Z")

</div>

Function APIs are way more flexible, from my standpoint.

Just like `JLD2.@load "a.jld2"` can be replaced with `first(JLD2.load("a.jld2")).second`.

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [November 14, 2025, 5:05am UTC](https://discourse.julialang.org/t/run-macro/133861/4 "2025-11-14T05:05:47Z")

</div>

> [@technocrat](#):
>
> I don’t see a way of avoiding the quotation marks in the invocation, however.

It’s _mostly_ possible if we painstakingly validate an `Expr` with “variables” and “operators” like `/`, `\`, `.` etc, but it seems like a lot of trouble just to hit a `ParseError` at a very common `C:\`. If the point is to save keystrokes, then a single-character alias for `include` or a nonstandard string literal saves more. To edit your macro, the latter looks like:

```julia-auto
julia> macro run_str(script)
         esc( :(include($script)) )
       end

julia> @macroexpand run"C:\blah.jl"
:(include("C:\\blah.jl"))

```

Note that non-standard string literals are parsed like `raw` strings (which are actually implemented with no processing) unlike normal `String`s, so this doesn’t allow string interpolation or most unescaping.

---

<div class="post-metadata">

### Author: ![cstjean](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cstjean/32/1444_2.png) [@cstjean](https://discourse.julialang.org/u/cstjean)
#### Post date: [November 14, 2025, 5:46am UTC](https://discourse.julialang.org/t/run-macro/133861/5 "2025-11-14T05:46:24Z")

</div>

`@run my_file.jl` wouldn’t TAB-complete in the REPL, which saves far more keystrokes and errors than this macro would IMHO.

---

<div class="post-metadata">

### Author: ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)
#### Post date: [November 14, 2025, 7:51am UTC](https://discourse.julialang.org/t/run-macro/133861/6 "2025-11-14T07:51:27Z")

</div>

I like the custom string literal most. Nice: After TAB completion, you do not need to type the closing bracket.

```julia-auto
macro run_str(script)
    esc( :(include($script)) )
end

```

I put it in my `startup.jl` file in the `.julia/config` folder.

---

<div class="post-metadata">

### Author: ![PatrickHaecker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/patrickhaecker/32/222891_2.png) [@PatrickHaecker](https://discourse.julialang.org/u/PatrickHaecker)
#### Post date: [November 15, 2025, 8:42am UTC](https://discourse.julialang.org/t/run-macro/133861/7 "2025-11-15T08:42:13Z")

</div>

> [@ufechner7](#):
>
> That would save 7 keystrokes in the REPL.

With cheating you can save 9 instead of the 7 (isn’t it 6?) keystrokes by just entering the file name in the REPL and hitting `alt`+`i`:

```julia
using REPL: LineEdit
@async Base.active_repl.interface.modes[1].keymap_dict['\e']['i'] = function (state, _, _)
    io = LineEdit.buffer(state)
    write(io, "include(\"", io |> take! |> String, "\")")
    LineEdit.commit_line(state)
    return :done
end

```

Replace `['i']` by `['r']` if you want to indicate `run` instead of `include`. The `@async` supports adding this to `startup.jl`. And obviously `includet` might be more useful than `include`. However, @cstjean’s point of missing TAB-complete still stands.

This is tested with Julia 1.11.7 and 1.12.1. Expect breakage with other Julia versions (as e.g. seen on nightly) due to using private identifiers.

I am not sure how seriously I am suggesting this. But I do think we should consider using REPL shortcuts more often in non-composable use cases like this.

---

<div class="post-metadata">

### Author: ![PatrickHaecker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/patrickhaecker/32/222891_2.png) [@PatrickHaecker](https://discourse.julialang.org/u/PatrickHaecker)
#### Post date: [November 15, 2025, 9:51am UTC](https://discourse.julialang.org/t/run-macro/133861/8 "2025-11-15T09:51:15Z")

</div>

> [@PatrickHaecker](#):
>
> However, @cstjean’s point of missing TAB-complete still stands.

This sounded too much of a challenge to let it pass. So with

```julia
using REPL
using REPL.REPLCompletions: completions, complete_path, PathCompletion, ModuleCompletion

const completions_orig_world = Base.get_world_counter()

function REPL.REPLCompletions.completions(string::String, pos::Int, context_module::Module=Main, shift::Bool=true, hint::Bool=false)
    # Get original completions.
    ret, range, should_complete = Base.invoke_in_world(completions_orig_world, completions, string, pos, context_module, shift, hint)

    # If we don't already have path completions, add them.
    has_paths = any(x -> x isa PathCompletion, ret)
    if !has_paths
        paths, dir, path_success = complete_path(string[1:pos])
        if path_success
            # In subfolder completion, do not suggest anything else than paths.
            contains(string[1:pos], '/') && filter!(x -> x isa PathCompletion, ret)

            # In subfolder completion, do not suggest strange entries.
            filter!(x -> !(x isa ModuleCompletion && x.parent == Main && x.mod in ("/", "//")), ret)

            # Join directory with paths.
            paths = (PathCompletion(joinpath(dir, p.path)) for p in paths)
            append!(ret, paths)
            range = 1:pos
        end
    end

    return ret, range, should_complete
end

```

it also TAB-completes here in 1.12.1. There are many reasons why this is dirty and yet I find it fascinating. Maybe we should think about making more of completions a public API? It would be interesting to see more packages adding specific completions.

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [November 15, 2025, 7:10pm UTC](https://discourse.julialang.org/t/run-macro/133861/9 "2025-11-15T19:10:24Z")

</div>

> [@PatrickHaecker](#):
>
> There are many reasons why this is dirty

Just to list the ones that come to my mind:

- It’s wholly interactive, though it’s not much of a problem in this case to write out the `include` call in a file. Generally, a simple public function could be provided for scripts, and the REPL’s generated code would just call it.
- As type piracy, this is hard to distribute through packages without clashes. Maybe a different package decides that `ALT + i` does something different.
- `ALT + i` is too quick of a decision to generate and execute code, which is vulnerable to typos (`ALT + o` could do something else we’d rather not) or misremembering (was it `ALT + t`, nope, that swaps the file name and extension for some reason).

I don’t know of a way to address all those at once to save keystrokes, and maybe there isn’t one. However, the last one could be improved with a custom prompt mode. We still wouldn’t see the generated code, but there is at least a visible indicator of its intention and we decide to do it separately.

```julia-auto
julia> # ALT+i or whatever keystrokes are necessary

include> helloworld.jl # ENTER to go through with it
hello world

include> # ready for more files or backspace to return to julia> prompt

```

It’d be nice if custom TAB-completion can be isolated to a custom prompt mode instead of changing the builtin prompt modes, but I don’t know how.

---

<div class="post-metadata">

### Author: ![PatrickHaecker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/patrickhaecker/32/222891_2.png) [@PatrickHaecker](https://discourse.julialang.org/u/PatrickHaecker)
#### Post date: [November 16, 2025, 5:42am UTC](https://discourse.julialang.org/t/run-macro/133861/10 "2025-11-16T05:42:31Z")

</div>

> [@Benny](#):
>
> - It’s wholly interactive, though it’s not much of a problem in this case to write out the `include` call in a file. Generally, a simple public function could be provided for scripts, and the REPL’s generated code would just call it.

In general providing functionality as key-combination would indeed be problematic, as it won’t compose. In this case, I think the public function already exists as `include`.  
Packages do effectively already register a callback to `keymap_dict`. It does not need to be public, because only the packages need to know about it in the code itself.

> [@](#):
>
> - As type piracy, this is hard to distribute through packages without clashes. Maybe a different package decides that `ALT + i` does something different.

This is already the case for all packages which add a REPL shortcut or a REPL mode (see e.g. TerminalPager.jl or Pkg.jl). It might make sense to add a coordination package for REPL shortcuts and for REPL modes similar to FileIO.jl.

> [@](#):
>
> - `ALT + i` is too quick of a decision to generate and execute code, which is vulnerable to typos (`ALT + o` could do something else we’d rather not) or misremembering (was it `ALT + t`, nope, that swaps the file name and extension for some reason).

Users should at least be able to activate/deactivate this feature. There are probably not too many users which have highly problematic scripts lying around in their working directory and there are are other key combinations which can in the worst case destroy some hours of work (e.g. `ctrl`+`d`). But I agree that this feature can be unparalleled in how much you can damage yourself with little effort.

> It’d be nice if custom TAB-completion can be isolated to a custom prompt mode instead of changing the builtin prompt modes, but I don’t know how.

This is quite some work, but not too difficult when you use an existing implementation as a template, e.g. the [TerminalPager.jl implementation](https://github.com/ronisbr/TerminalPager.jl/blob/main/src/repl.jl). You create a new mode and either use your own `CompletionProvider` or add the shortcut to the REPL mode of choice. I used `modes[1]` above which is the regular REPL mode.

I opted against a REPL mode, because it’s both more work for the initial implementation and for the user. Additionally, I think some features which are currently implemented as a REPL mode are better implemented as a shortcut (see e.g. the help mode versus the inline help feature of TerminalPager.jl). However, the best solution would probably be to offer both and let the user decide. We already see that preferences vary for this `include` feature.

> There are many reasons why this is dirty

I had some additional reasons in my mind, e.g.

- the way how this patches the `REPL.REPLCompletions.completions` method, which has a fully specific signature, by using world age is interesting and might be worth its own macro(s) (and possibly package).  
In most of the cases (like this one), I think you should not notice any resulting world age issues, but there are more hideous cases which can hit you hard.  
Additionally, I think this will be slower than it could be (probably does not matter here), but this is the disadvantage of using fully-specific method signatures.
- I think we do not need to add path completions if the original `completions` already found some, but I am not sure.
- Checking only for `/` to see whether we already have a path is neither sufficient (e.g. division) nor necessary (e.g. Windows) for having a path.
- It would be cleaner if this last `filter!` was unnecessary, but I do not know why these entries are generated at all and how to avoid this.
