Skip to content

Commit fbc32b0

Browse files
committed
Make compatible with Julia 1.0
1 parent d7496d2 commit fbc32b0

23 files changed

+2457
-190
lines changed

.github/workflows/CI.yml

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ on:
33
push:
44
branches:
55
- main
6+
- julia1-compat
67
tags: ['*']
78
pull_request:
89
concurrency:
@@ -18,10 +19,17 @@ jobs:
1819
fail-fast: false
1920
matrix:
2021
version:
21-
# - '1.0'
22-
# - '1.6'
23-
# - '1'
24-
- 'nightly'
22+
- '1.0'
23+
- '1.1'
24+
- '1.2'
25+
- '1.3'
26+
- '1.4'
27+
- '1.5'
28+
- '1.6'
29+
- '1.7'
30+
- '1.8'
31+
- '1.9'
32+
- '1.10'
2533
os:
2634
- ubuntu-latest
2735
- macOS-latest
@@ -53,7 +61,7 @@ jobs:
5361
- uses: actions/checkout@v4
5462
- uses: julia-actions/setup-julia@v1
5563
with:
56-
version: 'nightly'
64+
version: '1.10'
5765
- name: Install dependencies
5866
run: julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()'
5967
- name: Build and deploy

Project.toml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
name = "StyledStrings"
22
uuid = "f489334b-da3d-4c2e-b8f0-e476e12c162b"
33
authors = ["TEC <[email protected]>"]
4-
version = "1.11.0"
4+
version = "1.0.0"
5+
6+
[deps]
7+
TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
58

69
[compat]
7-
julia = "1.11"
10+
# 1.0 - 1.10, but the range version isn't supported till 1.4
11+
# In 1.11+ precompilation fails due to method overwriting.
12+
julia = "1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 1.10"
813

914
[extras]
10-
Supposition = "5a0628fe-1738-4658-9b6d-0b7605a9755b"
1115
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
1216

1317
[targets]
14-
test = ["Supposition", "Test"]
18+
test = ["Test"]

docs/make.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ makedocs(;
1414
"Example Usage" => "examples.md",
1515
"Internals" => "internals.md",
1616
],
17-
warnonly = [:cross_references],
17+
warnonly = [:cross_references, :missing_docs],
1818
)
1919

2020
deploydocs(repo="github.com/JuliaLang/StyledStrings.jl",

docs/src/examples.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ failing to actually convey what it can accomplish. With this in mind, examples
99
can be particularly useful to show how it can be used.
1010

1111
A styled string can be constructed manually, but the [`styled"..."`](@ref
12-
@styled_str) literal is almost always a nicer option. We can see what a manual construction would involve by extracting the string and annotation parts of a [`AnnotatedString`](@ref Base.AnnotatedString).
12+
@styled_str) literal is almost always a nicer option. We can see what a manual construction would involve by extracting the string and annotation parts of a [`AnnotatedString`](@ref StyledStrings.AnnotatedString).
1313

1414
```@repl examples
1515
using StyledStrings
@@ -24,7 +24,7 @@ str = styled"{yellow:hello} {blue:there}"
2424
Base.get(::IOContext, s::Symbol, d::Bool) = s === :color || d
2525
```
2626

27-
Most of the examples here will show [`AnnotatedString`](@ref Base.AnnotatedString) in a terminal/REPL context, however they can be trivially adapted to produce HTML with `show(::IO, ::MIME"text/plain", ::AnnotatedString)`, and other packages wanting do deal with styled content in other contexts will likely find integration with StyledStrings worthwhile.
27+
Most of the examples here will show `AnnotatedString` in a terminal/REPL context, however they can be trivially adapted to produce HTML with `show(::IO, ::MIME"text/plain", ::AnnotatedString)`, and other packages wanting do deal with styled content in other contexts will likely find integration with StyledStrings worthwhile.
2828

2929
## Basic face application and colouring
3030

