diff --git a/Project.toml b/Project.toml index 10d76ae7..48bb5a89 100644 --- a/Project.toml +++ b/Project.toml @@ -12,15 +12,18 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" [weakdeps] ConstructionBase = "187b0558-2788-49d3-abe0-74a17ed4e7c9" InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" +Latexify = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" [extensions] ConstructionBaseUnitfulExt = "ConstructionBase" InverseFunctionsUnitfulExt = "InverseFunctions" +LatexifyUnitfulExt = "Latexify" [compat] Aqua = "0.6.3" ConstructionBase = "1" InverseFunctions = "0.1" +Latexify = "0.15.1, 0.16" julia = "1" [extras] @@ -32,4 +35,4 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Aqua", "ConstructionBase", "InverseFunctions", "LinearAlgebra", "Test", "Random"] +test = ["Aqua", "ConstructionBase", "InverseFunctions", "LinearAlgebra", "Test", "Random", "Latexify"] diff --git a/ext/LatexifyUnitfulExt.jl b/ext/LatexifyUnitfulExt.jl new file mode 100644 index 00000000..0f300dd0 --- /dev/null +++ b/ext/LatexifyUnitfulExt.jl @@ -0,0 +1,450 @@ +#=========================================# +# Extension for Unitful.jl + Latexify.jl, # +# based on UnitfulLatexify.jl by # +# David Gustavsson (@gustaphe) # +#=========================================# +module LatexifyUnitfulExt + +using Unitful: + Unitful, + Unit, + Units, + AbstractQuantity, + AffineUnits, + AffineQuantity, + power, + abbr, + name, + tens, + sortexp, + unit, + NoDims, + ustrip, + @u_str, + genericunit, + has_unit_spacing +using Latexify: + Latexify, + @latexrecipe, + latexify, + _latexarray, + FancyNumberFormatter, + latexraw +using Latexify.LaTeXStrings: + LaTeXString + +import Latexify.latexify +import Base.(:*) + +""" +prefixes are listed in this dictionary +`(unitformat::Symbol, pow::Integer) => prefix::String` +""" +const prefixes = begin + Dict( + (:mathrm, -24) => "y", + (:mathrm, -21) => "z", + (:mathrm, -18) => "a", + (:mathrm, -15) => "f", + (:mathrm, -12) => "p", + (:mathrm, -9) => "n", + (:mathrm, -6) => "\\mu{}", + (:mathrm, -3) => "m", + (:mathrm, -2) => "c", + (:mathrm, -1) => "d", + (:mathrm, 0) => "", + (:mathrm, 1) => "D", + (:mathrm, 2) => "h", + (:mathrm, 3) => "k", + (:mathrm, 6) => "M", + (:mathrm, 9) => "G", + (:mathrm, 12) => "T", + (:mathrm, 15) => "P", + (:mathrm, 18) => "E", + (:mathrm, 21) => "Z", + (:mathrm, 24) => "Y", + (:siunitx, -24) => "\\yocto", + (:siunitx, -21) => "\\zepto", + (:siunitx, -18) => "\\atto", + (:siunitx, -15) => "\\femto", + (:siunitx, -12) => "\\pico", + (:siunitx, -9) => "\\nano", + (:siunitx, -6) => "\\micro", + (:siunitx, -3) => "\\milli", + (:siunitx, -2) => "\\centi", + (:siunitx, -1) => "\\deci", + (:siunitx, 0) => "", + (:siunitx, 1) => "\\deka", + (:siunitx, 2) => "\\hecto", + (:siunitx, 3) => "\\kilo", + (:siunitx, 6) => "\\mega", + (:siunitx, 9) => "\\giga", + (:siunitx, 12) => "\\tera", + (:siunitx, 15) => "\\peta", + (:siunitx, 18) => "\\exa", + (:siunitx, 21) => "\\zetta", + (:siunitx, 24) => "\\yotta", + (:siunitxsimple, -24) => "y", + (:siunitxsimple, -21) => "z", + (:siunitxsimple, -18) => "a", + (:siunitxsimple, -15) => "f", + (:siunitxsimple, -12) => "p", + (:siunitxsimple, -9) => "n", + (:siunitxsimple, -6) => "\\u", + (:siunitxsimple, -3) => "m", + (:siunitxsimple, -2) => "c", + (:siunitxsimple, -1) => "d", + (:siunitxsimple, 0) => "", + (:siunitxsimple, 1) => "D", + (:siunitxsimple, 2) => "h", + (:siunitxsimple, 3) => "k", + (:siunitxsimple, 6) => "M", + (:siunitxsimple, 9) => "G", + (:siunitxsimple, 12) => "T", + (:siunitxsimple, 15) => "P", + (:siunitxsimple, 18) => "E", + (:siunitxsimple, 21) => "Z", + (:siunitxsimple, 24) => "Y", + ) +end + +"""" +`unitnames` + +Unit names generally follow a simple scheme, but there are exceptions, listed in this +dictionary: `(unitformat::Symbol, name::Symbol) => unitname::String` +""" +const unitnames = begin + Dict( + (:mathrm, :Percent) => "\\%", + (:siunitxsimple, :Percent) => "\\%", + (:mathrm, :Degree) => "^{\\circ}", + (:siunitxsimple, :Degree) => "\\degree", + (:siunitx, :eV) => "\\electronvolt", + (:mathrm, :Ohm) => "\\Omega", + (:mathrm, :Celsius) => "^\\circ C", + (:siunitx, :Celsius) => "\\celsius", + (:siunitxsimple, :Celsius) => "\\celsius", + (:mathrm, :Fahrenheit) => "^\\circ F", + (:siunitx, :Fahrenheit) => "\\fahrenheit", + (:siunitxsimple, :Fahrenheit) => "\\fahrenheit", + (:siunitxsimple, :Angstrom) => "\\angstrom", + (:mathrm, :Angstrom) => "\\AA", + (:mathrm, :DoubleTurn) => "\\S", + (:mathrm, :Turn) => "\\tau", + (:mathrm, :HalfTurn) => "\\pi", + (:mathrm, :Quadrant) => "\\frac{\\pi}{2}", + (:mathrm, :Sextant) => "\\frac{\\pi}{3}", + (:mathrm, :Octant) => "\\frac{\\pi}{4}", + (:mathrm, :ClockPosition) => "\\frac{\\pi}{12}", + (:mathrm, :HourAngle) => "\\frac{\\pi}{24}", + (:mathrm, :CompassPoint) => "\\frac{\\pi}{32}", + (:mathrm, :Hexacontade) => "\\frac{\\pi}{60}", + (:mathrm, :BinaryRadian) => "\\frac{\\pi}{256}", + (:mathrm, :DiameterPart) => "\\oslash", # This is slightly wrong + (:mathrm, :Gradian) => "^g", + (:mathrm, :Arcminute) => "'", + (:mathrm, :Arcsecond) => "''", + (:mathrm, :ArcsecondShort) => "''", + ) +end + +function getunitname(p::T, unitformat) where {T<:Unit} + unitname = get(unitnames, (unitformat, name(p)), nothing) + isnothing(unitname) || return unitname + if unitformat === :siunitx + return "\\$(lowercase(String(name(p))))" + end + return abbr(p) +end +function listunits(::T) where {T<:Units} + return sortexp(T.parameters[1]) +end +""" +```julia +intersperse(t, delim) +``` +Create a vector whose elements alternate between the elements of `t` and `delim`, analogous +to `join` for strings. + +# Example +```julia +julia> intersperse((1, 2, 3, 4), :a) +[1, :a, 2, :a, 3, :a, 4] +``` +""" +function intersperse(t::T, delim::U) where {T,U} + iszero(length(t)) && return () + L = length(t) * 2 - 1 + out = Vector{Union{typeof.(t)...,U}}(undef, L) + out[1:2:L] .= t + out[2:2:L] .= delim + return out +end + +@latexrecipe function f(p::T; unitformat=:mathrm) where {T<:Unit} + prefix = prefixes[(unitformat, tens(p))] + pow = power(p) + unitname = getunitname(p, unitformat) + if unitformat === :mathrm + env --> :inline + if pow == 1//1 + expo = "" + else + expo = "^{$(latexify(pow; kwargs..., fmt="%g", env=:raw))}" + end + return LaTeXString("\\mathrm{$prefix$unitname}$expo") + end + env --> :raw + if unitformat === :siunitx + per = pow < 0 ? "\\per" : "" + pow = abs(pow) + expo = pow == 1//1 ? "" : "\\tothe{$(latexify(pow; kwargs..., fmt="%g", env=:raw))}" + else + per = "" + expo = pow == 1//1 ? "" : "^{$(latexify(pow; kwargs..., fmt="%g", env=:raw))}" + end + return LaTeXString("$per$prefix$unitname$expo") +end + +@latexrecipe function f( + u::T; unitformat=:mathrm, permode=:power, siunitxlegacy=false +) where {T<:Units} + if unitformat === :mathrm + env --> :inline + return Expr(:latexifymerge, NakedUnits(u)) + end + env --> :raw + siunitxlegacy && return Expr(:latexifymerge, "\\si{", NakedUnits(u), "}") + return Expr(:latexifymerge, "\\unit{", NakedUnits(u), "}") +end + +@latexrecipe function f( + q::T; unitformat=:mathrm, siunitxlegacy=false +) where {T<:AbstractQuantity} + if unitformat === :mathrm + env --> :inline + fmt --> FancyNumberFormatter() + return Expr( + :latexifymerge, + q.val, + has_unit_spacing(unit(q)) ? "\\;" : nothing, + NakedUnits(unit(q)), + ) + end + env --> :raw + siunitxlegacy && + return Expr(:latexifymerge, "\\SI{", q.val, "}{", NakedUnits(unit(q)), "}") + return Expr(:latexifymerge, "\\qty{", q.val, "}{", NakedUnits(unit(q)), "}") +end + +struct NakedUnits + u::Units +end + +@latexrecipe function f(u::T; unitformat=:mathrm, permode=:power) where {T<:NakedUnits} + unitlist = listunits(u.u) + if unitformat in (:siunitx, :siunitxsimple) || permode === :power + return Expr(:latexifymerge, intersperse(unitlist, delimiters[unitformat])...) + end + + numunits = [x for x in unitlist if power(x) >= 0] + denunits = [typeof(x)(tens(x), -power(x)) for x in unitlist if power(x) < 0] + + numerator = intersperse(numunits, delimiters[unitformat]) + if iszero(length(denunits)) + return Expr(:latexifymerge, numerator...) + end + if iszero(length(numunits)) + numerator = [1] + end + denominator = intersperse(denunits, delimiters[unitformat]) + + if permode === :slash + return Expr(:latexifymerge, numerator..., "\\,/\\,", denominator...) + end + if permode === :frac + return Expr(:latexifymerge, "\\frac{", numerator..., "}{", denominator..., "}") + end + return error("permode $permode undefined.") +end + +const delimiters = Dict{Symbol,String}( + :mathrm => "\\,", :siunitx => "", :siunitxsimple => "." +) + +@latexrecipe function f(p::T; unitformat=:mathrm) where {T<:Unit{:One,NoDims}} + return "" +end + +@latexrecipe function f( + p::T; unitformat=:mathrm +) where {T<:Units{(Unit{:One,NoDims}(0, 1),),NoDims,nothing}} + return "" +end + +@latexrecipe function f( + q::T; unitformat=:mathrm +) where {T<:AbstractQuantity{<:Number,NoDims,<:Units{(),NoDims,nothing}}} + if unitformat === :mathrm + env --> :inline + fmt --> FancyNumberFormatter() + return ustrip(q) + end + env --> :raw + return Expr(:latexifymerge, "\\num{", ustrip(q), "}") +end + +@latexrecipe function f( + q::T; unitformat=:mathrm +) where { + T<:AbstractQuantity{<:Number,NoDims,<:Units{(Unit{:One,NoDims}(0, 1),),NoDims,nothing}} +} + if unitformat === :mathrm + env --> :inline + fmt --> FancyNumberFormatter() + return ustrip(q) + end + env --> :raw + return Expr(:latexifymerge, "\\num{", ustrip(q), "}") +end + +@latexrecipe function f( # Array{Quantity{U}} + a::AbstractArray{<:AbstractQuantity{N,D,U}}; + unitformat=:mathrm, +) where {N<:Number,D,U} + # Array of quantities with the same unit + env --> :equation + return Expr( + :latexifymerge, + ustrip.(a) * u"One", + has_unit_spacing(first(a)) ? "\\;" : "", + unit(first(a)), + ) +end + +@latexrecipe function f( # Array{Quantity{One} + a::T; + unitformat=:mathrm, +) where { + T<:AbstractArray{<:AbstractQuantity{N,D,U}} +} where {N<:Number,D,U<:Units{(Unit{:One,NoDims}(0, 1),),NoDims,nothing}} + env --> :equation + if unitformat in (:siunitx, :siunitxsimple) + return latexify.(a; kwargs..., unitformat=unitformat, env=:raw) + end + return ustrip.(a) +end + +@latexrecipe function f( # Range{Quantity{U}} + r::AbstractRange{<:AbstractQuantity{N,D,U}}; + unitformat=:mathrm, +) where {N<:Number,D,U} + if unitformat in (:siunitx, :siunitxsimple) + env --> :raw + return Expr( + :latexifymerge, + "\\qtyrange{", + r.start.val, + "}{", + r.stop.val, + "}{", + NakedUnits(unit(r.start)), + "}", + ) + end + return collect(r) +end + +@latexrecipe function f( # Range{Quantity{One}} + r::T; + unitformat=:mathrm, +) where { + T<:AbstractRange{<:AbstractQuantity{N,D,U}} +} where {N<:Number,D,U<:Units{(Unit{:One,NoDims}(0, 1),),NoDims,nothing}} + if unitformat in (:siunitx, :siunitxsimple) + env --> :raw + return Expr(:latexifymerge, "\\numrange{", r.start.val, "}{", r.stop.val, "}") + end + return ustrip.(r) +end + +@latexrecipe function f( # Tuple{Quantity{U}} + l::Tuple{T,Vararg{T}}; + unitformat=:mathrm, +) where {T<:AbstractQuantity{N,D,U}} where {N<:Number,D,U} + if unitformat in (:siunitx, :siunitxsimple) + env --> :raw + return Expr( + :latexifymerge, + "\\qtylist{", + intersperse(ustrip.(l), ";")..., + "}{", + NakedUnits(unit(first(l))), + "}", + ) + end + return collect(l) +end + +@latexrecipe function f( # Tuple{Quantity{One}} + l::Tuple{T,Vararg{T}}; + unitformat=:mathrm, +) where { + T<:AbstractQuantity{N,D,U} +} where {N<:Number,D,U<:Units{(Unit{:One,NoDims}(0, 1),),NoDims,nothing}} + if unitformat in (:siunitx, :siunitxsimple) + env --> :raw + return Expr(:latexifymerge, "\\numlist{", intersperse(ustrip.(l), ";")..., "}") + end + return ustrip.(l) +end + +@latexrecipe function f(u::T; unitformat=:mathrm) where {T<:AffineUnits} + if u == Unitful.°C + unitname = :Celsius + elseif u == Unitful.°F + unitname = :Fahrenheit + else + # If it's not celsius or farenheit, let it do the default thing + return genericunit(u) + end + if unitformat === :mathrm + env --> :inline + return LaTeXString(unitnames[(unitformat, unitname)]) + end + env --> :raw + return Expr(:latexifymerge, "\\unit{", unitnames[(unitformat, unitname)], "}") +end + +@latexrecipe function f(q::T; unitformat=:mathrm) where {T<:AffineQuantity} + u = unit(q) + if u == Unitful.°C + unitname = :Celsius + elseif u == Unitful.°F + unitname = :Fahrenheit + else + # If it's not celsius or farenheit, let it do the default thing + return genericunit(u) + end + if unitformat === :mathrm + env --> :inline + fmt --> FancyNumberFormatter() + return Expr( + :latexifymerge, q.val, "\\;\\mathrm{", unitnames[(unitformat, unitname)], "}" + ) + end + env --> :raw + return Expr( + :latexifymerge, "\\qty{", q.val, "}{", unitnames[(unitformat, unitname)], "}" + ) +end + +@latexrecipe function f(l::AbstractString, u::Units; labelformat=:slash) + labelformat === :slash && return Expr(:latexifymerge, l, "\\;\\left/\\;", u, "\\right.") + labelformat === :square && return Expr(:latexifymerge, l, "\\;\\left[", u, "\\right]") + labelformat === :round && return Expr(:latexifymerge, l, "\\;\\left(", u, "\\right)") + labelformat === :frac && return Expr(:latexifymerge, "\\frac{", l, "}{", u, "}") +end + +end diff --git a/src/Unitful.jl b/src/Unitful.jl index e8919035..b3e9a0cc 100644 --- a/src/Unitful.jl +++ b/src/Unitful.jl @@ -72,6 +72,7 @@ include("dates.jl") if !isdefined(Base, :get_extension) include("../ext/ConstructionBaseUnitfulExt.jl") include("../ext/InverseFunctionsUnitfulExt.jl") + include("../ext/LatexifyUnitfulExt.jl") end end diff --git a/src/one.jl b/src/one.jl new file mode 100644 index 00000000..4dbbcbde --- /dev/null +++ b/src/one.jl @@ -0,0 +1,17 @@ +# Multiplication with u"One" is identity for all unitful numbers (but not for unitless) + +*(q::AbstractQuantity, ::Units{(Unit{:One,NoDims}(0, 1),),NoDims,nothing}) = q +function *( + a::AbstractQuantity, b::T +) where { + T<:AbstractQuantity{<:Number,NoDims,<:Units{(Unit{:One,NoDims}(0, 1),),NoDims,nothing}} +} + return a * b.val +end +function *( + b::T, a::AbstractQuantity +) where { + T<:AbstractQuantity{<:Number,NoDims,<:Units{(Unit{:One,NoDims}(0, 1),),NoDims,nothing}} +} + return b.val * a +end diff --git a/src/pkgdefaults.jl b/src/pkgdefaults.jl index 38dab79f..a25aeaef 100644 --- a/src/pkgdefaults.jl +++ b/src/pkgdefaults.jl @@ -368,6 +368,10 @@ const q = 1.602_176_634e-19*C # CODATA 2018; `e` means 2.718... \nDimension: 𝐌 𝐋^-1 𝐓^-2. \nSee also: [`Unitful.atm`](@ref)." @unit Torr "Torr" Torr 101325Pa//760 true true +" Unitful.One +\nUnitless number, less fragile than NoUnits. +\nDimension: [`Unitful.NoDims`](@ref)." +@unit One "" One 1 false # Constants (2018 CODATA values) (uncertainties in final digits) " Unitful.c0 diff --git a/test/runtests.jl b/test/runtests.jl index 75a54750..57cc020b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,5 +1,5 @@ using Unitful -using Test, LinearAlgebra, Random, ConstructionBase, InverseFunctions +using Test, LinearAlgebra, Random, ConstructionBase, InverseFunctions, Latexify import Unitful: DimensionError, AffineError import Unitful: LogScaled, LogInfo, Level, Gain, MixedUnits, Decibel import Unitful: FreeUnits, ContextUnits, FixedUnits, AffineUnits, AffineQuantity @@ -40,6 +40,8 @@ using Dates: Month, Year, CompoundPeriod +import Latexify.LaTeXStrings: LaTeXString + const colon = Base.:(:) @testset "Construction" begin @@ -2058,6 +2060,181 @@ end @test @doc(Unitful.Å) == @doc(Unitful.Å) end +@testset "Latexify" begin + +function unitfullatexifytest(val, mathrmexpected, siunitxexpected, siunitxsimpleexpected) + @test latexify(val; unitformat=:mathrm) == + LaTeXString(replace(mathrmexpected, "\r\n" => "\n")) + @test latexify(val; unitformat=:siunitx) == + LaTeXString(replace(siunitxexpected, "\r\n" => "\n")) + @test latexify(val; unitformat=:siunitxsimple) == + LaTeXString(replace(siunitxsimpleexpected, "\r\n" => "\n")) +end + +@testset "Latexify units" begin + unitfullatexifytest( + u"H*J/kg", + raw"$\mathrm{H}\,\mathrm{J}\,\mathrm{kg}^{-1}$", + raw"\unit{\henry\joule\per\kilo\gram}", + raw"\unit{H.J.kg^{-1}}", + ) + unitfullatexifytest( + 24.7e9u"Gm/s^2", + raw"$2.47 \cdot 10^{10}\;\mathrm{Gm}\,\mathrm{s}^{-2}$", + raw"\qty{2.47e10}{\giga\meter\per\second\tothe{2}}", + raw"\qty{2.47e10}{Gm.s^{-2}}", + ) + unitfullatexifytest( + 6.02214076e23u"One", + raw"$6.022 \cdot 10^{23}$", + raw"\num{6.02214076e23}", + raw"\num{6.02214076e23}", + ) + unitfullatexifytest( + u"percent", raw"$\mathrm{\%}$", raw"\unit{\percent}", raw"\unit{\%}" + ) + unitfullatexifytest( + 2u"°C", raw"$2\;\mathrm{^\circ C}$", raw"\qty{2}{\celsius}", raw"\qty{2}{\celsius}" + ) + unitfullatexifytest( + 1u"°", raw"$1\mathrm{^{\circ}}$", raw"\qty{1}{\degree}", raw"\qty{1}{\degree}" + ) + unitfullatexifytest( + [1, 2, 3]*m, + raw""" + \begin{equation} + \left[ + \begin{array}{c} + 1 \\ + 2 \\ + 3 \\ + \end{array} + \right]\;\mathrm{m} + \end{equation} + """, + raw""" + \begin{equation} + \left[ + \begin{array}{c} + \num{1} \\ + \num{2} \\ + \num{3} \\ + \end{array} + \right]\;\unit{\meter} + \end{equation} + """, + raw""" + \begin{equation} + \left[ + \begin{array}{c} + \num{1} \\ + \num{2} \\ + \num{3} \\ + \end{array} + \right]\;\unit{m} + \end{equation} + """, + ) + unitfullatexifytest((1:3)*m, + raw""" + \begin{equation} + \left[ + \begin{array}{c} + 1 \\ + 2 \\ + 3 \\ + \end{array} + \right]\;\mathrm{m} + \end{equation} + """, + raw"\qtyrange{1}{3}{\meter}", + raw"\qtyrange{1}{3}{m}", + ) + unitfullatexifytest((1,2,3).*m, + raw""" + \begin{equation} + \left[ + \begin{array}{c} + 1 \\ + 2 \\ + 3 \\ + \end{array} + \right]\;\mathrm{m} + \end{equation} + """, + raw"\qtylist{1;2;3}{\meter}", + raw"\qtylist{1;2;3}{m}", + ) + unitfullatexifytest((1:3)*u"One", + raw""" + \begin{equation} + \left[ + \begin{array}{c} + 1 \\ + 2 \\ + 3 \\ + \end{array} + \right] + \end{equation} + """, + raw"\numrange{1}{3}", + raw"\numrange{1}{3}", + ) + unitfullatexifytest((1,2,3).*u"One", + raw""" + \begin{equation} + \left[ + \begin{array}{c} + 1 \\ + 2 \\ + 3 \\ + \end{array} + \right] + \end{equation} + """, + raw"\numlist{1;2;3}", + raw"\numlist{1;2;3}", + ) + @test latexify(24.7e9u"Gm/s^2"; fmt="%.1e") == + LaTeXString("\$2.5e+10\\;\\mathrm{Gm}\\,\\mathrm{s}^{-2}\$") + + @test latexify(5.9722e24u"kg"; unitformat=:siunitx, siunitxlegacy=true) == + raw"\SI{5.9722e24}{\kilo\gram}" + @test latexify(u"eV"; unitformat=:siunitx, siunitxlegacy=true) == + raw"\si{\electronvolt}" +end + +@testset "permode" begin + p = 5u"m^3*s^2/H/kg^4" + @test latexify(p) == LaTeXString( + raw"$5\;\mathrm{m}^{3}\,\mathrm{s}^{2}\,\mathrm{kg}^{-4}\,\mathrm{H}^{-1}$" + ) + @test latexify(p; permode=:power) == LaTeXString( + raw"$5\;\mathrm{m}^{3}\,\mathrm{s}^{2}\,\mathrm{kg}^{-4}\,\mathrm{H}^{-1}$" + ) + @test latexify(p; permode=:slash) == LaTeXString( + raw"$5\;\mathrm{m}^{3}\,\mathrm{s}^{2}\,/\,\mathrm{kg}^{4}\,\mathrm{H}$" + ) + @test latexify(p; permode=:frac) == LaTeXString( + raw"$5\;\frac{\mathrm{m}^{3}\,\mathrm{s}^{2}}{\mathrm{kg}^{4}\,\mathrm{H}}$" + ) + @test latexify(p; unitformat=:siunitx, permode=:frac) == + latexify(p; unitformat=:siunitx) + @test latexify(u"m"; permode=:frac) == latexify(u"m") + @test latexify(u"m^-1"; permode=:frac) == LaTeXString(raw"$\frac{1}{\mathrm{m}}$") + @test_throws ErrorException latexify(p; permode=:wrogn) +end + +@testset "Labels" begin + @test latexify("x", m) == raw"$x\;\left/\;\mathrm{m}\right.$" + @test latexify("x", m; labelformat=:slash) == raw"$x\;\left/\;\mathrm{m}\right.$" + @test latexify("x", m; labelformat=:square) == raw"$x\;\left[\mathrm{m}\right]$" + @test latexify("x", m; labelformat=:round) == raw"$x\;\left(\mathrm{m}\right)$" + @test latexify("x", m; labelformat=:frac) == raw"$\frac{x}{\mathrm{m}}$" +end + +end + module DocUnits using Unitful using Unitful: 𝐋 @@ -2179,3 +2356,5 @@ end using Aqua Aqua.test_all(Unitful, ambiguities=VERSION≥v"1.1", unbound_args=false, piracy=VERSION≥v"1.8", project_toml_formatting=VERSION≥v"1.6") + +