diff --git a/biome.json b/biome.json index 2ee8fb48b..1f48da7bc 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/2.4.6/schema.json", + "$schema": "https://biomejs.dev/schemas/2.4.11/schema.json", "formatter": { "enabled": true, "indentStyle": "tab" diff --git a/package-lock.json b/package-lock.json index 6ad39e852..e1bc9c02e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -88,7 +88,7 @@ "@babel/preset-typescript": "^7.28.5", "@babel/runtime": "^7.28.4", "@babel/runtime-corejs3": "^7.28.4", - "@biomejs/biome": "2.4.6", + "@biomejs/biome": "2.4.11", "@rspack/cli": "^1.7.0", "@rspack/core": "^1.7.0", "@types/ace": "^0.0.52", @@ -1788,9 +1788,9 @@ } }, "node_modules/@biomejs/biome": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.6.tgz", - "integrity": "sha512-QnHe81PMslpy3mnpL8DnO2M4S4ZnYPkjlGCLWBZT/3R9M6b5daArWMMtEfP52/n174RKnwRIf3oT8+wc9ihSfQ==", + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.11.tgz", + "integrity": "sha512-nWxHX8tf3Opb/qRgZpBbsTOqOodkbrkJ7S+JxJAruxOReaDPPmPuLBAGQ8vigyUgo0QBB+oQltNEAvalLcjggA==", "dev": true, "license": "MIT OR Apache-2.0", "bin": { @@ -1804,20 +1804,20 @@ "url": "https://opencollective.com/biome" }, "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.4.6", - "@biomejs/cli-darwin-x64": "2.4.6", - "@biomejs/cli-linux-arm64": "2.4.6", - "@biomejs/cli-linux-arm64-musl": "2.4.6", - "@biomejs/cli-linux-x64": "2.4.6", - "@biomejs/cli-linux-x64-musl": "2.4.6", - "@biomejs/cli-win32-arm64": "2.4.6", - "@biomejs/cli-win32-x64": "2.4.6" + "@biomejs/cli-darwin-arm64": "2.4.11", + "@biomejs/cli-darwin-x64": "2.4.11", + "@biomejs/cli-linux-arm64": "2.4.11", + "@biomejs/cli-linux-arm64-musl": "2.4.11", + "@biomejs/cli-linux-x64": "2.4.11", + "@biomejs/cli-linux-x64-musl": "2.4.11", + "@biomejs/cli-win32-arm64": "2.4.11", + "@biomejs/cli-win32-x64": "2.4.11" } }, "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.6.tgz", - "integrity": "sha512-NW18GSyxr+8sJIqgoGwVp5Zqm4SALH4b4gftIA0n62PTuBs6G2tHlwNAOj0Vq0KKSs7Sf88VjjmHh0O36EnzrQ==", + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.11.tgz", + "integrity": "sha512-wOt+ed+L2dgZanWyL6i29qlXMc088N11optzpo10peayObBaAshbTcxKUchzEMp9QSY8rh5h6VfAFE3WTS1rqg==", "cpu": [ "arm64" ], @@ -1832,9 +1832,9 @@ } }, "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.6.tgz", - "integrity": "sha512-4uiE/9tuI7cnjtY9b07RgS7gGyYOAfIAGeVJWEfeCnAarOAS7qVmuRyX6d7JTKw28/mt+rUzMasYeZ+0R/U1Mw==", + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.11.tgz", + "integrity": "sha512-gZ6zR8XmZlExfi/Pz/PffmdpWOQ8Qhy7oBztgkR8/ylSRyLwfRPSadmiVCV8WQ8PoJ2MWUy2fgID9zmtgUUJmw==", "cpu": [ "x64" ], @@ -1849,9 +1849,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.6.tgz", - "integrity": "sha512-kMLaI7OF5GN1Q8Doymjro1P8rVEoy7BKQALNz6fiR8IC1WKduoNyteBtJlHT7ASIL0Cx2jR6VUOBIbcB1B8pew==", + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.11.tgz", + "integrity": "sha512-avdJaEElXrKceK0va9FkJ4P5ci3N01TGkc6ni3P8l3BElqbOz42Wg2IyX3gbh0ZLEd4HVKEIrmuVu/AMuSeFFA==", "cpu": [ "arm64" ], @@ -1866,9 +1866,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.6.tgz", - "integrity": "sha512-F/JdB7eN22txiTqHM5KhIVt0jVkzZwVYrdTR1O3Y4auBOQcXxHK4dxULf4z43QyZI5tsnQJrRBHZy7wwtL+B3A==", + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.11.tgz", + "integrity": "sha512-+Sbo1OAmlegtdwqFE8iOxFIWLh1B3OEgsuZfBpyyN/kWuqZ8dx9ZEes6zVnDMo+zRHF2wLynRVhoQmV7ohxl2Q==", "cpu": [ "arm64" ], @@ -1883,9 +1883,9 @@ } }, "node_modules/@biomejs/cli-linux-x64": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.6.tgz", - "integrity": "sha512-oHXmUFEoH8Lql1xfc3QkFLiC1hGR7qedv5eKNlC185or+o4/4HiaU7vYODAH3peRCfsuLr1g6v2fK9dFFOYdyw==", + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.11.tgz", + "integrity": "sha512-TagWV0iomp5LnEnxWFg4nQO+e52Fow349vaX0Q/PIcX6Zhk4GGBgp3qqZ8PVkpC+cuehRctMf3+6+FgQ8jCEFQ==", "cpu": [ "x64" ], @@ -1900,9 +1900,9 @@ } }, "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.6.tgz", - "integrity": "sha512-C9s98IPDu7DYarjlZNuzJKTjVHN03RUnmHV5htvqsx6vEUXCDSJ59DNwjKVD5XYoSS4N+BYhq3RTBAL8X6svEg==", + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.11.tgz", + "integrity": "sha512-bexd2IklK7ZgPhrz6jXzpIL6dEAH9MlJU1xGTrypx+FICxrXUp4CqtwfiuoDKse+UlgAlWtzML3jrMqeEAHEhA==", "cpu": [ "x64" ], @@ -1917,9 +1917,9 @@ } }, "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.6.tgz", - "integrity": "sha512-xzThn87Pf3YrOGTEODFGONmqXpTwUNxovQb72iaUOdcw8sBSY3+3WD8Hm9IhMYLnPi0n32s3L3NWU6+eSjfqFg==", + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.11.tgz", + "integrity": "sha512-RJhaTnY8byzxDt4bDVb7AFPHkPcjOPK3xBip4ZRTrN3TEfyhjLRm3r3mqknqydgVTB74XG8l4jMLwEACEeihVg==", "cpu": [ "arm64" ], @@ -1934,9 +1934,9 @@ } }, "node_modules/@biomejs/cli-win32-x64": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.6.tgz", - "integrity": "sha512-7++XhnsPlr1HDbor5amovPjOH6vsrFOCdp93iKXhFn6bcMUI6soodj3WWKfgEO6JosKU1W5n3uky3WW9RlRjTg==", + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.11.tgz", + "integrity": "sha512-A8D3JM/00C2KQgUV3oj8Ba15EHEYwebAGCy5Sf9GAjr5Y3+kJIYOiESoqRDeuRZueuMdCsbLZIUqmPhpYXJE9A==", "cpu": [ "x64" ], diff --git a/package.json b/package.json index 1cdadb41a..36540e151 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "@babel/preset-typescript": "^7.28.5", "@babel/runtime": "^7.28.4", "@babel/runtime-corejs3": "^7.28.4", - "@biomejs/biome": "2.4.6", + "@biomejs/biome": "2.4.11", "@rspack/cli": "^1.7.0", "@rspack/core": "^1.7.0", "@types/ace": "^0.0.52", diff --git a/src/cm/lsp/codeActions.ts b/src/cm/lsp/codeActions.ts index 0eb6e1c97..7f5905761 100644 --- a/src/cm/lsp/codeActions.ts +++ b/src/cm/lsp/codeActions.ts @@ -415,4 +415,4 @@ export async function performQuickFix(view: EditorView): Promise { return showCodeActionsMenu(view); } -export { CODE_ACTION_KINDS, getCodeActionIcon, formatCodeActionKind }; +export { CODE_ACTION_KINDS, formatCodeActionKind, getCodeActionIcon }; diff --git a/src/cm/lsp/types.ts b/src/cm/lsp/types.ts index 26c4a7c47..7802be5f8 100644 --- a/src/cm/lsp/types.ts +++ b/src/cm/lsp/types.ts @@ -21,14 +21,14 @@ export type { LSPClient, LSPClientConfig, LSPClientExtension, + LSPDiagnostic, + LSPFormattingOptions, + Position, + Range, + TextEdit, Transport, Workspace, WorkspaceFile, - TextEdit, - LSPFormattingOptions, - LSPDiagnostic, - Range, - Position, }; export interface WorkspaceFileUpdate { diff --git a/src/components/lspInfoDialog/index.js b/src/components/lspInfoDialog/index.js index 5b35ab220..5bea3db08 100644 --- a/src/components/lspInfoDialog/index.js +++ b/src/components/lspInfoDialog/index.js @@ -704,5 +704,5 @@ function hasConnectedServers() { return relevantServers.length > 0; } -export { showLspInfoDialog, hasConnectedServers, addLspLog, getLspLogs }; +export { addLspLog, getLspLogs, hasConnectedServers, showLspInfoDialog }; export default showLspInfoDialog; diff --git a/src/components/terminal/index.js b/src/components/terminal/index.js index ef2372fa1..847cc8209 100644 --- a/src/components/terminal/index.js +++ b/src/components/terminal/index.js @@ -8,10 +8,10 @@ import TerminalManager from "./terminalManager"; import TerminalThemeManager from "./terminalThemeManager"; export { + DEFAULT_TERMINAL_SETTINGS, TerminalComponent, TerminalManager, TerminalThemeManager, - DEFAULT_TERMINAL_SETTINGS, }; export default { diff --git a/src/test/tester.js b/src/test/tester.js index fbfc16307..be3ba8ac6 100644 --- a/src/test/tester.js +++ b/src/test/tester.js @@ -2,6 +2,7 @@ import { runAceCompatibilityTests } from "./ace.test"; import { runCodeMirrorTests } from "./editor.tests"; import { runExecutorTests } from "./exec.tests"; import { runSanityTests } from "./sanity.tests"; +import { runUrlTests } from "./url.tests"; export async function runAllTests() { const terminal = acode.require("terminal"); @@ -20,6 +21,7 @@ export async function runAllTests() { await runCodeMirrorTests(write); await runAceCompatibilityTests(write); await runExecutorTests(write); + await runUrlTests(write); write("\x1b[36m\x1b[1mTests completed!\x1b[0m\n"); } catch (error) { diff --git a/src/test/url.tests.js b/src/test/url.tests.js new file mode 100644 index 000000000..49a2ad9a5 --- /dev/null +++ b/src/test/url.tests.js @@ -0,0 +1,96 @@ +import Url from "../utils/Url"; +import { TestRunner } from "./tester"; + +const JOIN_CASES = [ + { + name: "Android SAF join", + folderUrl: + "content://com.android.externalstorage.documents/tree/primary%3ATesthtml", + activeLocation: + "content://com.android.externalstorage.documents/tree/primary%3ATesthtml::primary:Testhtml/Styles/", + expectedJoined: + "content://com.android.externalstorage.documents/tree/primary%3ATesthtml::primary:Testhtml/Styles/index.html", + }, + { + name: "Termux SAF join", + folderUrl: + "content://com.termux.documents/tree/%2Fdata%2Fdata%2Fcom.termux%2Ffiles%2Fhome%2Facode-site-ui", + activeLocation: + "content://com.termux.documents/tree/%2Fdata%2Fdata%2Fcom.termux%2Ffiles%2Fhome%2Facode-site-ui::/data/data/com.termux/files/home/acode-site-ui/", + expectedJoined: + "content://com.termux.documents/tree/%2Fdata%2Fdata%2Fcom.termux%2Ffiles%2Fhome%2Facode-site-ui::/data/data/com.termux/files/home/acode-site-ui/index.html", + }, + { + name: "Acode SAF join", + folderUrl: + "content://com.foxdebug.acode.documents/tree/%2Fdata%2Fuser%2F0%2Fcom.foxdebug.acode%2Ffiles%2Fpublic", + activeLocation: + "content://com.foxdebug.acode.documents/tree/%2Fdata%2Fuser%2F0%2Fcom.foxdebug.acode%2Ffiles%2Fpublic::/data/user/0/com.foxdebug.acode/files/public/", + expectedJoined: + "content://com.foxdebug.acode.documents/tree/%2Fdata%2Fuser%2F0%2Fcom.foxdebug.acode%2Ffiles%2Fpublic::/data/user/0/com.foxdebug.acode/files/public/index.html", + }, +]; + +const TRAILING_SLASH_CASES = [ + { + name: "Android SAF trailing slash", + a: "content://com.android.externalstorage.documents/tree/primary%3ATesthtml/", + b: "content://com.android.externalstorage.documents/tree/primary%3ATesthtml", + }, + { + name: "Termux SAF trailing slash", + a: "content://com.termux.documents/tree/%2Fdata%2Fdata%2Fcom.termux%2Ffiles%2Fhome%2Facode-site-ui/", + b: "content://com.termux.documents/tree/%2Fdata%2Fdata%2Fcom.termux%2Ffiles%2Fhome%2Facode-site-ui", + }, + { + name: "Acode SAF trailing slash", + a: "content://com.foxdebug.acode.documents/tree/%2Fdata%2Fuser%2F0%2Fcom.foxdebug.acode%2Ffiles%2Fpublic/", + b: "content://com.foxdebug.acode.documents/tree/%2Fdata%2Fuser%2F0%2Fcom.foxdebug.acode%2Ffiles%2Fpublic", + }, +]; + +function assertJoinCase( + test, + { folderUrl, activeLocation, expectedJoined, segment }, +) { + const joined = Url.join(activeLocation, segment || "index.html"); + + test.assert(joined !== null, "Joining the SAF URL should return a value"); + test.assertEqual( + joined, + expectedJoined, + "Joined URL should match the expected SAF file URI", + ); + test.assert( + !Url.areSame(folderUrl, joined), + "Folder URL and joined file URL should not be considered the same", + ); +} + +export async function runUrlTests(writeOutput) { + const runner = new TestRunner("URL / SAF URIs"); + + for (const joinCase of JOIN_CASES) { + runner.test(joinCase.name, (test) => { + assertJoinCase(test, joinCase); + }); + } + + for (const trailingSlashCase of TRAILING_SLASH_CASES) { + runner.test(trailingSlashCase.name, (test) => { + test.assert( + Url.areSame(trailingSlashCase.a, trailingSlashCase.b), + "Folder URLs differing only by a trailing slash should be same", + ); + }); + } + + runner.test("Android SAF leading slash", (test) => { + assertJoinCase(test, { + ...JOIN_CASES[0], + segment: "/index.html", + }); + }); + + return await runner.run(writeOutput); +} diff --git a/src/utils/Url.js b/src/utils/Url.js index 34bca3043..948503129 100644 --- a/src/utils/Url.js +++ b/src/utils/Url.js @@ -76,7 +76,7 @@ export default { if (pathnames[1].startsWith("/")) pathnames[1] = pathnames[1].slice(1); const contentUri = Uri.parse(url); let [root, pathname] = contentUri.docId.split(":"); - const newDocId = path.join(pathname, ...pathnames.slice(1)); + let newDocId = path.join(pathname, ...pathnames.slice(1)); if (/^content:\/\/com.termux/.test(url)) { const rootCondition = root.endsWith("/"); const newDocIdCondition = newDocId.startsWith("/"); @@ -87,6 +87,21 @@ export default { } return `${contentUri.rootUri}::${root}${newDocId}${query}`; } + + // if pathname is undefined, meaning a docId/volume (e.g :primary:) + // has not been detected, so no newDocId's ":" will be added. + if (!pathname) { + // Ensure proper path separator between root and newDocId + let separator = ""; + if (root.endsWith("/") && newDocId.startsWith("/")) { + // Both have separator, strip one from newDocId + newDocId = newDocId.slice(1); + } else if (!root.endsWith("/") && !newDocId.startsWith("/")) { + // Neither has separator, add one + separator = "/"; + } + return `${contentUri.rootUri}::${root}${separator}${newDocId}${query}`; + } return `${contentUri.rootUri}::${root}:${newDocId}${query}`; } catch (error) { return null;