Skip to content

Commit

Permalink
Make compatible with Julia 1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
tecosaur committed Oct 28, 2023
1 parent 9b35f08 commit 6670401
Show file tree
Hide file tree
Showing 16 changed files with 1,698 additions and 89 deletions.
15 changes: 12 additions & 3 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ on:
push:
branches:
- main
- julia1-compat
tags: ['*']
pull_request:
concurrency:
Expand All @@ -18,9 +19,17 @@ jobs:
fail-fast: false
matrix:
version:
# - '1.0'
# - '1.6'
# - '1'
- '1.0'
- '1.1'
- '1.2'
- '1.3'
- '1.4'
- '1.5'
- '1.6'
- '1.7'
- '1.8'
- '1.9'
- '1.10'
- 'nightly'
os:
- ubuntu-latest
Expand Down
9 changes: 7 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
name = "StyledStrings"
uuid = "f489334b-da3d-4c2e-b8f0-e476e12c162b"
authors = ["TEC <[email protected]>"]
version = "1.11.0"
version = "1.0.0"

[deps]
Revise = "295af30f-e4ad-537b-8983-00126c2a3abe"
TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76"

[compat]
julia = "1.11"
TOML = "1"
julia = "1"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Expand Down
13 changes: 9 additions & 4 deletions src/StyledStrings.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@

module StyledStrings

import Base: AnnotatedString, AnnotatedChar, annotations, annotate!,
annotatedstring, convert, merge, show, print, write
import Base: convert, merge, show, print, write

export @styled_str
public Face, addface!, SimpleColor

include("compat.jl")
include("terminfo.jl")
include("strings/strings.jl")

import .AnnotatedStrings: AnnotatedString, AnnotatedChar, annotations, annotate!,
annotatedstring, annotatedstring_optimize!

include("faces.jl")
include("regioniterator.jl")
Expand All @@ -20,7 +25,7 @@ function __init__()
Legacy.load_env_colors!()
end

if Base.generating_output()
if generating_output()
include("precompile.jl")
end

Expand Down
133 changes: 133 additions & 0 deletions src/compat.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license

@static if VERSION < v"1.1"
isnothing(x) = x === nothing
end

@static if VERSION < v"1.2"
ncodeunits(s::AbstractString) = Base.ncodeunits(s)
ncodeunits(c::AbstractChar) = ncodeunits(string(c))
else
const ncodeunits = Base.ncodeunits
end

@static if VERSION < v"1.3"
function _str_sizehint(x)
if x isa Float64
return 20
elseif x isa Float32
return 12
elseif x isa String || x isa SubString{String}
return sizeof(x)
elseif x isa Char
return ncodeunits(x)
else
return 8
end
end
else
import Base._str_sizehint
end