@@ -172,28 +172,28 @@ styled"It's nice to include $color, and it composes too: {bold,inverse:$color}"
172172
```
173173

174174
Sometimes it's useful to compose a string incrementally, or interoperate with
175-
other `IO`-based code. For these use-cases, the [`AnnotatedIOBuffer`](@ref Base.AnnotatedIOBuffer) is very handy, as you can [`read`](@ref Base.read) an [`AnnotatedString`](@ref Base.AnnotatedString) from it.
175+
other `IO`-based code. For these use-cases, the [`AnnotatedIOBuffer`](@ref StyledStrings.AnnotatedIOBuffer) is very handy, as you can [`read`](@ref Base.read) an [`AnnotatedString`](@ref StyledStrings.AnnotatedString) from it.
176176

177177
```@repl examples
178-
aio = Base.AnnotatedIOBuffer()
178+
aio = StyledStrings.AnnotatedIOBuffer()
179179
typ = Int
180180
print(aio, typ)
181181
while typ != Any # We'll pretend that `supertypes` doesn't exist.
182182
typ = supertype(typ)
183183
print(aio, styled" {bright_red:<:} $typ")
184184
end
185-
read(seekstart(aio), Base.AnnotatedString)
185+
read(seekstart(aio), StyledStrings.AnnotatedString)
186186
```
187187

188188
StyledStrings adds a specialised [`printstyled`](@ref) method `printstyled(::AnnotatedIOBuffer, ...)` that means that you can pass an `AnnotatedIOBuffer` as IO to "legacy" code written to use `printstyled`, and extract all the styling as though it had used [`styled"..."`](@ref @styled_str) macros.
189189

190190
```@repl
191-
aio = Base.AnnotatedIOBuffer()
191+
aio = StyledStrings.AnnotatedIOBuffer()
192192
printstyled(aio, 'c', color=:red)
193193
printstyled(aio, 'o', color=:yellow)
194194
printstyled(aio, 'l', color=:green)
195195
printstyled(aio, 'o', color=:blue)
196196
printstyled(aio, 'r', color=:magenta)
197-
read(seekstart(aio), Base.AnnotatedString)
197+
read(seekstart(aio), StyledStrings.AnnotatedString)
198198
read(seekstart(aio), String)
199199
```

docs/src/index.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ output formats.
1919

2020
Instead of leaving this headache to be widely experienced downstream, it is
2121
tackled head-on by the introduction of a special string type
22-
([`AnnotatedString`](@ref Base.AnnotatedString)). This string type wraps any other
22+
([`AnnotatedString`](@ref StyledStrings.AnnotatedString)). This string type wraps any other
2323
[`AbstractString`](@ref) type and allows for formatting information to be applied to regions (e.g.
2424
characters 1 through to 7 are bold and red).
2525

@@ -29,15 +29,15 @@ convenience, faces in the global faces dictionary (e.g. `shadow`) can just be
2929
named instead of giving the [`Face`](@ref StyledStrings.Face) directly.
3030

3131
Along with these capabilities, we also provide a convenient way for constructing
32-
[`AnnotatedString`](@ref Base.AnnotatedString)s, detailed in [Styled String
32+
[`AnnotatedString`](@ref StyledStrings.AnnotatedString)s, detailed in [Styled String
3333
Literals](@ref stdlib-styledstring-literals).
3434

3535
```@repl demo
3636
using StyledStrings
3737
styled"{yellow:hello} {blue:there}"
3838
```
3939

40-
## Styling via [`AnnotatedString`](@ref Base.AnnotatedString)s
40+
## Styling via [`AnnotatedString`](@ref StyledStrings.AnnotatedString)s
4141

4242
## [Faces](@id stdlib-styledstrings-faces)
4343

@@ -129,7 +129,7 @@ On initialization, the `config/faces.toml` file under the first Julia depot (usu
129129
### Applying faces to a `AnnotatedString`
130130

131131
By convention, the `:face` attributes of a [`AnnotatedString`](@ref
132-
Base.AnnotatedString) hold information on the [`Face`](@ref StyledStrings.Face)s
132+
StyledStrings.AnnotatedString) hold information on the [`Face`](@ref StyledStrings.Face)s
133133
that currently apply. This can be given in multiple forms, as a single `Symbol`
134134
naming a [`Face`](@ref StyledStrings.Face)s in the global face dictionary, a
135135
[`Face`](@ref StyledStrings.Face) itself, or a vector of either.
@@ -143,7 +143,7 @@ them to the properties list afterwards, or use the convenient [Styled String
143143
literals](@ref stdlib-styledstring-literals).
144144

145145
```@repl demo
146-
str1 = Base.AnnotatedString("blue text", [(1:9, :face => :blue)])
146+
str1 = StyledStrings.AnnotatedString("blue text", [(1:9, :face => :blue)])
147147
str2 = styled"{blue:blue text}"
148148
str1 == str2
149149
sprint(print, str1, context = :color => true)
@@ -152,7 +152,7 @@ sprint(show, MIME("text/html"), str1, context = :color => true)
152152

