texlive[73119] Master/texmf-dist: expltools (14dec24)
commits+karl at tug.org
commits+karl at tug.org
Sat Dec 14 22:17:22 CET 2024
Revision: 73119
https://tug.org/svn/texlive?view=revision&revision=73119
Author: karl
Date: 2024-12-14 22:17:22 +0100 (Sat, 14 Dec 2024)
Log Message:
-----------
expltools (14dec24)
Modified Paths:
--------------
trunk/Master/texmf-dist/doc/support/expltools/CHANGES.md
trunk/Master/texmf-dist/doc/support/expltools/README.md
trunk/Master/texmf-dist/doc/support/expltools/project-proposal.pdf
trunk/Master/texmf-dist/doc/support/expltools/warnings-and-errors.pdf
trunk/Master/texmf-dist/scripts/expltools/explcheck-cli.lua
trunk/Master/texmf-dist/scripts/expltools/explcheck-format.lua
Modified: trunk/Master/texmf-dist/doc/support/expltools/CHANGES.md
===================================================================
--- trunk/Master/texmf-dist/doc/support/expltools/CHANGES.md 2024-12-14 00:42:01 UTC (rev 73118)
+++ trunk/Master/texmf-dist/doc/support/expltools/CHANGES.md 2024-12-14 21:17:22 UTC (rev 73119)
@@ -1,5 +1,33 @@
# Changes
+## expltools 2024-12-13
+
+### explcheck v0.2.0
+
+#### Development
+
+- Add a command-line option `--porcelain` for machine-readable output.
+ (suggested by @FrankMittelbach in #8, added in #14)
+
+ See <https://github.com/Witiko/expltools/pull/15#issuecomment-2542418484>
+ and below for a demonstration of how you might set up your text editor, so
+ that it automatically navigates you to lines with warnings and errors.
+
+#### Fixes
+
+- In the command-line interface, forbid the checking of .ins and .dtx files.
+ Display messages that direct users to check the generated files instead.
+ (reported by @josephwright and @FrankMittelbach in #8, fixed in #14)
+
+- Expect both backslashes and forward slashes when shortening pathnames. (#14)
+
+- Correctly pluralize "1 file" on the first line of command-line output. (#14)
+
+#### Documentation
+
+- Normalize the behavior and documentation of functions `get_*()` across files
+ `explcheck/build.lua`, `explcheck/test.lua`, and `explcheck-cli.lua`. (#14)
+
## expltools 2024-12-09
### explcheck v0.1.1
@@ -7,7 +35,6 @@
#### Fixes
- In LuaTeX, initialize Kpathsea Lua module searchers first.
-
(reported by @josephwright, Lars Madsen, and Philip Taylor on
[tex-live at tug.org][tex-live-02] and by @muzimuzhi in #9,
fixed on [tex-live at tug.org][tex-live-03] by @gucci-on-fleek)
@@ -23,7 +50,7 @@
- Include explcheck version in the command-line interface.
(reported in #10, fixed in #13)
-- Hint in the file `README.md` that .dtx are not well supported.
+- Hint in the file `README.md` that .dtx files are not well-supported.
(reported by @josephwright in #8, added in #13)
- Show in the file `README.md` how explcheck can be used from Lua code. (#13)
Modified: trunk/Master/texmf-dist/doc/support/expltools/README.md
===================================================================
--- trunk/Master/texmf-dist/doc/support/expltools/README.md 2024-12-14 00:42:01 UTC (rev 73118)
+++ trunk/Master/texmf-dist/doc/support/expltools/README.md 2024-12-14 21:17:22 UTC (rev 73119)
@@ -81,8 +81,8 @@
You can prepare the expltools bundle for distribution with the following two commands:
-- `l3build tag`: Add the current version numbers to the file `explcheck-lua.cli`.
-- `l3build ctan`: Run tests, build the documentation, and create a CTAN archive `expltools-ctan.zip`.
+1. `l3build tag`: Add the current version numbers to the file `explcheck-lua.cli`.
+2. `l3build ctan`: Run tests, build the documentation, and create a CTAN archive `expltools-ctan.zip`.
The file `explcheck.lua` should be installed in the TDS directory `scripts/expltools/explcheck`. Furthermore, it should be made executable and either symlinked to system directories as `explcheck` on Unix or have a wrapper `explcheck.exe` installed on Windows.
Modified: trunk/Master/texmf-dist/doc/support/expltools/project-proposal.pdf
===================================================================
(Binary files differ)
Modified: trunk/Master/texmf-dist/doc/support/expltools/warnings-and-errors.pdf
===================================================================
(Binary files differ)
Modified: trunk/Master/texmf-dist/scripts/expltools/explcheck-cli.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/expltools/explcheck-cli.lua 2024-12-14 00:42:01 UTC (rev 73118)
+++ trunk/Master/texmf-dist/scripts/expltools/explcheck-cli.lua 2024-12-14 21:17:22 UTC (rev 73119)
@@ -24,12 +24,71 @@
return deduplicated_pathnames
end
+-- Convert a pathname of a file to the suffix of the file.
+local function get_suffix(pathname)
+ return pathname:gsub(".*%.", "."):lower()
+end
+
+-- Convert a pathname of a file to the base name of the file.
+local function get_basename(pathname)
+ return pathname:gsub(".*[\\/]", "")
+end
+
+-- Convert a pathname of a file to the pathname of its parent directory.
+local function get_parent(pathname)
+ if pathname:find("[\\/]") then
+ return pathname:gsub("(.*)[\\/].*", "%1")
+ else
+ return "."
+ end
+end
+
+-- Check that the pathname specifies a file that we can process.
+local function check_pathname(pathname)
+ local suffix = get_suffix(pathname)
+ if suffix == ".ins" then
+ local basename = get_basename(pathname)
+ if basename:find(" ") then
+ basename = "'" .. basename .. "'"
+ end
+ return
+ false,
+ "explcheck can't currently process .ins files directly\n"
+ .. 'Use a command such as "luatex ' .. basename .. '" '
+ .. "to generate .tex, .cls, and .sty files and process these files instead."
+ elseif suffix == ".dtx" then
+ local parent = get_parent(pathname)
+ local basename = "*.ins"
+ local has_lfs, lfs = pcall(require, "lfs")
+ if has_lfs then
+ for candidate_basename in lfs.dir(parent) do
+ local candidate_suffix = get_suffix(candidate_basename)
+ if candidate_suffix == ".ins" then
+ basename = candidate_basename
+ if basename:find(" ") then
+ basename = "'" .. candidate_basename .. "'"
+ end
+ break
+ end
+ end
+ end
+ return
+ false,
+ "explcheck can't currently process .dtx files directly\n"
+ .. 'Use a command such as "luatex ' .. basename .. '" '
+ .. "to generate .tex, .cls, and .sty files and process these files instead."
+ end
+ return true
+end
+
-- Process all input files.
-local function main(pathnames, warnings_are_errors, max_line_length)
+local function main(pathnames, warnings_are_errors, max_line_length, porcelain)
local num_warnings = 0
local num_errors = 0
- print("Checking " .. #pathnames .. " files")
+ if not porcelain then
+ print("Checking " .. #pathnames .. " " .. format.pluralize("file", #pathnames))
+ end
for pathname_number, pathname in ipairs(pathnames) do
@@ -53,11 +112,13 @@
::continue::
num_warnings = num_warnings + #issues.warnings
num_errors = num_errors + #issues.errors
- format.print_results(pathname, issues, line_starting_byte_numbers, pathname_number == #pathnames)
+ format.print_results(pathname, issues, line_starting_byte_numbers, pathname_number == #pathnames, porcelain)
end
-- Print a summary.
- format.print_summary(#pathnames, num_warnings, num_errors)
+ if not porcelain then
+ format.print_summary(#pathnames, num_warnings, num_errors, porcelain)
+ end
if(num_errors > 0) then
return 1
@@ -72,12 +133,13 @@
print("Usage: " .. arg[0] .. " [OPTIONS] FILENAMES\n")
print("Run static analysis on expl3 files.\n")
print("Options:")
- print("\t--max-line-length=N\tThe maximum line length before the warning S103 (Line too long) is produced.")
- print("\t--warnings-are-errors\tProduce a non-zero exit code if any warnings are produced by the analysis.")
+ print("\t--max-line-length=N The maximum line length before the warning S103 (Line too long) is produced.")
+ print("\t--porcelain Produce machine-readable output.")
+ print("\t--warnings-are-errors Produce a non-zero exit code if any warnings are produced by the analysis.")
end
local function print_version()
- print("explcheck (expltools 2024-12-09) v0.1.1")
+ print("explcheck (expltools 2024-12-13) v0.2.0")
print("Copyright (c) 2024 Vít Starý Novotný")
print("Licenses: LPPL 1.3 or later, GNU GPL v2 or later")
end
@@ -91,6 +153,7 @@
local warnings_are_errors = false
local only_pathnames_from_now_on = false
local max_line_length = nil
+ local porcelain = false
for _, argument in ipairs(arg) do
if only_pathnames_from_now_on then
table.insert(pathnames, argument)
@@ -104,6 +167,8 @@
os.exit(0)
elseif argument == "--warnings-are-errors" then
warnings_are_errors = true
+ elseif argument == "--porcelain" then
+ porcelain = true
elseif argument:sub(1, 18) == "--max-line-length=" then
max_line_length = tonumber(argument:sub(19))
elseif argument:sub(1, 2) == "--" then
@@ -114,9 +179,18 @@
table.insert(pathnames, argument)
end
end
+
+ -- Deduplicate and check that pathnames specify files that we can process.
pathnames = deduplicate_pathnames(pathnames)
+ for _, pathname in ipairs(pathnames) do
+ local is_ok, error_message = check_pathname(pathname)
+ if not is_ok then
+ print('Failed to process "' .. pathname .. '": ' .. error_message)
+ os.exit(1)
+ end
+ end
-- Run the analysis.
- local exit_code = main(pathnames, warnings_are_errors, max_line_length)
+ local exit_code = main(pathnames, warnings_are_errors, max_line_length, porcelain)
os.exit(exit_code)
end
Modified: trunk/Master/texmf-dist/scripts/expltools/explcheck-format.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/expltools/explcheck-format.lua 2024-12-14 00:42:01 UTC (rev 73118)
+++ trunk/Master/texmf-dist/scripts/expltools/explcheck-format.lua 2024-12-14 21:17:22 UTC (rev 73119)
@@ -16,9 +16,9 @@
while #pathname > max_length do
local pattern
if first_iteration then
- pattern = "([^/]*)/[^/]*/(.*)"
+ pattern = "([^\\/]*)[\\/][^\\/]*[\\/](.*)"
else
- pattern = "([^/]*)/%.%.%./[^/]*/(.*)"
+ pattern = "([^\\/]*)/%.%.%.[\\/][^\\/]*[\\/](.*)"
end
local prefix_start, _, prefix, suffix = pathname:find(pattern)
if prefix_start == nil or prefix_start > 1 then
@@ -31,9 +31,9 @@
if #pathname > max_length then
local pattern
if first_iteration then
- pattern = "([^/]*/?)(.*)"
+ pattern = "([^\\/]*[\\/]?)(.*)"
else
- pattern = "([^/]*/%.%.%./)(.*)"
+ pattern = "([^\\/]*[\\/]%.%.%.[\\/])(.*)"
end
local prefix_start, _, _, suffix = pathname:find(pattern)
if prefix_start == 1 then
@@ -81,7 +81,7 @@
end
-- Print the results of analyzing a file.
-local function print_results(pathname, issues, line_starting_byte_numbers, is_last_file)
+local function print_results(pathname, issues, line_starting_byte_numbers, is_last_file, porcelain)
-- Display an overview.
local all_issues = {}
local status
@@ -95,7 +95,7 @@
), 1, 31
)
)
- table.insert(all_issues, issues.errors)
+ table.insert(all_issues, {issues.errors, "error: "})
if(#issues.warnings > 0) then
status = (
status
@@ -108,7 +108,7 @@
), 1, 33
)
)
- table.insert(all_issues, issues.warnings)
+ table.insert(all_issues, {issues.warnings, "warning: "})
end
elseif(#issues.warnings > 0) then
status = colorize(
@@ -118,45 +118,50 @@
.. pluralize("warning", #issues.warnings)
), 1, 33
)
- table.insert(all_issues, issues.warnings)
+ table.insert(all_issues, {issues.warnings, "warning: "})
else
status = colorize("OK", 1, 32)
end
- local max_overview_length = 72
- local prefix = "Checking "
- local formatted_pathname = format_pathname(
- pathname,
- math.max(
- (
- max_overview_length
- - #prefix
- - #(" ")
- - #decolorize(status)
- ), 1
- )
- )
- local overview = (
- prefix
- .. formatted_pathname
- .. (" "):rep(
+ if not porcelain then
+ local max_overview_length = 72
+ local prefix = "Checking "
+ local formatted_pathname = format_pathname(
+ pathname,
math.max(
(
max_overview_length
- #prefix
+ - #(" ")
- #decolorize(status)
- - #formatted_pathname
), 1
)
)
- .. status
- )
- io.write("\n" .. overview)
+ local overview = (
+ prefix
+ .. formatted_pathname
+ .. (" "):rep(
+ math.max(
+ (
+ max_overview_length
+ - #prefix
+ - #decolorize(status)
+ - #formatted_pathname
+ ), 1
+ )
+ )
+ .. status
+ )
+ io.write("\n" .. overview)
+ end
-- Display the errors, followed by warnings.
if #all_issues > 0 then
- for _, warnings_or_errors in ipairs(all_issues) do
- print()
+ for _, warnings_or_errors_and_porcelain_prefix in ipairs(all_issues) do
+ local warnings_or_errors, porcelain_prefix = table.unpack(warnings_or_errors_and_porcelain_prefix)
+ if not porcelain then
+ print()
+ end
-- Display the warnings/errors.
for _, issue in ipairs(issues.sort(warnings_or_errors)) do
local code = issue[1]
@@ -172,40 +177,44 @@
local reserved_suffix_length = 30
local label_indent = (" "):rep(4)
local suffix = code:upper() .. " " .. message
- formatted_pathname = format_pathname(
- pathname,
- math.max(
- (
- max_line_length
- - #label_indent
- - reserved_position_length
- - #(" ")
- - math.max(#suffix, reserved_suffix_length)
- ), 1
- )
- )
- local line = (
- label_indent
- .. formatted_pathname
- .. position
- .. (" "):rep(
+ if not porcelain then
+ local formatted_pathname = format_pathname(
+ pathname,
math.max(
(
max_line_length
- #label_indent
- - #formatted_pathname
- - #decolorize(position)
+ - reserved_position_length
+ - #(" ")
- math.max(#suffix, reserved_suffix_length)
), 1
)
)
- .. suffix
- .. (" "):rep(math.max(reserved_suffix_length - #suffix, 0))
- )
- io.write("\n" .. line)
+ local line = (
+ label_indent
+ .. formatted_pathname
+ .. position
+ .. (" "):rep(
+ math.max(
+ (
+ max_line_length
+ - #label_indent
+ - #formatted_pathname
+ - #decolorize(position)
+ - math.max(#suffix, reserved_suffix_length)
+ ), 1
+ )
+ )
+ .. suffix
+ .. (" "):rep(math.max(reserved_suffix_length - #suffix, 0))
+ )
+ io.write("\n" .. line)
+ else
+ print(pathname .. position .. " " .. porcelain_prefix .. suffix)
+ end
end
end
- if(not is_last_file) then
+ if not is_last_file and not porcelain then
print()
end
end
@@ -228,6 +237,7 @@
return {
convert_byte_to_line_and_column = convert_byte_to_line_and_column,
+ pluralize = pluralize,
print_results = print_results,
print_summary = print_summary,
}
More information about the tex-live-commits
mailing list.