mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-04-12 04:19:25 -07:00
fix(mpv-plugin): restore Lua parser compatibility
This commit is contained in:
4
changes/lua-plugin-parser-compat.md
Normal file
4
changes/lua-plugin-parser-compat.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: mpv-plugin
|
||||||
|
|
||||||
|
- Fixed the mpv Lua plugin so hover and environment modules no longer use the `goto continue` pattern that can fail to parse on some user Lua runtimes.
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
"test:config:src": "bun test src/config/config.test.ts src/config/path-resolution.test.ts src/config/resolve/anki-connect.test.ts src/config/resolve/integrations.test.ts src/config/resolve/subtitle-style.test.ts src/config/resolve/jellyfin.test.ts src/config/definitions/domain-registry.test.ts src/generate-config-example.test.ts src/verify-config-example.test.ts",
|
"test:config:src": "bun test src/config/config.test.ts src/config/path-resolution.test.ts src/config/resolve/anki-connect.test.ts src/config/resolve/integrations.test.ts src/config/resolve/subtitle-style.test.ts src/config/resolve/jellyfin.test.ts src/config/definitions/domain-registry.test.ts src/generate-config-example.test.ts src/verify-config-example.test.ts",
|
||||||
"test:config:dist": "bun test dist/config/config.test.js dist/config/path-resolution.test.js dist/config/resolve/anki-connect.test.js dist/config/resolve/integrations.test.js dist/config/resolve/subtitle-style.test.js dist/config/resolve/jellyfin.test.js dist/config/definitions/domain-registry.test.js dist/generate-config-example.test.js dist/verify-config-example.test.js",
|
"test:config:dist": "bun test dist/config/config.test.js dist/config/path-resolution.test.js dist/config/resolve/anki-connect.test.js dist/config/resolve/integrations.test.js dist/config/resolve/subtitle-style.test.js dist/config/resolve/jellyfin.test.js dist/config/definitions/domain-registry.test.js dist/generate-config-example.test.js dist/verify-config-example.test.js",
|
||||||
"test:config:smoke:dist": "bun test dist/config/path-resolution.test.js",
|
"test:config:smoke:dist": "bun test dist/config/path-resolution.test.js",
|
||||||
"test:plugin:src": "lua scripts/test-plugin-start-gate.lua && lua scripts/test-plugin-binary-windows.lua",
|
"test:plugin:src": "lua scripts/test-plugin-lua-compat.lua && lua scripts/test-plugin-start-gate.lua && lua scripts/test-plugin-binary-windows.lua",
|
||||||
"test:launcher:smoke:src": "bun test launcher/smoke.e2e.test.ts",
|
"test:launcher:smoke:src": "bun test launcher/smoke.e2e.test.ts",
|
||||||
"test:launcher:src": "bun test launcher/config.test.ts launcher/config-domain-parsers.test.ts launcher/config/cli-parser-builder.test.ts launcher/config/args-normalizer.test.ts launcher/mpv.test.ts launcher/picker.test.ts launcher/parse-args.test.ts launcher/main.test.ts launcher/commands/command-modules.test.ts launcher/smoke.e2e.test.ts && bun run test:plugin:src",
|
"test:launcher:src": "bun test launcher/config.test.ts launcher/config-domain-parsers.test.ts launcher/config/cli-parser-builder.test.ts launcher/config/args-normalizer.test.ts launcher/mpv.test.ts launcher/picker.test.ts launcher/parse-args.test.ts launcher/main.test.ts launcher/commands/command-modules.test.ts launcher/smoke.e2e.test.ts && bun run test:plugin:src",
|
||||||
"test:core:src": "bun test src/cli/args.test.ts src/cli/help.test.ts src/shared/setup-state.test.ts src/core/services/cli-command.test.ts src/core/services/field-grouping-overlay.test.ts src/core/services/numeric-shortcut-session.test.ts src/core/services/secondary-subtitle.test.ts src/core/services/mpv-render-metrics.test.ts src/core/services/overlay-content-measurement.test.ts src/core/services/mpv-control.test.ts src/core/services/mpv.test.ts src/core/services/runtime-options-ipc.test.ts src/core/services/runtime-config.test.ts src/core/services/yomitan-extension-paths.test.ts src/core/services/config-hot-reload.test.ts src/core/services/discord-presence.test.ts src/core/services/tokenizer.test.ts src/core/services/tokenizer/annotation-stage.test.ts src/core/services/tokenizer/parser-selection-stage.test.ts src/core/services/tokenizer/parser-enrichment-stage.test.ts src/core/services/subsync.test.ts src/core/services/overlay-bridge.test.ts src/core/services/overlay-shortcut-handler.test.ts src/core/services/stats-window.test.ts src/core/services/mining.test.ts src/core/services/anki-jimaku.test.ts src/core/services/jimaku-download-path.test.ts src/core/services/jellyfin.test.ts src/core/services/jellyfin-remote.test.ts src/core/services/immersion-tracker-service.test.ts src/core/services/overlay-runtime-init.test.ts src/core/services/app-ready.test.ts src/core/services/startup-bootstrap.test.ts src/core/services/subtitle-processing-controller.test.ts src/core/services/anilist/anilist-update-queue.test.ts src/core/services/anilist/rate-limiter.test.ts src/core/services/jlpt-token-filter.test.ts src/core/services/subtitle-position.test.ts src/core/utils/shortcut-config.test.ts src/main/runtime/first-run-setup-plugin.test.ts src/main/runtime/first-run-setup-service.test.ts src/main/runtime/first-run-setup-window.test.ts src/main/runtime/tray-runtime.test.ts src/main/runtime/tray-main-actions.test.ts src/main/runtime/tray-main-deps.test.ts src/main/runtime/tray-runtime-handlers.test.ts src/main/runtime/cli-command-context-main-deps.test.ts src/main/runtime/app-ready-main-deps.test.ts src/renderer/error-recovery.test.ts src/renderer/subtitle-render.test.ts src/renderer/handlers/mouse.test.ts src/renderer/handlers/keyboard.test.ts src/renderer/modals/jimaku.test.ts src/subsync/utils.test.ts src/main/anilist-url-guard.test.ts src/window-trackers/hyprland-tracker.test.ts src/window-trackers/x11-tracker.test.ts src/window-trackers/windows-helper.test.ts src/window-trackers/windows-tracker.test.ts launcher/config.test.ts launcher/config-domain-parsers.test.ts launcher/config/cli-parser-builder.test.ts launcher/config/args-normalizer.test.ts launcher/parse-args.test.ts launcher/main.test.ts launcher/commands/command-modules.test.ts launcher/setup-gate.test.ts stats/src/lib/api-client.test.ts",
|
"test:core:src": "bun test src/cli/args.test.ts src/cli/help.test.ts src/shared/setup-state.test.ts src/core/services/cli-command.test.ts src/core/services/field-grouping-overlay.test.ts src/core/services/numeric-shortcut-session.test.ts src/core/services/secondary-subtitle.test.ts src/core/services/mpv-render-metrics.test.ts src/core/services/overlay-content-measurement.test.ts src/core/services/mpv-control.test.ts src/core/services/mpv.test.ts src/core/services/runtime-options-ipc.test.ts src/core/services/runtime-config.test.ts src/core/services/yomitan-extension-paths.test.ts src/core/services/config-hot-reload.test.ts src/core/services/discord-presence.test.ts src/core/services/tokenizer.test.ts src/core/services/tokenizer/annotation-stage.test.ts src/core/services/tokenizer/parser-selection-stage.test.ts src/core/services/tokenizer/parser-enrichment-stage.test.ts src/core/services/subsync.test.ts src/core/services/overlay-bridge.test.ts src/core/services/overlay-shortcut-handler.test.ts src/core/services/stats-window.test.ts src/core/services/mining.test.ts src/core/services/anki-jimaku.test.ts src/core/services/jimaku-download-path.test.ts src/core/services/jellyfin.test.ts src/core/services/jellyfin-remote.test.ts src/core/services/immersion-tracker-service.test.ts src/core/services/overlay-runtime-init.test.ts src/core/services/app-ready.test.ts src/core/services/startup-bootstrap.test.ts src/core/services/subtitle-processing-controller.test.ts src/core/services/anilist/anilist-update-queue.test.ts src/core/services/anilist/rate-limiter.test.ts src/core/services/jlpt-token-filter.test.ts src/core/services/subtitle-position.test.ts src/core/utils/shortcut-config.test.ts src/main/runtime/first-run-setup-plugin.test.ts src/main/runtime/first-run-setup-service.test.ts src/main/runtime/first-run-setup-window.test.ts src/main/runtime/tray-runtime.test.ts src/main/runtime/tray-main-actions.test.ts src/main/runtime/tray-main-deps.test.ts src/main/runtime/tray-runtime-handlers.test.ts src/main/runtime/cli-command-context-main-deps.test.ts src/main/runtime/app-ready-main-deps.test.ts src/renderer/error-recovery.test.ts src/renderer/subtitle-render.test.ts src/renderer/handlers/mouse.test.ts src/renderer/handlers/keyboard.test.ts src/renderer/modals/jimaku.test.ts src/subsync/utils.test.ts src/main/anilist-url-guard.test.ts src/window-trackers/hyprland-tracker.test.ts src/window-trackers/x11-tracker.test.ts src/window-trackers/windows-helper.test.ts src/window-trackers/windows-tracker.test.ts launcher/config.test.ts launcher/config-domain-parsers.test.ts launcher/config/cli-parser-builder.test.ts launcher/config/args-normalizer.test.ts launcher/parse-args.test.ts launcher/main.test.ts launcher/commands/command-modules.test.ts launcher/setup-gate.test.ts stats/src/lib/api-client.test.ts",
|
||||||
|
|||||||
@@ -55,23 +55,17 @@ function M.create(ctx)
|
|||||||
if not image then
|
if not image then
|
||||||
image = line:match('^"([^"]+)"')
|
image = line:match('^"([^"]+)"')
|
||||||
end
|
end
|
||||||
if not image then
|
if image then
|
||||||
goto continue
|
|
||||||
end
|
|
||||||
if image == "subminer" or image == "subminer.exe" or image == "subminer.appimage" or image == "subminer.app" then
|
if image == "subminer" or image == "subminer.exe" or image == "subminer.appimage" or image == "subminer.app" then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
if image:find("subminer", 1, true) and not image:find(".lua", 1, true) then
|
if image:find("subminer", 1, true) and not image:find(".lua", 1, true) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
end
|
||||||
else
|
else
|
||||||
local argv0 = line:match('^"([^"]+)"') or line:match("^%s*([^%s]+)")
|
local argv0 = line:match('^"([^"]+)"') or line:match("^%s*([^%s]+)")
|
||||||
if not argv0 then
|
if argv0 and not argv0:find("subminer.lua", 1, true) and not argv0:find("subminer.conf", 1, true) then
|
||||||
goto continue
|
|
||||||
end
|
|
||||||
if argv0:find("subminer.lua", 1, true) or argv0:find("subminer.conf", 1, true) then
|
|
||||||
goto continue
|
|
||||||
end
|
|
||||||
local exe = argv0:match("([^/\\]+)$") or argv0
|
local exe = argv0:match("([^/\\]+)$") or argv0
|
||||||
if exe == "SubMiner" or exe == "SubMiner.AppImage" or exe == "SubMiner.exe" or exe == "subminer" or exe == "subminer.appimage" or exe == "subminer.exe" then
|
if exe == "SubMiner" or exe == "SubMiner.AppImage" or exe == "SubMiner.exe" or exe == "subminer" or exe == "subminer.appimage" or exe == "subminer.exe" then
|
||||||
return true
|
return true
|
||||||
@@ -80,8 +74,7 @@ function M.create(ctx)
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
::continue::
|
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -189,10 +189,7 @@ function M.create(ctx)
|
|||||||
local source_len = #plain
|
local source_len = #plain
|
||||||
local cursor = 1
|
local cursor = 1
|
||||||
for _, token in ipairs(payload.tokens or {}) do
|
for _, token in ipairs(payload.tokens or {}) do
|
||||||
if type(token) ~= "table" or type(token.text) ~= "string" or token.text == "" then
|
if type(token) == "table" and type(token.text) == "string" and token.text ~= "" then
|
||||||
goto continue
|
|
||||||
end
|
|
||||||
|
|
||||||
local token_text = token.text
|
local token_text = token.text
|
||||||
local start_pos = nil
|
local start_pos = nil
|
||||||
local end_pos = nil
|
local end_pos = nil
|
||||||
@@ -222,8 +219,7 @@ function M.create(ctx)
|
|||||||
end
|
end
|
||||||
cursor = end_pos + 1
|
cursor = end_pos + 1
|
||||||
end
|
end
|
||||||
|
end
|
||||||
::continue::
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
141
scripts/test-plugin-lua-compat.lua
Normal file
141
scripts/test-plugin-lua-compat.lua
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
local MODULE_PATHS = {
|
||||||
|
"plugin/subminer/hover.lua",
|
||||||
|
"plugin/subminer/environment.lua",
|
||||||
|
}
|
||||||
|
|
||||||
|
local LEGACY_PARSER_CANDIDATES = {
|
||||||
|
"luajit",
|
||||||
|
"lua5.1",
|
||||||
|
"lua51",
|
||||||
|
}
|
||||||
|
|
||||||
|
local function assert_true(condition, message)
|
||||||
|
if condition then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
error(message or "assert_true failed")
|
||||||
|
end
|
||||||
|
|
||||||
|
local function read_file(path)
|
||||||
|
local file = assert(io.open(path, "r"), "failed to open " .. path)
|
||||||
|
local content = file:read("*a")
|
||||||
|
file:close()
|
||||||
|
return content
|
||||||
|
end
|
||||||
|
|
||||||
|
local function find_legacy_incompatible_continue(source)
|
||||||
|
local goto_start, goto_end = source:find("%f[%a]goto%s+continue%f[%A]")
|
||||||
|
if goto_start then
|
||||||
|
return "goto continue", goto_start, goto_end
|
||||||
|
end
|
||||||
|
|
||||||
|
local label_start, label_end = source:find("::continue::", 1, true)
|
||||||
|
if label_start then
|
||||||
|
return "::continue::", label_start, label_end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function assert_no_legacy_incompatible_continue(path)
|
||||||
|
local source = read_file(path)
|
||||||
|
local match = find_legacy_incompatible_continue(source)
|
||||||
|
assert_true(match == nil, path .. " still contains legacy-incompatible continue control flow: " .. tostring(match))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function assert_loadfile_ok(path)
|
||||||
|
local chunk, err = loadfile(path)
|
||||||
|
assert_true(chunk ~= nil, "loadfile failed for " .. path .. ": " .. tostring(err))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function normalize_execute_result(ok, why, code)
|
||||||
|
if type(ok) == "number" then
|
||||||
|
return ok == 0, ok
|
||||||
|
end
|
||||||
|
if type(ok) == "boolean" then
|
||||||
|
if ok then
|
||||||
|
return true, code or 0
|
||||||
|
end
|
||||||
|
return false, code or 1
|
||||||
|
end
|
||||||
|
return false, code or 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local function command_succeeds(command)
|
||||||
|
local ok, why, code = os.execute(command)
|
||||||
|
return normalize_execute_result(ok, why, code)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function command_exists(command)
|
||||||
|
local shell = package.config:sub(1, 1) == "\\" and "where " or "command -v "
|
||||||
|
local redirect = package.config:sub(1, 1) == "\\" and " >NUL 2>NUL" or " >/dev/null 2>&1"
|
||||||
|
local escaped = command
|
||||||
|
local success = command_succeeds(shell .. escaped .. redirect)
|
||||||
|
return success
|
||||||
|
end
|
||||||
|
|
||||||
|
local function find_legacy_parser()
|
||||||
|
for _, command in ipairs(LEGACY_PARSER_CANDIDATES) do
|
||||||
|
if command_exists(command) then
|
||||||
|
return command
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function shell_redirect()
|
||||||
|
if package.config:sub(1, 1) == "\\" then
|
||||||
|
return " >NUL 2>NUL"
|
||||||
|
end
|
||||||
|
return " >/dev/null 2>&1"
|
||||||
|
end
|
||||||
|
|
||||||
|
local function assert_parser_accepts_file(parser, path)
|
||||||
|
local command = string.format("%s -e %q%s", parser, "assert(loadfile(" .. string.format("%q", path) .. "))", shell_redirect())
|
||||||
|
local success = command_succeeds(command)
|
||||||
|
assert_true(success, parser .. " failed to parse " .. path)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function assert_parser_rejects_legacy_fixture(parser)
|
||||||
|
local legacy_fixture = [[
|
||||||
|
local tokens = {}
|
||||||
|
for _, token in ipairs(tokens or {}) do
|
||||||
|
if type(token) ~= "table" then
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
]]
|
||||||
|
local command = string.format("%s -e %q%s", parser, legacy_fixture, shell_redirect())
|
||||||
|
local success = command_succeeds(command)
|
||||||
|
assert_true(not success, parser .. " unexpectedly accepted legacy goto/label continue fixture")
|
||||||
|
end
|
||||||
|
|
||||||
|
do
|
||||||
|
local legacy_fixture = [[
|
||||||
|
for _, token in ipairs(tokens or {}) do
|
||||||
|
if type(token) ~= "table" then
|
||||||
|
goto continue
|
||||||
|
end
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
]]
|
||||||
|
local match = find_legacy_incompatible_continue(legacy_fixture)
|
||||||
|
assert_true(match ~= nil, "legacy fixture should trigger incompatible continue detector")
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, path in ipairs(MODULE_PATHS) do
|
||||||
|
assert_no_legacy_incompatible_continue(path)
|
||||||
|
assert_loadfile_ok(path)
|
||||||
|
end
|
||||||
|
|
||||||
|
local parser = find_legacy_parser()
|
||||||
|
if parser then
|
||||||
|
assert_parser_rejects_legacy_fixture(parser)
|
||||||
|
for _, path in ipairs(MODULE_PATHS) do
|
||||||
|
assert_parser_accepts_file(parser, path)
|
||||||
|
end
|
||||||
|
print("plugin lua compatibility regression tests: OK (" .. parser .. ")")
|
||||||
|
else
|
||||||
|
print("plugin lua compatibility regression tests: OK (legacy parser unavailable; structural checks only)")
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user