153153
## [Styled String Literals](@id stdlib-styledstring-literals)
154154

155-
To ease construction of [`AnnotatedString`](@ref Base.AnnotatedString)s with [`Face`](@ref StyledStrings.Face)s applied,
155+
To ease construction of [`AnnotatedString`](@ref StyledStrings.AnnotatedString)s with [`Face`](@ref StyledStrings.Face)s applied,
156156
the [`styled"..."`](@ref @styled_str) styled string literal allows for the content and
157157
attributes to be easily expressed together via a custom grammar.
158158

src/StyledStrings.jl

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,20 @@
22

33
module StyledStrings
44

5-
import Base: AnnotatedString, AnnotatedChar, annotations, annotate!,
6-
annotatedstring, convert, merge, show, print, write,
7-
ScopedValue, with, @with
5+
import Base: convert, merge, show, print, write
86

97
export @styled_str
10-
public Face, addface!, withfaces, styled, SimpleColor
8+
9+
include("compat.jl")
10+
using .Compat
11+
12+
const ncodeunits = Compat.ncodeunits
13+
14+
include("strings/strings.jl")
15+
include("terminfo.jl")
16+
17+
import .AnnotatedStrings: AnnotatedString, AnnotatedChar, annotations,
18+
annotate!, annotatedstring, AnnotatedIOBuffer
1119

1220
include("faces.jl")
1321
include("regioniterator.jl")
@@ -18,12 +26,14 @@ include("legacy.jl")
1826
using .StyledMarkup
1927

2028
function __init__()
29+
term_env = get(ENV, "TERM", @static Sys.iswindows() ? "" : "dumb")
30+
global current_terminfo = load_terminfo(term_env)
2131
userfaces = joinpath(first(DEPOT_PATH), "config", "faces.toml")
2232
isfile(userfaces) && loaduserfaces!(userfaces)
2333
Legacy.load_env_colors!()
2434
end
2535

26-
if Base.generating_output()
36+
if generating_output()
2737
include("precompile.jl")
2838
end
2939

