Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package ex_doc for openSUSE:Factory checked in at 2026-09-08 16:59:00 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/ex_doc (Old) and /work/SRC/openSUSE:Factory/.ex_doc.new.1265 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "ex_doc" Tue Sep 8 16:59:00 2026 rev:2 rq:1376369 version:0.40.3 Changes: -------- --- /work/SRC/openSUSE:Factory/ex_doc/ex_doc.changes 2025-06-02 22:02:00.428820797 +0200 +++ /work/SRC/openSUSE:Factory/.ex_doc.new.1265/ex_doc.changes 2026-09-08 17:02:11.783199449 +0200 @@ -1,0 +2,134 @@ +Wed Aug 05 23:15:57 UTC 2026 - Simon Lees <[email protected]> + +- Update to version 0.40.3: + * Fix record autolink - and bump CI (#2240) + * Update makeup_erlang version (#2236) + * Raise on extras that conflict with reserved filenames + * Prevent #search selector from impacting user content + * Fix inline code font size in headings (#2220) + * Raise on duplicate entries in files in :extras (#2226) + * Fix false positive warning when linking to asset files (#2225) + * Fix markdown backend code fence language and opaque type display (#2215) + * Skip HTML comments when computing synopsis (#2214) + * Bump versions in release CI for prebuilt archives (#2213) + * Enable use of admonition blocks in cheatsheets (#2210) + * Bump minimatch from 3.1.2 to 3.1.5 in /assets (#2205) + * Make minor doc language fixes in source_url/rel section + * Improve docstring headings, including admonition blocks (#2202) + * Tweak ExDoc module doc text (#2203) + * The code for :meta seems to have dissapeared, so remove it from docs (#2200) + * epub: fix links to extra documents (#2199) + * epub: void elements, such `wbr`, must be terminated by the matching end-tag (#2198) + * markdown: conditionally copy assets to target dir (#2195) + * html: improve consistency across private helpers (#2196) + * Replace Karma with Vitest for JavaScript testing (#2197) + * use File.write! for everything (as in the html formatter) + * group resources just one time + * based on llmstxt.org the description should be a blockquote + * Add rel="nofollow" to external links in HTML output (#2193) + * Turn whitespace minification back on + * Improve distinction between docstring headings (H2-H4) + * Change headings' levels to match their context + * Fix Summary Types heading size + * Improve word-breaking of module names and sizing of main page titles (#2190) + * Include a description in llms.txt + * Do not include source links in markdown files + * Generate link to view markdown source + * Generate llms.txt instead of index.md + * Start marking formatter API public + * Reduce the places we pass markdown globally and extend Erlang coverage + * Make ExDoc module public + * Unify formatter return type + * Generate markdown before epub + * Have config as first argument in formatter functions + * Pass autolink structs around instead of options + * Make tests with global state sync + * Make source_beam a required argument + * Run mix format + * Move GroupMatcher to Config + * Simplify before_closing_*_tag callback handling + * Run retriever just once per formatter + * Rename markdown build file + * Rename to_string to to_html in DocAST + * Normalize formatter dispatch + * Split formatter configuration into its own module + * Remove unused fields from config + * Start unifying autolink rendering + * Simplify formatter API + * Remove filter_list and nodes_map + * Unify markdown and epub nav + * Clean up tests to rely less on integration + * Extract extras into their module for easier testing + * Simplify outputs and formatter + * Also default to markdown in the CLI + * Assert on types/specs rendered in markdown + * Keep specs and source_specs as distinct fields + * Simplify markdown template rendering + * Only discard unselectable for bash prompts, closes #2180 + * Update esbuild and turn off whitespace minification (#2186) + * Support anchors on redirects (#2185) + * Prevent recent heading size changes from applying to docstring headings (#2184) + * Only include .html links in Swup + * Add Markdown formatter (#2148) + * Add the option to trim down the footer + * Ensure IDs rather than hrefs are stripped, closes #2175 + * Show go to latest for prereleases, closes #2173 + * Fix docs: Move `source_url` to `project` (#2172) + * Prevent fake italic in autocomplete text (#2168) + * Rename Search Hexdocs link to Go to package docs + * Better differentiate headings/sections (#2164) + * Document double-colon notation when autolink to Erlang entities (#2161) +- Patches for reproducable builds in erlang + * fix-upstream-reprod-epub.patch + * fix-upstream-reprod-html.patch + * fix-upstream-reprod-utils.patch +- Add ex_doc-rpmlintrc to suppress the noarch warning + +------------------------------------------------------------------- +Wed Nov 05 12:02:38 UTC 2025 - Matwey Kornilov <[email protected]> + +- Update to version 0.39.1: + * Release v0.39.1 + * Italics for autocompletion header to not confuse with results + * Fix admonition on small screens + * Improve box shadow, minimize search button space + * Release v0.39.0 + * Search style + * Tags are a list of atoms, closes #2159 + * Update GitHub organization for Ecto in README (#2158) + * Support specifying packages under search + * Validate extra fields + * Allow multiple search engines (#2153) + * Streamline autocompletion + * Remove preview feature + * Remove tabindex from tabpanels + * Add inline code to admonition block test page headings (#2152) + * Update assets + * Update admonitions; simplify code bg/bdr colors + * Include admonition type in test block headings + * refactor: extract minimal shared logic between HTML and EPUB formatters (#2147) + * Add .cheatmd to epub to avoid broken links + * Clean up epub tests + * Safe guard exdoc:autocomplete-limit meta access (#2142) + * Release v0.38.4 + * Fix position os stale icon + * Escape attributes + * Update assets + * Increase spacing in bottom actions + * Release v0.38.3 + * Bump tmp from 0.2.1 to 0.2.4 in /assets (#2139) + * Load discovered makeup apps for cli (#2138) + * Fix typos (#2137) + * Improve docs for :skip_undefined_reference_warnings_on (#2136) + * Ensure default groups for docs are sorted alphabetically (#2132) + * Display description text in docs groups (#2113) + * Set minimum elixir on the `README.md` to 1.15 (#2130) + * Update assets + * Allow configuring autocomplete limit, and default it to 10 instead of 8 (#2129) + * Release v0.38.2 + * Do not add fancy anchors around verbatim headers, closes #2128 + * Update assets + * Move external link margin to the left (#2126) + * Fix typo in CHANGELOG.md (#2125) + +------------------------------------------------------------------- Old: ---- ex_doc-0.38.1.tar.xz New: ---- build.specials.obscpio ex_doc-0.40.3.tar.xz ex_doc-rpmlintrc fix-upstream-reprod-epub.patch fix-upstream-reprod-html.patch fix-upstream-reprod-utils.patch ----------(New B)---------- New:- Patches for reproducable builds in erlang * fix-upstream-reprod-epub.patch * fix-upstream-reprod-html.patch New: * fix-upstream-reprod-epub.patch * fix-upstream-reprod-html.patch * fix-upstream-reprod-utils.patch New: * fix-upstream-reprod-html.patch * fix-upstream-reprod-utils.patch - Add ex_doc-rpmlintrc to suppress the noarch warning ----------(New E)---------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ ex_doc.spec ++++++ --- /var/tmp/diff_new_pack.0y0XYi/_old 2026-09-08 17:02:12.701237822 +0200 +++ /var/tmp/diff_new_pack.0y0XYi/_new 2026-09-08 17:02:12.703237905 +0200 @@ -17,7 +17,7 @@ Name: ex_doc -Version: 0.38.1 +Version: 0.40.3 Release: 0 Summary: ExDoc produces HTML and online documentation for Elixir projects License: Apache-2.0 AND MIT @@ -25,8 +25,15 @@ URL: https://github.com/elixir-lang/ex_doc Source0: %{name}-%{version}.tar.xz Source1: vendor.tar.gz +Source2: ex_doc-rpmlintrc +# Upstream fixes for reproducable builds - [email protected] +# https://github.com/erlang/otp/issues/4417 +Patch0: fix-upstream-reprod-epub.patch +Patch1: fix-upstream-reprod-html.patch +Patch2: fix-upstream-reprod-utils.patch BuildRequires: elixir >= 1.15 BuildRequires: elixir-hex +BuildRequires: strip-nondeterminism >= 1.15 Obsoletes: elixir-ex_doc < %{version} Provides: elixir-ex_doc = %{version} BuildRoot: %{_tmppath}/%{name}-%{version}-build @@ -39,7 +46,7 @@ are looking for documentation for Elixir itself, check out Elixir's website. %prep -%autosetup -a1 -v +%autosetup -a1 -v -p1 %build export LANG=en_US.UTF-8 @@ -50,6 +57,7 @@ %install sed -i -e '1s|/usr/bin/env escript|/usr/bin/escript|' ex_doc install -D -m 0755 ex_doc %{buildroot}%{_bindir}/ex_doc +strip-nondeterminism --timestamp=${SOURCE_DATE_EPOCH:-1494270000} --clamp-timestamp %{buildroot}%{_bindir}/ex_doc %files %defattr(-,root,root) ++++++ _scmsync.obsinfo ++++++ --- /var/tmp/diff_new_pack.0y0XYi/_old 2026-09-08 17:02:12.754240037 +0200 +++ /var/tmp/diff_new_pack.0y0XYi/_new 2026-09-08 17:02:12.759240246 +0200 @@ -1,6 +1,6 @@ -mtime: 1748878486 -commit: f476efe93376c5693568f23856e90a7d136b92ddeb660a8ffa50e272161b132e -url: https://src.opensuse.org/erlang/ex_doc.git -revision: f476efe93376c5693568f23856e90a7d136b92ddeb660a8ffa50e272161b132e +mtime: 1785974149 +commit: f70cc7fc184cc8a925357e5f35cb996abcd5f31968d270663113e40c1aa72af0 +url: https://src.opensuse.org/erlang/ex_doc +revision: f70cc7fc184cc8a925357e5f35cb996abcd5f31968d270663113e40c1aa72af0 projectscmsync: https://src.opensuse.org/erlang/_ObsPrj.git ++++++ _service ++++++ --- /var/tmp/diff_new_pack.0y0XYi/_old 2026-09-08 17:02:12.791241584 +0200 +++ /var/tmp/diff_new_pack.0y0XYi/_new 2026-09-08 17:02:12.795241751 +0200 @@ -2,7 +2,7 @@ <service name="tar_scm" mode="manual"> <param name="url">https://github.com/elixir-lang/ex_doc</param> <param name="versionformat">@PARENT_TAG@</param> - <param name="revision">v0.38.1</param> + <param name="revision">v0.40.3</param> <param name="scm">git</param> <param name="changesgenerate">enable</param> <param name="versionrewrite-pattern">v(.+)</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.0y0XYi/_old 2026-09-08 17:02:12.827243088 +0200 +++ /var/tmp/diff_new_pack.0y0XYi/_new 2026-09-08 17:02:12.838243548 +0200 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/elixir-lang/ex_doc</param> - <param name="changesrevision">b28eaa93d568d519f949a110687e048faad41bb6</param></service></servicedata> + <param name="changesrevision">07a6abc805e98247d16eed02a49f6970d0440760</param></service></servicedata> (No newline at EOF) ++++++ build.specials.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.gitignore new/.gitignore --- old/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/.gitignore 2026-08-06 01:55:49.000000000 +0200 @@ -0,0 +1,2 @@ +.osc/ +ex_doc/ ++++++ ex_doc-0.38.1.tar.xz -> ex_doc-0.40.3.tar.xz ++++++ ++++ 23848 lines of diff (skipped) ++++++ ex_doc-rpmlintrc ++++++ # ex_doc package IS arch dependent # See https://github.com/elixir-lang/elixir/issues/2785 for details addFilter("no-binary") ++++++ fix-upstream-reprod-epub.patch ++++++ >From 23cbd237c9913945096837bab50e14887e1972ad Mon Sep 17 00:00:00 2001 From: "Bernhard M. Wiedemann" <[email protected]> Date: Mon, 27 Jul 2026 23:07:28 +0200 Subject: [PATCH] epub: Honor SOURCE_DATE_EPOCH for dcterms:modified and dc:identifier (#2256) --- lib/ex_doc/formatter/epub.ex | 37 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/lib/ex_doc/formatter/epub.ex b/lib/ex_doc/formatter/epub.ex index 64cafbf54..5fdd9257b 100644 --- a/lib/ex_doc/formatter/epub.ex +++ b/lib/ex_doc/formatter/epub.ex @@ -59,7 +59,7 @@ defmodule ExDoc.Formatter.EPUB do end defp generate_content(config, modules, tasks, extras, static_files) do - uuid = "urn:uuid:#{uuid4()}" + uuid = "urn:uuid:#{uuid(config)}" datetime = format_datetime() static_files = @@ -137,7 +137,17 @@ defmodule ExDoc.Formatter.EPUB do # Helper to format Erlang datetime tuple defp format_datetime do - {{year, month, day}, {hour, min, sec}} = :calendar.universal_time() + # Use SOURCE_DATE_EPOCH instead of the current time when set, for + # reproducible builds + # (https://reproducible-builds.org/docs/source-date-epoch/). + {{year, month, day}, {hour, min, sec}} = + with epoch when is_binary(epoch) <- System.get_env("SOURCE_DATE_EPOCH"), + {seconds, ""} <- Integer.parse(epoch) do + :calendar.system_time_to_universal_time(seconds, :second) + else + _ -> :calendar.universal_time() + end + list = [year, month, day, hour, min, sec] "~4..0B-~2..0B-~2..0BT~2..0B:~2..0B:~2..0BZ" @@ -145,21 +155,12 @@ defmodule ExDoc.Formatter.EPUB do |> IO.iodata_to_binary() end - @two_power_16 65536 - @two_power_32 4_294_967_296 - @two_power_48 281_474_976_710_656 - - defp uuid4 do - Enum.map_join( - [ - <<:rand.uniform(@two_power_32) - 1::32>>, - <<:rand.uniform(@two_power_16) - 1::16>>, - <<:rand.uniform(@two_power_16) - 1::16>>, - <<:rand.uniform(@two_power_16) - 1::16>>, - <<:rand.uniform(@two_power_48) - 1::48>> - ], - <<45>>, - &Base.encode16(&1, case: :lower) - ) + # For reproducible builds, derive the identifier from the project name + # and version instead of random data + defp uuid(config) do + <<a::binary-size(4), b::binary-size(2), c::binary-size(2), d::binary-size(2), + e::binary-size(6)>> = :erlang.md5("#{config.project} #{config.version}") + + Enum.map_join([a, b, c, d, e], <<45>>, &Base.encode16(&1, case: :lower)) end end ++++++ fix-upstream-reprod-html.patch ++++++ >From a37128a2c334b35c1612eb4be9ff8a6c19115edf Mon Sep 17 00:00:00 2001 From: "Bernhard M. Wiedemann" <[email protected]> Date: Mon, 27 Jul 2026 09:48:59 +0200 Subject: [PATCH] Use a fixed makeup group_prefix for reproducible HTML output (#2255) makeup's lexers default to a random 10-digit group_prefix, generated anew on every lexer invocation, so every data-group-id attribute in highlighted code blocks differs between two builds of the same docs. Group ids only need to be unique within a single code block: the hover-highlighting JS (assets/js/makeup.js) resolves them through parentElement, never across blocks. Passing a fixed prefix therefore changes nothing functionally while making the generated HTML (and the content-hashed search_data/sidebar file names derived from it) reproducible. --- lib/ex_doc/doc_ast.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ex_doc/doc_ast.ex b/lib/ex_doc/doc_ast.ex index c7f7ece2d..94a7b65a3 100644 --- a/lib/ex_doc/doc_ast.ex +++ b/lib/ex_doc/doc_ast.ex @@ -246,7 +246,7 @@ defmodule ExDoc.DocAST do try do Makeup.highlight_inner_html(code, lexer: lexer, - lexer_options: lexer_opts, + lexer_options: Keyword.put_new(lexer_opts, :group_prefix, "group"), formatter_options: opts ) rescue ++++++ fix-upstream-reprod-utils.patch ++++++ >From 5220946a5e918155ef38c2437b8bc8d392c06b9a Mon Sep 17 00:00:00 2001 From: "Bernhard M. Wiedemann" <[email protected]> Date: Mon, 27 Jul 2026 23:42:49 +0200 Subject: [PATCH] Sort @standard_token_types for reproducible compilation (#75) --- lib/makeup/token/utils.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/makeup/token/utils.ex b/lib/makeup/token/utils.ex index 17f3d25..2317590 100644 --- a/deps/makeup/lib/makeup/token/utils.ex +++ b/deps/makeup/lib/makeup/token/utils.ex @@ -95,7 +95,7 @@ defmodule Makeup.Token.Utils do @precedence Hierarchy.hierarchy_to_precedence(@hierarchy) @token_to_class_map Hierarchy.style_to_class_map(@hierarchy) - @standard_token_types Map.keys(@token_to_class_map) + @standard_token_types @token_to_class_map |> Map.keys() |> Enum.sort() def precedence do @precedence ++++++ vendor.tar.gz ++++++ Binary files old/deps/makeup_erlang/.hex and new/deps/makeup_erlang/.hex differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/deps/makeup_erlang/CHANGELOG.md new/deps/makeup_erlang/CHANGELOG.md --- old/deps/makeup_erlang/CHANGELOG.md 1970-01-01 01:00:00.000000000 +0100 +++ new/deps/makeup_erlang/CHANGELOG.md 2026-08-06 01:16:07.000000000 +0200 @@ -0,0 +1,94 @@ +# Changelog + +## v1.1.0 (2026-05-08) + +### Added + + * Support for OTP 27+ triple-, quadruple-, and quintuple-quoted strings, + including the matching sigil delimiters. + * Support for OTP 27 underscore separators in numeric literals + (`1_000_000`, `16#FF_FF`, `0.1_5e1_0`). + * Support for OTP 25/27 `?=` maybe-match operator as a single token. + * Support for OTP 29 native records, including the external + `#Module:Name{...}` / `#Module:Name.field` forms and variable-shape / + keyword record names (`#State{}`, `#case{}`, `#fun{}`). + * Distinct token for parameterised macro calls (`?FOO(args)` → + `:name_function`) versus parameterless macro references (`?FOO` → + `:name_constant`). + * `:string_escape` sub-tokens emitted inside plain double-quoted strings + (previously only emitted inside triple-quoted strings). + * Multi-character escape sequences (`$\xFF`, `$\x{1F600}`, `$\077`, + `$\^A`) in `$\...` character literals tokenised as a single escape. + * Underscore-prefixed identifiers (`_5`, `_X`, `_unused`) lex as a single + `:name` variable rather than `_` + integer/name. + +### Changed + + * BIF list is now generated at compile time from `erl_internal:bif/2`, + keeping the recognised builtins in sync with the OTP version + `makeup_erlang` is compiled against. Adds 30+ post-OTP-19 BIFs and + fixes one stale typo. Closes #13. + * Reserved words written adjacent to `(` (e.g. `fun(X)`) are recovered + as `:keyword` instead of misclassified `:name_function`. + * Erlang shell prompts (`1> ...`) are detected after multi-line + whitespace blocks, not only after a single immediate `\n`. + +## v1.0.3 + + * Do not tag newlines before prompts as unselectable. + * LICENSE and CI housekeeping. + +## v1.0.2 + +### Added + + * Support for strict generators in comprehensions. + * Support for parallel comprehensions. + * Group handling for binaries. + * Elixir 1.18 and Erlang 27 in the CI matrix. + * Tests for list and binary comprehensions. + +## v1.0.1 + + * Parse tabs correctly (#29). + +## v1.0.0 + + * Properly deal with unicode characters in `$` character literals. + * Fix publishing and generation of docs. + +## v0.1.5 + + * Highlight errors in shell examples correctly. + +## v0.1.4 + + * Support for `maybe ... end` expressions. + * Support for triple-quoted strings and sigils. + * CI updates. + +## v0.1.3 + + * Support for the Erlang shell prompt (`1> ...`) (#24). + * Use the SPDX-conformant license name. + * Run `mix format` over the codebase. + +## v0.1.2 + + * Require Elixir 1.6 or newer (#17). + * Drop unused `assert_value` dependency (#18). + * Remove `@inline` annotations (#22). + * CI configuration cleanup. + +## v0.1.1 + +Initial public release. + + * Tokenisation of module attributes (with optional parentheses), atoms, + binary syntax, charlists, escaped characters, operators and + punctuation. + * Tokenisation of records: creation, access, and update. + * Tokenisation of qualified function calls — module name tagged as + `:name_class`. + * Combinator for function/arity syntax (`fun/N`). + * Registers itself as a Makeup lexer on application boot. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/deps/makeup_erlang/LICENSE new/deps/makeup_erlang/LICENSE --- old/deps/makeup_erlang/LICENSE 1970-01-01 01:00:00.000000000 +0100 +++ new/deps/makeup_erlang/LICENSE 2026-08-06 01:16:07.000000000 +0200 @@ -0,0 +1,25 @@ +BSD 2-Clause License + +Copyright (c) 2019 by the respective authors (see AUTHORS file). +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/deps/makeup_erlang/hex_metadata.config new/deps/makeup_erlang/hex_metadata.config --- old/deps/makeup_erlang/hex_metadata.config 2025-05-26 18:34:20.000000000 +0200 +++ new/deps/makeup_erlang/hex_metadata.config 2026-08-06 01:16:07.000000000 +0200 @@ -1,17 +1,9 @@ {<<"links">>, [{<<"GitHub">>,<<"https://github.com/elixir-makeup/makeup_erlang">>}]}. {<<"name">>,<<"makeup_erlang">>}. -{<<"version">>,<<"1.0.2">>}. +{<<"version">>,<<"1.1.0">>}. {<<"description">>,<<"Erlang lexer for the Makeup syntax highlighter.">>}. {<<"elixir">>,<<"~> 1.6">>}. -{<<"files">>, - [<<"lib">>,<<"lib/makeup">>,<<"lib/makeup/lexers">>, - <<"lib/makeup/lexers/erlang_lexer.ex">>, - <<"lib/makeup/lexers/erlang_lexer">>, - <<"lib/makeup/lexers/erlang_lexer/testing.ex">>, - <<"lib/makeup/lexers/erlang_lexer/application.ex">>, - <<"lib/makeup/lexers/erlang_lexer/helper.ex">>,<<".formatter.exs">>, - <<"mix.exs">>,<<"README.md">>]}. {<<"app">>,<<"makeup_erlang">>}. {<<"licenses">>,[<<"BSD-2-Clause">>]}. {<<"requirements">>, @@ -20,4 +12,12 @@ {<<"optional">>,false}, {<<"requirement">>,<<"~> 1.0">>}, {<<"repository">>,<<"hexpm">>}]]}. +{<<"files">>, + [<<"lib">>,<<"lib/makeup">>,<<"lib/makeup/lexers">>, + <<"lib/makeup/lexers/erlang_lexer.ex">>, + <<"lib/makeup/lexers/erlang_lexer">>, + <<"lib/makeup/lexers/erlang_lexer/testing.ex">>, + <<"lib/makeup/lexers/erlang_lexer/application.ex">>, + <<"lib/makeup/lexers/erlang_lexer/helper.ex">>,<<".formatter.exs">>, + <<"mix.exs">>,<<"README.md">>,<<"LICENSE">>,<<"CHANGELOG.md">>]}. {<<"build_tools">>,[<<"mix">>]}. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/deps/makeup_erlang/lib/makeup/lexers/erlang_lexer.ex new/deps/makeup_erlang/lib/makeup/lexers/erlang_lexer.ex --- old/deps/makeup_erlang/lib/makeup/lexers/erlang_lexer.ex 2025-05-26 18:34:20.000000000 +0200 +++ new/deps/makeup_erlang/lib/makeup/lexers/erlang_lexer.ex 2026-08-06 01:16:07.000000000 +0200 @@ -14,7 +14,7 @@ # Step #1: tokenize the input (into a list of tokens) ################################################################### - whitespace = ascii_string([?\s, ?\f, ?\n, ?\t], min: 1) |> token(:whitespace) + whitespace = ascii_string([?\s, ?\f, ?\r, ?\n, ?\t], min: 1) |> token(:whitespace) # This is the combinator that ensures that the lexer will never reject a file # because of invalid input syntax @@ -59,24 +59,29 @@ ]) # Numbers - digits = ascii_string([?0..?9], min: 1) + # + # Erlang/OTP 27 added underscore separators in numeric literals + # (`1_000_000`, `16#FF_FF`, `0.1_5e1_0`). Lexer-tolerant: underscores are + # accepted anywhere inside the digit run; we don't validate position. + digits = ascii_string([?0..?9, ?_], min: 1) number_integer = optional(ascii_char([?+, ?-])) - |> concat(digits) + |> ascii_char([?0..?9]) + |> optional(ascii_string([?0..?9, ?_], min: 1)) |> token(:number_integer) number_integer_in_weird_base = optional(ascii_char([?+, ?-])) |> concat(numeric_base) |> string("#") - |> ascii_string([?0..?9, ?a..?z, ?A..?Z], min: 1) + |> ascii_string([?0..?9, ?a..?z, ?A..?Z, ?_], min: 1) |> token(:number_integer) # Floating point numbers float_scientific_notation_part = ascii_string([?e, ?E], 1) - |> optional(string("-")) + |> optional(ascii_char([?+, ?-])) |> concat(digits) number_float = @@ -91,6 +96,16 @@ ascii_string([?A..?Z, ?_], 1) |> optional(ascii_string([?a..?z, ?_, ?0..?9, ?A..?Z], min: 1)) + # An underscore followed by at least one identifier character (`_5`, + # `_X`, `_unused`). Bare `_` stays as a punctuation token (the wildcard + # pattern), but `_<id>` is a variable in Erlang grammar and should + # render as `:name`. Without this rule the `_` is matched first by + # the `punctuation` rule and the rest of the identifier falls through. + underscore_identifier = + string("_") + |> ascii_string([?a..?z, ?_, ?0..?9, ?A..?Z], min: 1) + |> token(:name) + simple_atom_name = ascii_string([?a..?z], 1) |> optional(ascii_string([?a..?z, ?_, ?@, ?0..?9, ?A..?Z], min: 1)) @@ -141,6 +156,20 @@ macro_name = choice([variable_name, atom_name]) + # Parameterised macro reference: `?FOO(arg1, arg2)`. Tokenised + # separately from the parameterless form so themes can render the two + # distinctly (matches `makeup_elixir`'s split between `@foo` and + # `@foo(...)`). The macro head emits as `:name_function`; the trailing + # `(` opens the standard punctuation group so paren matching still + # works. + macro_call = + string("?") + |> concat(macro_name) + |> token(:name_function) + |> concat(optional(whitespace)) + |> concat(token("(", :punctuation)) + + # Parameterless macro: `?FOO`. Constants by convention. macro = string("?") |> concat(macro_name) @@ -152,10 +181,23 @@ |> optional(string(".") |> concat(atom_name)) |> token(:name_label) + # `$\xFF`, `$\x{1F600}`, `$\077`, `$\^A`, plus simple `$\n` / `$\t` / `$\\` / + # `$\"` / `$\'` etc. The structured escapes (octal, hex, ctrl) must be tried + # before the single-char fallback so multi-character sequences are consumed + # whole. + character_escape = + string("\\") + |> choice([ + escape_hex, + escape_octal, + escape_ctrl, + utf8_char([]) + ]) + character = string("$") |> choice([ - string("\\") |> utf8_char([]), + character_escape, utf8_char(not: ?\\) ]) |> token(:string_char) @@ -166,18 +208,55 @@ |> ascii_char(to_charlist("~#+BPWXb-ginpswx")) |> token(:string_interpol) - escape_double_quote = string(~s/\\"/) - erlang_string = string_like(~s/"/, ~s/"/, [escape_double_quote, string_interpol], :string) - + # Sub-token emitted inside string literals for escape sequences. Mirrors + # the `character_escape` shape so multi-character escapes (`\xFF`, + # `\x{1F600}`, `\077`, `\^A`) are consumed whole instead of getting + # cut at the first byte. Themes can render these distinctly. escaped_char = string("\\") - |> utf8_string([], 1) + |> choice([ + escape_hex, + escape_octal, + escape_ctrl, + utf8_char([]) + ]) |> token(:string_escape) + erlang_string = string_like(~s/"/, ~s/"/, [escaped_char, string_interpol], :string) + + # Multi-quoted strings (OTP 27+). The opening run of `"""` (or more) on + # its own line opens the string; a matching run on its own line closes + # it. Use a quadruple/quintuple opener when the body needs to contain + # `"""` literally. Each variant is a separate rule because NimbleParsec + # doesn't support dynamic delimiter lengths; longer-quote variants must + # be tried first so the triple-quote rule doesn't claim them prematurely. + quintuple_quoted_string = + lookahead_string( + string(~s/"""""\n/), + string(~s/\n"""""/), + [escaped_char, string_interpol] + ) + + quadruple_quoted_string = + lookahead_string( + string(~s/""""\n/), + string(~s/\n""""/), + [escaped_char, string_interpol] + ) + triple_quoted_string = lookahead_string(string(~s/"""\n/), string(~s/\n"""/), [escaped_char, string_interpol]) + # Longer-quote variants must come first so the longest matching delimiter + # wins for sigils like `~"""""..."""""` (quintuple) or `~""""..."""" ` + # (quadruple) — these are needed when the sigil body has to contain + # `"""` or `""""` literally, mirroring the rule for plain multi-quoted + # strings above. sigil_delimiters = [ + {~s["""""\n], ~s[\n"""""]}, + {"'''''\n", "\n'''''"}, + {~s[""""\n], ~s[\n""""]}, + {"''''\n", "\n''''"}, {~s["""\n], ~s[\n"""]}, {"'''\n", "\n'''"}, {"\"", "\""}, @@ -220,13 +299,47 @@ syntax_operators = word_from_list( - ~W[+ - +? ++ = == -- * / < > /= =:= =/= =< >= ==? <- <:- <= <:= ! ? ?!], + ~W[+ - +? ++ = == -- * / < > /= =:= =/= =< >= ==? <- <:- <= <:= ! ? ?! ?=], :operator ) + # OTP 29 native records relax the record-name rule: per the spec + # (https://www.erlang.org/doc/system/data_types.html), "it is not necessary + # to quote atoms that look like variable names or keywords." So `#State{}`, + # `#div{}`, `#case{}` are all valid record references even though `State` + # is variable-shape and `div`/`case` are reserved words. Tuple-based records + # don't allow these forms, but the lexer can't tell the two record kinds + # apart from local context — so accept the union. + # + # The `record_name: true` meta marker tells postprocess to skip the + # keyword / builtin / word-operator conversion for this position. Without + # it, `#case{}` would tokenise as `[#, keyword case, {]` — visually + # confusing because `case` here names a record, not an expression keyword. + record_name = + choice([ + token(atom_name, :string_symbol, %{record_name: true}), + token(variable_name, :string_symbol, %{record_name: true}) + ]) + + # External native record construction / pattern / field access: + # #Module:Name{F = V} + # #Module:Name.field + # The `Module:Name` shape between `#` and `{` (or `.`) was added in OTP 29 + # alongside native records. Local construction (`#Name{...}`) is identical + # in shape to a tuple-based record and is handled by the rule below. + native_record_external = + token(string("#"), :operator) + |> concat(token(atom_name, :name_class)) + |> concat(token(":", :punctuation)) + |> concat(record_name) + |> choice([ + token("{", :punctuation), + token(".", :punctuation) + ]) + record = token(string("#"), :operator) - |> concat(atom) + |> concat(record_name) |> choice([ token("{", :punctuation), token(".", :punctuation) @@ -249,13 +362,26 @@ |> concat(token("/", :punctuation)) |> concat(number_integer) - # Erlang prompt + # Erlang prompt. Anchored to a line boundary by requiring the leading + # whitespace to contain at least one `\n`. The original rule required + # the `\n` immediately before the prompt body, which broke when the + # generic `whitespace` rule had already consumed the trailing `\n` of + # a multi-character whitespace block (see makeup_elixir #28). Allowing + # any leading non-newline whitespace before the `\n` and any further + # whitespace after lets the rule match in those cases without + # false-positiving on `1 > 2` or `x. 1> a.` (neither contains a `\n` + # in the relevant position). erl_prompt = - string("\n") - |> optional(string("(") |> concat(atom_name) |> string(")")) - |> optional(digits) - |> string("> ") - |> token(:generic_prompt, %{selectable: false}) + ascii_string([?\s, ?\f, ?\r, ?\t], min: 0) + |> string("\n") + |> optional(ascii_string([?\s, ?\f, ?\r, ?\n, ?\t], min: 1)) + |> token(:whitespace) + |> concat( + optional(string("(") |> concat(atom_name) |> string(")")) + |> optional(digits) + |> string("> ") + |> token(:generic_prompt, %{selectable: false}) + ) # Error in shell erl_shell_error = @@ -295,12 +421,21 @@ hashbang, whitespace, comment, + quintuple_quoted_string, + quadruple_quoted_string, triple_quoted_string, erlang_string ] ++ all_sigils ++ [ + native_record_external, record, + underscore_identifier, + # Macros must be tried before `syntax_operators`, since the + # operator list contains `?` and `?=` and would otherwise eat the + # leading `?` of `?FOO` / `?FOO(X)`. + macro_call, + macro, punctuation, # `tuple` might be unnecessary tuple, @@ -315,7 +450,6 @@ function_arity, function, atom, - macro, character, label, # If we can't parse any of the above, we highlight the next character as an error @@ -349,38 +483,45 @@ @keywords ~W[after begin case catch cond end fun if let of query receive try when maybe else] - @builtins ~W[ - abs append_element apply atom_to_list binary_to_list bitstring_to_list - binary_to_term bit_size bump_reductions byte_size cancel_timer - check_process_code delete_module demonitor disconnect_node display - element erase exit float float_to_list fun_info fun_to_list - function_exported garbage_collect get get_keys group_leader hash - hd integer_to_list iolist_to_binary iolist_size is_atom is_binary - is_bitstring is_boolean is_builtin is_float is_function is_integer - is_list is_number is_pid is_port is_process_alive is_record is_reference - is_tuple length link list_to_atom list_to_binary list_to_bitstring - list_to_existing_atom list_to_float list_to_integer list_to_pid - list_to_tuple load_module localtime_to_universaltime make_tuple - md5 md5_final md5_update memory module_loaded monitor monitor_node - node nodes open_port phash phash2 pid_to_list port_close port_command - port_connect port_control port_call port_info port_to_list - process_display process_flag process_info purge_module put read_timer - ref_to_list register resume_processround send send_after send_nosuspend - set_cookie setelement size spawn spawn_link spawn_monitor spawn_opt - split_binary start_timer statistics suspend_process system_flag - system_info system_monitor system_profile term_to_binary tl trace - trace_delivered trace_info trace_pattern trunc tuple_size tuple_to_list - universaltime_to_localtime unlink unregister whereis - ] + # Auto-imported BIFs, sourced at compile time from `erl_internal:bif/2` — + # the same predicate the Erlang compiler uses to decide what's auto-imported. + # Refreshed every time `makeup_erlang` is rebuilt, so the list stays in sync + # with the OTP version we compile against and never bit-rots. + @builtins :erlang.module_info(:exports) + |> Enum.filter(fn {name, arity} -> :erl_internal.bif(name, arity) end) + |> Enum.map(fn {name, _arity} -> Atom.to_string(name) end) + |> Enum.uniq() + |> Enum.sort() @word_operators ~W[and andalso band bnot bor bsl bsr bxor div not or orelse rem xor] + # Record names tagged by the `record_name` combinator should not be + # reclassified as keywords / builtins / word-operators even if their + # text happens to match. Strip the marker after acting on it so it + # doesn't leak into the rendered output. + defp postprocess_helper([{:string_symbol, %{record_name: true} = meta, value} | tokens]), + do: [{:string_symbol, Map.delete(meta, :record_name), value} | postprocess_helper(tokens)] + defp postprocess_helper([{:string_symbol, meta, value} | tokens]) when value in @keywords, do: [{:keyword, meta, value} | postprocess_helper(tokens)] + # Keywords followed by `(` are first matched by the `function` rule and + # tagged `:name_function`. Recover them here. The most common case is + # `fun(X) -> ... end`; the rule also covers any other keyword that gets + # written next to `(` (e.g. `if(X)` in a teaching example of invalid + # syntax). + defp postprocess_helper([{:name_function, meta, value} | tokens]) when value in @keywords, + do: [{:keyword, meta, value} | postprocess_helper(tokens)] + defp postprocess_helper([{:string_symbol, meta, value} | tokens]) when value in @builtins, do: [{:name_builtin, meta, value} | postprocess_helper(tokens)] + # Same recovery for builtins: when a BIF is called as `length(L)` it is + # first matched by the `function` rule and tagged `:name_function`. Closes + # makeup_erlang #13. + defp postprocess_helper([{:name_function, meta, value} | tokens]) when value in @builtins, + do: [{:name_builtin, meta, value} | postprocess_helper(tokens)] + defp postprocess_helper([{:string_symbol, meta, value} | tokens]) when value in @word_operators, do: [{:operator_word, meta, value} | postprocess_helper(tokens)] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/deps/makeup_erlang/mix.exs new/deps/makeup_erlang/mix.exs --- old/deps/makeup_erlang/mix.exs 2025-05-26 18:34:20.000000000 +0200 +++ new/deps/makeup_erlang/mix.exs 2026-08-06 01:16:07.000000000 +0200 @@ -1,7 +1,7 @@ defmodule MakeupErlang.Mixfile do use Mix.Project - @version "1.0.2" + @version "1.1.0" @url "https://github.com/elixir-makeup/makeup_erlang" def project do @@ -56,7 +56,7 @@ paths = Path.join(Mix.Project.build_path(), "lib/*/ebin") args = ["MakeupErlang", @version, Mix.Project.compile_path()] - opts = ~w[--main Makeup.Lexers.ErlangLexer --source-ref v#{@version} --source-url #{@url}] + opts = ~w[--source-ref v#{@version} --source-url #{@url} --config .ex_doc.exs] System.cmd(ex_doc, args ++ ["--paths", paths] ++ opts) Mix.shell().info("Docs built successfully") end
