Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
`--languages=julia,julia-repl,jldoctest,@example,@repl,@setup,@eval`. The corresponding
`languages` keyword argument is also accepted by `Runic.format_string` and
`Runic.format_file`.
- `git-runic` now accepts the `--docstrings` and `--languages` options, with the same
meaning as for `runic`. The defaults can be configured with the `runic.docstrings` and
`runic.languages` git-config settings, analogous to the existing `runic.extensions`.

## [v1.11.1] - 2026-09-23
### Fixed
Expand Down
67 changes: 60 additions & 7 deletions src/GitRunic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

module GitRunic

import ..Context, ..format_tree!, ..format_markdown, ..is_markdown_file, ..MainError
import ..Context, ..format_tree!, ..format_markdown, ..is_markdown_file, ..MainError,
..DEFAULT_MARKDOWN_LANGUAGES
import ..JuliaSyntax

const USAGE = "git runic [OPTIONS] [<commit>] [<commit>|--staged] [--] [<file>...]"
Expand All @@ -33,7 +34,9 @@ second <commit> that differ from the first <commit>.

The following git-config settings set the default of the corresponding option:
runic.commit
runic.docstrings
runic.extensions
runic.languages
"""

const TEMP_INDEX_BASENAME = "runic-index"
Expand Down Expand Up @@ -337,7 +340,10 @@ end
# Format a file using Runic in-process and store the result as a git blob.
# revision is nothing (workdir), "" (index/staged), or a commit SHA.
# Returns the blob SHA.
function runic_to_blob(filename, line_ranges::Vector{UnitRange{Int}}; revision = nothing)
function runic_to_blob(
filename, line_ranges::Vector{UnitRange{Int}}; revision = nothing,
docstrings::Bool = false, languages::Vector{String} = DEFAULT_MARKDOWN_LANGUAGES,
)
# Get source content from the working directory or a git object
if revision !== nothing
show_args = String["git", "cat-file", "blob", "$(revision):$(filename)"]
Expand All @@ -355,9 +361,14 @@ function runic_to_blob(filename, line_ranges::Vector{UnitRange{Int}}; revision =
local fmt_io::IO
try
if is_markdown_file(filename)
fmt_io = IOBuffer(format_markdown(src_str; line_ranges = line_ranges))
fmt_io = IOBuffer(
format_markdown(src_str; line_ranges = line_ranges, languages = languages)
)
else
ctx = Context(src_str; line_ranges = line_ranges, filename = filename)
ctx = Context(
src_str; line_ranges = line_ranges, filename = filename,
docstrings = docstrings, languages = languages,
)
format_tree!(ctx)
fmt_io = seekstart(ctx.fmt_io)
end
Expand All @@ -380,12 +391,17 @@ function runic_to_blob(filename, line_ranges::Vector{UnitRange{Int}}; revision =
end

# Format all changed files in-process and save results to a new git tree.
function run_runic_and_save_to_tree(changed_lines, revision)
function run_runic_and_save_to_tree(
changed_lines, revision; docstrings::Bool, languages::Vector{String},
)
index_info_lines = String[]
modes = file_modes(keys(changed_lines), revision)
for (filename, line_ranges) in changed_lines
mode = modes[filename]
blob_id = runic_to_blob(filename, line_ranges; revision = revision)
blob_id = runic_to_blob(
filename, line_ranges; revision = revision, docstrings = docstrings,
languages = languages,
)
push!(index_info_lines, "$(mode) $(blob_id)\t$(filename)")
end
return create_tree(index_info_lines, "--index-info")
Expand Down Expand Up @@ -531,8 +547,12 @@ function print_help()
println(io, " --commit COMMIT default commit if none is specified (default: HEAD)")
println(io, " --diff print a diff instead of applying the changes")
println(io, " --diffstat print a diffstat instead of applying the changes")
println(io, " --docstrings format code blocks in docstrings")
println(io, " --extensions LIST comma-separated list of file extensions (default: jl)")
println(io, " -f, --force allow changes to unstaged files")
println(io, " --languages LIST comma-separated list of code block languages treated")
println(io, " as Julia code in Markdown files and docstrings")
println(io, " (default: $(join(DEFAULT_MARKDOWN_LANGUAGES, ",")))")
println(io, " -p, --patch select hunks interactively")
println(io, " -q, --quiet print less information")
println(io, " --staged, --cached format lines in the stage instead of the working dir")
Expand All @@ -541,6 +561,26 @@ function print_help()
return println(io, " diff from last common commit (requires two commits)")
end

# Parse a git-config style boolean (see `git help config`, "boolean").
function parse_config_bool(key::String, value::String)
v = lowercase(strip(value))
v in ("true", "yes", "on", "1") && return true
v in ("false", "no", "off", "0", "") && return false
return die("invalid boolean value for `$key`: $value")
end

# Parse a comma-separated list of languages, mirroring `--languages` in `Runic.main`.
function parse_languages(value::AbstractString)
languages = String[]
for part in split(value, ',')
s = strip(part)
isempty(s) && continue
push!(languages, String(s))
end
isempty(languages) && die("`--languages` requires at least one language")
return languages
end

function main(argv)
try
return _main(argv)
Expand All @@ -567,6 +607,8 @@ function _main(argv)
# Defaults, overridden by git config or command-line flags
default_commit = get(config, "runic.commit", "HEAD")
extensions = get(config, "runic.extensions", "jl")
docstrings = parse_config_bool("runic.docstrings", get(config, "runic.docstrings", "false"))
languages = get(config, "runic.languages", join(DEFAULT_MARKDOWN_LANGUAGES, ","))

diff_mode = false
diffstat_mode = false
Expand Down Expand Up @@ -609,6 +651,13 @@ function _main(argv)
elseif arg == "--extensions"
i += 1; i > length(argv) && die("expected argument after --extensions")
extensions = argv[i]
elseif arg == "--docstrings"
docstrings = true
elseif (m = match(r"^--languages=(.*)$", arg); m !== nothing)
languages = String(m.captures[1]::SubString)
elseif arg == "--languages"
i += 1; i > length(argv) && die("expected argument after --languages")
languages = argv[i]
elseif startswith(arg, '-')
die("unknown option: $arg")
else
Expand All @@ -617,6 +666,8 @@ function _main(argv)
i += 1
end

language_list = parse_languages(languages)

commits, files = interpret_args(positional, dash_dash, default_commit)

if length(commits) > 2
Expand Down Expand Up @@ -677,7 +728,9 @@ function _main(argv)
revision = nothing
end

new_tree = run_runic_and_save_to_tree(changed_lines, revision)
new_tree = run_runic_and_save_to_tree(
changed_lines, revision; docstrings = docstrings, languages = language_list,
)

if verbose >= 1
println("old tree: $old_tree")
Expand Down
54 changes: 54 additions & 0 deletions test/gitrunictests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,60 @@ end
end
end

@testset "--docstrings option and runic.docstrings config" begin
src = "\"\"\"\n```julia\nx=1\n```\n\"\"\"\nf(x) = x\n"
fmt = "\"\"\"\n```julia\nx = 1\n```\n\"\"\"\nf(x) = x"
with_repository() do
write("doc.jl", src)
GitRunic.git(["add", "doc.jl"])
# Docstrings are not formatted by default
@test GitRunic._main(["--staged", "--quiet"]) == 0
@test GitRunic._main(["--staged", "--quiet", "--docstrings"]) == 1
@test GitRunic.git(["show", ":doc.jl"]) == fmt
end
with_repository() do
write("doc.jl", src)
GitRunic.git(["add", "doc.jl"])
GitRunic.git(["config", "runic.docstrings", "yes"])
@test GitRunic._main(["--staged", "--quiet"]) == 1
@test GitRunic.git(["show", ":doc.jl"]) == fmt
GitRunic.git(["config", "runic.docstrings", "maybe"])
@test_throws Runic.MainError GitRunic._main(["--staged", "--quiet"])
end
end

@testset "--languages option and runic.languages config" begin
src = "```@example\nx=1\n```\n\n```julia\ny=2\n```\n"
with_repository() do
write("doc.md", src)
GitRunic.git(["add", "doc.md"])
# Only the default languages are formatted
@test GitRunic._main(["--staged", "--quiet", "--extensions=md"]) == 1
@test GitRunic.git(["show", ":doc.md"]) == "```@example\nx=1\n```\n\n```julia\ny = 2\n```"
write("doc.md", src)
GitRunic.git(["add", "doc.md"])
@test GitRunic._main(
["--staged", "--quiet", "--extensions=md", "--languages", "julia, @example"]
) == 1
@test GitRunic.git(["show", ":doc.md"]) == "```@example\nx = 1\n```\n\n```julia\ny = 2\n```"
# The list replaces the default
write("doc.md", src)
GitRunic.git(["add", "doc.md"])
@test GitRunic._main(["--staged", "--quiet", "--extensions=md", "--languages=@example"]) == 1
@test GitRunic.git(["show", ":doc.md"]) == "```@example\nx = 1\n```\n\n```julia\ny=2\n```"
@test_throws Runic.MainError GitRunic._main(["--staged", "--quiet", "--languages="])
@test_throws Runic.MainError GitRunic._main(["--staged", "--quiet", "--languages"])
end
with_repository() do
write("doc.md", src)
GitRunic.git(["add", "doc.md"])
GitRunic.git(["config", "runic.extensions", "md"])
GitRunic.git(["config", "runic.languages", "julia,@example"])
@test GitRunic._main(["--staged", "--quiet"]) == 1
@test GitRunic.git(["show", ":doc.md"]) == "```@example\nx = 1\n```\n\n```julia\ny = 2\n```"
end
end

@testset "staged formatting uses index file types" begin
if Sys.isunix()
with_repository() do
Expand Down
Loading