src/compat.jl

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
# This file is a part of Julia. License is MIT: https://julialang.org/license
2+
3+
module Compat
4+
5+
export isnothing, _str_sizehint, takewhile,
6+
escape_raw_string, peek, parseatom, generating_output
7+
8+
@static if VERSION < v"1.1"
9+
isnothing(x) = x === nothing
10+
end
11+
12+
@static if VERSION < v"1.2"
13+
ncodeunits(s::AbstractString) = Base.ncodeunits(s)
14+
ncodeunits(c::AbstractChar) = Base.ncodeunits(string(c))
15+
else
16+
import Base.ncodeunits
17+
end
18+
19+
@static if VERSION < v"1.3"
20+
function _str_sizehint(x)
21+
if x isa Float64
22+
return 20
23+
elseif x isa Float32
24+
return 12
25+
elseif x isa String || x isa SubString{String}
26+
return sizeof(x)
27+
elseif x isa Char
28+
return ncodeunits(x)
29+
else
30+
return 8
31+
end
32+
end
33+
else
34+
import Base._str_sizehint
35+
end
36+
37+
@static if VERSION < v"1.3"
38+
function unescape_string(io::IO, s::AbstractString, keep = ())
39+
a = Iterators.Stateful(s)
40+
for c in a
41+
if !isempty(a) && c == '\\'
42+
c = popfirst!(a)
43+
if c in keep
44+
print(io, '\\', c)
45+
elseif c == 'x' || c == 'u' || c == 'U'
46+
n = k = 0
47+
m = c == 'x' ? 2 :
48+
c == 'u' ? 4 : 8
49+
while (k += 1) <= m && !isempty(a)
50+
nc = peek(a)
51+
n = '0' <= nc <= '9' ? n<<4 + (nc-'0') :
52+
'a' <= nc <= 'f' ? n<<4 + (nc-'a'+10) :
53+
'A' <= nc <= 'F' ? n<<4 + (nc-'A'+10) : break
54+
popfirst!(a)
55+
end
56+
if k == 1 || n > 0x10ffff
57+
u = m == 4 ? 'u' : 'U'
58+
throw(ArgumentError("invalid $(m == 2 ? "hex (\\x)" :
59+
"unicode (\\$u)") escape sequence"))
60+
end
61+
if m == 2 # \x escape sequence
62+
write(io, UInt8(n))
63+
else
64+
print(io, Char(n))
65+
end
66+
elseif '0' <= c <= '7'
67+
k = 1
68+
n = c-'0'
69+
while (k += 1) <= 3 && !isempty(a)
70+
c = peek(a)
71+
n = ('0' <= c <= '7') ? n<<3 + c-'0' : break
72+
popfirst!(a)
73+
end
74+
if n > 255
75+
throw(ArgumentError("octal escape sequence out of range"))
76+
end
77+
write(io, UInt8(n))
78+
else
79+
print(io, c == 'a' ? '\a' :
80+
c == 'b' ? '\b' :
81+
c == 't' ? '\t' :
82+
c == 'n' ? '\n' :
83+
c == 'v' ? '\v' :
84+
c == 'f' ? '\f' :
85+
c == 'r' ? '\r' :
86+
c == 'e' ? '\e' :
87+
(c == '\\' || c == '"') ? c :
88+
throw(ArgumentError("invalid escape sequence \\$c")))
89+
end
90+
else
91+
print(io, c)
92+
end
93+
end
94+
end
95+
unescape_string(s::AbstractString, keep = ()) =
96+
sprint(unescape_string, s, keep; sizehint=lastindex(s))
97+
end
98+
99+
@static if VERSION < v"1.4"
100+
function takewhile(f::Function, itr)
101+
taken = Vector{eltype(itr)}()
102+
next = iterate(itr)
103+
while !isnothing(next) && ((item, state) = next) |> first |> f
104+
push!(taken, item)
105+
next = iterate(itr, state)
106+
end
107+
taken
108+
end
109+
function escape_raw_string(io::IO, str::AbstractString, delim::Char='"')
110+
total = 0
111+
escapes = 0
112+
for c in str
113+
if c == '\\'
114+
escapes += 1
115+
else
116+
if c == delim
117+
# if one or more backslashes are followed by
118+
# a double quote then escape all backslashes
119+
# and the double quote
120+
escapes += 1
121+
total += escapes
122+
while escapes > 0
123+
write(io, '\\')
124+
escapes -= 1
125+
end
126+
end
127+
escapes = 0
128+
end
129+
write(io, c)
130+
end
131+
# also escape any trailing backslashes,
132+
# so they do not affect the closing quote
133+
total += escapes
134+
while escapes > 0
135+
write(io, '\\')
136+
escapes -= 1
137+
end
138+
total
139+
end
140+
function escape_raw_string(str::AbstractString, delim::Char='"')
141+
total = escape_raw_string(devnull, str, delim) # check whether the string even needs to be copied and how much to allocate for it
142+
return total == 0 ? str : sprint(escape_raw_string, str, delim; sizehint = sizeof(str) + total)
143+
end
144+
else
145+
const takewhile = Iterators.takewhile
146+
const escape_raw_string = Base.escape_raw_string
147+
end
148+
149+
@static if VERSION < v"1.5"
150+
function peek(s::Iterators.Stateful, sentinel=nothing)
151+
ns = s.nextvalstate
152+
return ns !== nothing ? ns[1] : sentinel
153+
end
154+
end
155+
156+
@static if VERSION < v"1.6"
157+
function parseatom(text::AbstractString, pos::Integer)
158+
Meta.parse(text, pos, greedy = false)
159+
end
160+
else
161+
const parseatom = Meta.parseatom
162+
end
163+
164+
@static if VERSION < v"1.11"
165+
function generating_output(incremental::Union{Bool,Nothing}=nothing)
166+
ccall(:jl_generating_output, Cint, ()) == 0 && return false
167+
if incremental !== nothing
168+
Base.JLOptions().incremental == incremental || return false
169+
end
170+
return true
171+
end
172+
else
173+
const generating_output = Base.generating_output
174+
end
175+
176+
end

0 commit comments

Comments
 (0)