From 1c28c39673ddfeb7e22c665fb1b30247456b75ea Mon Sep 17 00:00:00 2001 From: Roman S Samarev Date: Wed, 7 Oct 2026 12:45:18 +0300 Subject: [PATCH] Embed entity table at precompile time; require Julia 1.10; modernize CI The entity table was loaded from data/html.dat in __init__ into a non-const, untyped global. Every lookup through `HTML_Entities.default` was type-unstable, the file had to be read on every load, and the package could not be compiled with `juliac --trim`. This supersedes the approach on the fix/artifacts branch (an artifact for html.dat), which had no download source and so failed to load unless deps/build.jl had copied the file into ~/.julia/artifacts. - Load data/html.dat at precompile time into a const, concretely typed table stored in the package image. No runtime file I/O; works with `juliac --trim=safe`. - Parameterize HTML_Table on the offset type so the `nam` field is concrete. - Drop the RelocatableFolders dependency and the obsolete __precompile__() call; make VER const. - Require Julia 1.10; set StrTables compat to 1.0.1; add Test compat. - CI: test 1.10, 1 and pre on Linux, macOS and Windows with current action versions; upload coverage to Codecov; remove .drone.yml. - Add Dependabot for GitHub Actions and Julia compat updates. - Rewrite README with usage examples and current badges. Co-Authored-By: Claude Opus 5.5 --- .drone.yml | 42 -------------------- .github/dependabot.yml | 11 ++++++ .github/workflows/ci.yml | 55 +++++++++++++------------- .gitignore | 2 + Project.toml | 9 ++--- README.md | 83 +++++++++++++++++++++++++++------------- deps/build.jl | 2 +- src/HTML_Entities.jl | 20 +++++----- test/runtests.jl | 10 ++++- 9 files changed, 122 insertions(+), 112 deletions(-) delete mode 100644 .drone.yml create mode 100644 .github/dependabot.yml diff --git a/.drone.yml b/.drone.yml deleted file mode 100644 index 2094e3a..0000000 --- a/.drone.yml +++ /dev/null @@ -1,42 +0,0 @@ ---- -kind: pipeline -name: linux - arm64 - Julia 1.5 - -platform: - os: linux - arch: arm64 - -steps: -- name: build - image: julia:1.5 - commands: - - "julia --project=. --check-bounds=yes --color=yes -e 'using InteractiveUtils; versioninfo(verbose=true); using Pkg; Pkg.build(); Pkg.test(coverage=true)'" - ---- -kind: pipeline -name: linux - arm - Julia 1.6 - -platform: - os: linux - arch: arm - -steps: -- name: build - image: julia:1.0 - commands: - - "julia --project=. --check-bounds=yes --color=yes -e 'using InteractiveUtils; versioninfo(verbose=true); using Pkg; Pkg.build(); Pkg.test(coverage=true)'" - ---- -kind: pipeline -name: linux - arm64 - Julia 1.6 - -platform: - os: linux - arch: arm64 - -steps: -- name: build - image: julia:1.0 - commands: - - "julia --project=. --check-bounds=yes --color=yes -e 'using InteractiveUtils; versioninfo(verbose=true); using Pkg; Pkg.build(); Pkg.test(coverage=true)'" - diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..5980858 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: "julia" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 36c5c31..6d80502 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,43 +1,46 @@ name: CI on: - - push - - pull_request + push: + branches: + - master + tags: ['*'] + pull_request: + workflow_dispatch: +concurrency: + # Skip intermediate builds: always. + # Cancel intermediate builds: only if it is a pull request build. + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} jobs: test: - name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} + name: Julia ${{ matrix.version }} - ${{ matrix.os }} runs-on: ${{ matrix.os }} + timeout-minutes: 60 + permissions: # needed to allow julia-actions/cache to proactively delete old caches that it has created + actions: write + contents: read strategy: fail-fast: false matrix: version: - - '1.5' - - '1.6' - - 'nightly' + - '1.10' # LTS, minimum supported version + - '1' # latest stable release + - 'pre' # latest prerelease, if any os: - ubuntu-latest - - macOS-latest + - macos-latest - windows-latest - arch: - - x64 - - x86 - exclude: - - os: macOS-latest - arch: x86 steps: - - uses: actions/checkout@v2 - - uses: julia-actions/setup-julia@v1 + - uses: actions/checkout@v7 + - uses: julia-actions/setup-julia@v3 with: version: ${{ matrix.version }} - arch: ${{ matrix.arch }} - - uses: actions/cache@v1 - env: - cache-name: cache-artifacts - with: - path: ~/.julia/artifacts - key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }} - restore-keys: | - ${{ runner.os }}-test-${{ env.cache-name }}- - ${{ runner.os }}-test- - ${{ runner.os }}- + - uses: julia-actions/cache@v3 - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-runtest@v1 + - uses: julia-actions/julia-processcoverage@v1 + - uses: codecov/codecov-action@v7 + with: + files: lcov.info + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: false diff --git a/.gitignore b/.gitignore index 8c960ec..497f131 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ *.jl.cov *.jl.*.cov *.jl.mem +Manifest.toml +deps/build.log diff --git a/Project.toml b/Project.toml index 49d72cd..befabd3 100644 --- a/Project.toml +++ b/Project.toml @@ -4,16 +4,15 @@ keywords = ["Entities", "HTML"] license = "MIT" desc = "Entities from HTML data tables" authors = ["ScottPJones "] -version = "1.0.2" +version = "1.1.0" [deps] -RelocatableFolders = "05181044-ff0b-4ac5-8273-598c1e38db00" StrTables = "9700d1a9-a7c8-5760-9816-a99fda30bb8f" [compat] -RelocatableFolders = "1" -StrTables = "1" -julia = "1" +StrTables = "1.0.1" +Test = "1" +julia = "1.10" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/README.md b/README.md index 429bc99..501951e 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,70 @@ -# HTML_Entities: Support for using HTML entity names for characters +# HTML_Entities.jl -[pkg-url]: https://github.com/JuliaString/HTML_Entities.jl.git +[![CI](https://github.com/JuliaString/HTML_Entities.jl/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/JuliaString/HTML_Entities.jl/actions/workflows/ci.yml?query=branch%3Amaster) +[![Coverage](https://codecov.io/gh/JuliaString/HTML_Entities.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/JuliaString/HTML_Entities.jl) +[![Release](https://img.shields.io/github/release/JuliaString/HTML_Entities.jl.svg)](https://github.com/JuliaString/HTML_Entities.jl/releases) +[![License: MIT](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE.md) -[julia-url]: https://github.com/JuliaLang/Julia -[julia-release]:https://img.shields.io/github/release/JuliaLang/julia.svg +Support for using HTML entity names for characters. -[release]: https://img.shields.io/github/release/JuliaString/HTML_Entities.jl.svg -[release-date]: https://img.shields.io/github/release-date/JuliaString/HTML_Entities.jl.svg +This package provides a table of the 2125 named character references defined by HTML, with +fast lookups in both directions: -[license-img]: http://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat -[license-url]: LICENSE.md +* from an entity name to the Unicode character(s) it represents, +* from a character, or a pair of characters, to all of the entity names for it, +* from a prefix to all of the entity names that start with it (for completions). -[gitter-img]: https://badges.gitter.im/Join%20Chat.svg -[gitter-url]: https://gitter.im/JuliaString/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge +## Installation -[travis-url]: https://travis-ci.org/JuliaString/HTML_Entities.jl -[travis-s-img]: https://travis-ci.org/JuliaString/HTML_Entities.jl.svg -[travis-m-img]: https://travis-ci.org/JuliaString/HTML_Entities.jl.svg?branch=master +Requires Julia 1.10 or later. -[codecov-url]: https://codecov.io/gh/JuliaString/HTML_Entities.jl -[codecov-img]: https://codecov.io/gh/JuliaString/HTML_Entities.jl/branch/master/graph/badge.svg +```julia +using Pkg +Pkg.add("HTML_Entities") +``` -[contrib]: https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat +## Usage -[![][release]][pkg-url] [![][release-date]][pkg-url] [![][license-img]][license-url] [![contributions welcome][contrib]](https://github.com/JuliaString/HTML_Entities.jl/issues) +Nothing is exported. The lookup functions come from +[StrTables.jl](https://github.com/JuliaString/StrTables.jl) and take the table +`HTML_Entities.default` as their first argument. -| **Julia Version** | **Unit Tests** | **Coverage** | -|:------------------:|:------------------:|:---------------------:| -| [![][julia-release]][julia-url] | [![][travis-s-img]][travis-url] | [![][codecov-img]][codecov-url] -| Julia Latest | [![][travis-m-img]][travis-url] | [![][codecov-img]][codecov-url] +```julia +using HTML_Entities, StrTables -HTML_Entities.jl: -==================================================================== +const tab = HTML_Entities.default -This builds tables for looking up HTML entity names and returning the Unicode character(s), -looking up a character or pair of characters and finding HTML names that return it/them, -and finding all of the HTML name completions for a particular string, if any. +lookupname(tab, "nle") # "≰" +lookupname(tab, "lvnE") # "≨︀" (two characters: U+2268 U+FE00) +lookupname(tab, "foobar") # "" (unknown name) +matchchar(tab, 'α') # ["alpha"] +matches(tab, "≨︀") # ["lvertneqq", "lvnE"] +longestmatches(tab, "≰ abcd") # ["NotLessEqual", "nle", "nleq"] +completions(tab, "um") # ["umacr", "uml"] +``` + +| Function | Description | +|:---------|:------------| +| `lookupname(tab, name)` | The string for an entity name, or `""` if not found | +| `matchchar(tab, ch)` | All entity names for a single character | +| `matches(tab, str)` | All entity names that exactly match a string of one or two characters | +| `longestmatches(tab, str)` | All entity names for the longest match at the start of `str` | +| `completions(tab, prefix)` | All entity names that start with `prefix` | + +`HTML_Entities.default` is also an `AbstractVector{String}` of all entity names, in sorted order. + +## Static compilation + +The table is loaded from `data/html.dat` at precompile time and stored in the package image. +Nothing is read from disk at runtime, and the table has a concrete type, so the package can be +used in executables built with [JuliaC.jl](https://github.com/JuliaLang/JuliaC.jl) and `--trim=safe`. + +## Regenerating the data + +The entity definitions are in [`deps/htmlnames.jl`](deps/htmlnames.jl). To rebuild the table, +delete `data/html.dat` and run: + +```sh +julia --project=. -e 'using Pkg; Pkg.instantiate(); include("deps/build.jl")' +``` diff --git a/deps/build.jl b/deps/build.jl index 8fe0bbd..14132be 100644 --- a/deps/build.jl +++ b/deps/build.jl @@ -6,7 +6,7 @@ println("Running HTML entity build in ", pwd()) using StrTables -VER = UInt32(1) +const VER = UInt32(1) const inpname = "htmlnames.jl" diff --git a/src/HTML_Entities.jl b/src/HTML_Entities.jl index 7da96dd..9aeebb0 100644 --- a/src/HTML_Entities.jl +++ b/src/HTML_Entities.jl @@ -1,4 +1,3 @@ -__precompile__() """ # Public API (nothing is exported) @@ -8,17 +7,17 @@ __precompile__() * completions(str) """ module HTML_Entities -using StrTables, RelocatableFolders +using StrTables -VER = UInt32(1) +const VER = UInt32(1) -struct HTML_Table{T} <: AbstractEntityTable +struct HTML_Table{T,O} <: AbstractEntityTable ver::UInt32 tim::String inf::String base32::UInt32 base2c::UInt32 - nam::StrTable{T} + nam::PackedTable{T,UInt8,O} ind::Vector{UInt16} val16::Vector{UInt16} ind16::Vector{UInt16} @@ -28,10 +27,11 @@ struct HTML_Table{T} <: AbstractEntityTable ind2c::Vector{UInt16} end -const DATA_PATH = @path joinpath(@__DIR__, "../data", "html.dat") +const DATA_PATH = joinpath(@__DIR__, "..", "data", "html.dat") +include_dependency(DATA_PATH) + +# The table is loaded at precompile time and stored in the package image, +# so no file I/O is needed at runtime (and it works with `juliac --trim`) +const default = HTML_Table(StrTables.load(DATA_PATH)...) -function __init__() - global default = HTML_Table(StrTables.load(DATA_PATH)...) - nothing -end end # module HTML_Entities diff --git a/test/runtests.jl b/test/runtests.jl index 0a33280..68f9668 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,6 +1,5 @@ using StrTables, HTML_Entities - -@static VERSION < v"0.7.0-DEV" ? (using Base.Test) : (using Test) +using Test # Test the functions lookupname, matches, longestmatches, completions # Check that characters from all 3 tables (BMP, non-BMP, string) are tested @@ -8,6 +7,13 @@ using StrTables, HTML_Entities const def = HTML_Entities.default @testset "HTML_Entities" begin +@testset "default table" begin + # Needed for type-stable access and for `juliac --trim` + @test isconst(HTML_Entities, :default) + @test isconcretetype(typeof(def)) + @test length(def) > 2000 +end + @testset "lookupname" begin @test lookupname(def, SubString("My name is Spock", 12)) == "" @test lookupname(def, "foobar") == ""