@static if VERSION < v"1.3"
function unescape_string(io::IO, s::AbstractString, keep = ())
a = Iterators.Stateful(s)
for c in a
if !isempty(a) && c == '\\'
c = popfirst!(a)
if c in keep
print(io, '\\', c)
elseif c == 'x' || c == 'u' || c == 'U'
n = k = 0
m = c == 'x' ? 2 :
c == 'u' ? 4 : 8
while (k += 1) <= m && !isempty(a)
nc = peek(a)
n = '0' <= nc <= '9' ? n<<4 + (nc-'0') :
'a' <= nc <= 'f' ? n<<4 + (nc-'a'+10) :
'A' <= nc <= 'F' ? n<<4 + (nc-'A'+10) : break
popfirst!(a)
end
if k == 1 || n > 0x10ffff
u = m == 4 ? 'u' : 'U'
throw(ArgumentError("invalid $(m == 2 ? "hex (\\x)" :
"unicode (\\$u)") escape sequence"))
end
if m == 2 # \x escape sequence
write(io, UInt8(n))
else
print(io, Char(n))
end
elseif '0' <= c <= '7'
k = 1
n = c-'0'
while (k += 1) <= 3 && !isempty(a)
c = peek(a)
n = ('0' <= c <= '7') ? n<<3 + c-'0' : break
popfirst!(a)
end
if n > 255
throw(ArgumentError("octal escape sequence out of range"))
end
write(io, UInt8(n))
else
print(io, c == 'a' ? '\a' :
c == 'b' ? '\b' :
c == 't' ? '\t' :
c == 'n' ? '\n' :
c == 'v' ? '\v' :
c == 'f' ? '\f' :
c == 'r' ? '\r' :
c == 'e' ? '\e' :
(c == '\\' || c == '"') ? c :
throw(ArgumentError("invalid escape sequence \\$c")))
end
else
print(io, c)
end
end
end
unescape_string(s::AbstractString, keep = ()) =
sprint(unescape_string, s, keep; sizehint=lastindex(s))
end

@static if VERSION < v"1.4"
function takewhile(f::Function, itr)
taken = Vector{eltype(itr)}()
next = iterate(itr)
while !isnothing(next) && ((item, state) = next) |> first |> f
push!(taken, item)
next = iterate(itr, state)
end
taken
end
else
const takewhile = Iterators.takewhile
end

@static if VERSION < v"1.5"
function peek(s::Iterators.Stateful, sentinel=nothing)
ns = s.nextvalstate
return ns !== nothing ? ns[1] : sentinel
end
end

@static if VERSION < v"1.6"
function parseatom(text::AbstractString, pos::Integer)
Meta.parse(text, pos, greedy = false)
end
else
const parseatom = Meta.parseatom
end

@static if VERSION < v"1.11"
function generating_output(incremental::Union{Bool,Nothing}=nothing)
ccall(:jl_generating_output, Cint, ()) == 0 && return false
if incremental !== nothing
Base.JLOptions().incremental == incremental || return false
end
return true
end
else
const generating_output = Base.generating_output
end
85 changes: 57 additions & 28 deletions src/faces.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license

using TOML

const RGBTuple = NamedTuple{(:r, :g, :b), NTuple{3, UInt8}}

"""
Expand Down Expand Up @@ -27,7 +29,7 @@ end
SimpleColor(r::Integer, g::Integer, b::Integer) = SimpleColor((; r=UInt8(r), g=UInt8(g), b=UInt8(b)))
SimpleColor(rgb::UInt32) = SimpleColor(reverse(reinterpret(UInt8, [rgb]))[2:end]...)

convert(::Type{SimpleColor}, (; r, g, b)::RGBTuple) = SimpleColor((; r, g, b))
convert(::Type{SimpleColor}, rgb::RGBTuple) = SimpleColor(rgb)
convert(::Type{SimpleColor}, namedcolor::Symbol) = SimpleColor(namedcolor)
convert(::Type{SimpleColor}, rgb::UInt32) = SimpleColor(rgb)

Expand Down Expand Up @@ -331,7 +333,7 @@ const FACES = let default = Dict{Symbol, Face}(
:repl_prompt_pkg => Face(inherit=[:blue, :repl_prompt]),
:repl_prompt_beep => Face(inherit=[:shadow, :repl_prompt]),
)
(; default, current=ScopedValue(copy(default)), lock=ReentrantLock())
(; default=default, current=Ref(copy(default)), lock=ReentrantLock())
end

## Adding and resetting faces ##
Expand All @@ -355,12 +357,14 @@ Face (sample)
```
"""
function addface!((name, default)::Pair{Symbol, Face})
@lock FACES.lock if !haskey(FACES.default, name)
FACES.default[name] = default
FACES.current[][name] = if haskey(FACES.current[], name)
merge(deepcopy(default), FACES.current[][name])
else
deepcopy(default)
lock(FACES.lock) do
if !haskey(FACES.default, name)
FACES.default[name] = default
FACES.current[][name] = if haskey(FACES.current[], name)
merge(deepcopy(default), FACES.current[][name])
else
deepcopy(default)
end
end
end
end
Expand All @@ -371,7 +375,7 @@ end
Reset the current global face dictionary to the default value.
"""
function resetfaces!()
@lock FACES.lock begin
lock(FACES.lock) do
current = FACES.current[]
empty!(current)
for (key, val) in FACES.default
Expand All @@ -391,13 +395,15 @@ In the unlikely event that the face `name` does not have a default value,
it is deleted, a warning message is printed, and `nothing` returned.
"""
function resetfaces!(name::Symbol)
@lock FACES.lock if !haskey(FACES.current[], name)
elseif haskey(FACES.default, name)
FACES.current[][name] = deepcopy(FACES.default[name])
else # This shouldn't happen
delete!(FACES.current[], name)
@warn """The face $name was reset, but it had no default value, and so has been deleted instead!,
This should not have happened, perhaps the face was added without using `addface!`?"""
lock(FACES.lock) do
if !haskey(FACES.current[], name)
elseif haskey(FACES.default, name)
FACES.current[][name] = deepcopy(FACES.default[name])
else # This shouldn't happen
delete!(FACES.current[], name)
@warn """The face $name was reset, but it had no default value, and so has been deleted instead!,
This should not have happened, perhaps the face was added without using `addface!`?"""
end
end
end

Expand All @@ -410,6 +416,9 @@ Execute `f` with `FACES``.current` temporarily modified by zero or more
temporarily unset an face (if if has been set). When `withfaces` returns, the
original `FACES``.current` has been restored.
!!! warning
Changing faces is not thread-safe.
# Examples
```jldoctest; setup = :(import StyledStrings: Face, withfaces)
Expand All @@ -420,27 +429,45 @@ red and blue mixed make purple
```
"""
function withfaces(f, keyvals::Pair{Symbol, <:Union{Face, Symbol, Nothing}}...)
newfaces = copy(FACES.current[])
old = Dict{Symbol, Union{Face, Nothing}}()
for (name, face) in keyvals
old[name] = get(FACES.current[], name, nothing)
if face isa Face
newfaces[name] = face
FACES.current[][name] = face
elseif face isa Symbol
newfaces[name] =get(FACES.current[], face, Face())
elseif haskey(newfaces, name)
delete!(newfaces, name)
FACES.current[][name] =
something(get(old, face, nothing), get(FACES.current[], face, Face()))
elseif haskey(FACES.current[], name)
delete!(FACES.current[], name)
end
end
try f()
finally
for (name, face) in old
if isnothing(face)
delete!(FACES.current[], name)
else
FACES.current[][name] = face
end
end
end
@with(FACES.current => newfaces, f())
end

"""
withfaces(f, altfaces::Dict{Symbol, Face})
Execute `f` with `FACES``.current` temporarily swapped out with `altfaces`
When `withfaces` returns, the original `FACES``.current` has been restored.
!!! warning
Changing faces is not thread-safe.
"""
function withfaces(f, altfaces::Dict{Symbol, Face})
@with(FACES.current => altfaces, f())
oldfaces, FACES.current[] = FACES.current[], altfaces
try f()
finally
FACES.current[] = oldfaces
end
end

withfaces(f) = f()
Expand Down Expand Up @@ -570,10 +597,12 @@ Face (sample)
```
"""
function loadface!((name, update)::Pair{Symbol, Face})
@lock FACES.lock if haskey(FACES.current[], name)
FACES.current[][name] = merge(FACES.current[][name], update)
else
FACES.current[][name] = update
lock(FACES.lock) do
if haskey(FACES.current[], name)
FACES.current[][name] = merge(FACES.current[][name], update)
else
FACES.current[][name] = update
end
end
end

Expand Down Expand Up @@ -609,7 +638,7 @@ end
Load all faces declared in the Faces.toml file `tomlfile`.
"""
loaduserfaces!(tomlfile::String) = loaduserfaces!(Base.parsed_toml(tomlfile))
loaduserfaces!(tomlfile::String) = loaduserfaces!(open(TOML.parse, tomlfile))

function convert(::Type{Face}, spec::Dict)
Face(if haskey(spec, "font") && spec["font"] isa String
Expand Down
Loading

0 comments on commit 6670401

Please sign in to comment.