Skip to content

Commit 0006340

Browse files
UnschooledGamergreptile-apps[bot]bajrangCoder
authored
fix: handle undefined pathname in URL builder (Acode-Foundation#2021)
* fix: handle undefined pathname in URL builder Return early when pathname is undefined to avoid inserting an extra ':' and strip a leading '/' from newDocId before concatenation * Update src/utils/Url.js Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * Update Url.js * fix(Url): root/docId separator in Url util Move pathname handling into the correct branch and ensure a ':' is inserted between root and newDocId when appropriate * feat: URL tests and include them in test runner * chore: reorder imports in url.test.js * chore: Bump @biomejs/biome to ^2.4.11 * Update biome.json * fix * fix * chore: update biome * fix: tests --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Co-authored-by: Raunak Raj <71929976+bajrangCoder@users.noreply.github.com>
1 parent 9adbf23 commit 0006340

File tree

10 files changed

+160
-47
lines changed

10 files changed

+160
-47
lines changed

biome.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"$schema": "https://biomejs.dev/schemas/2.4.6/schema.json",
2+
"$schema": "https://biomejs.dev/schemas/2.4.11/schema.json",
33
"formatter": {
44
"enabled": true,
55
"indentStyle": "tab"

package-lock.json

Lines changed: 36 additions & 36 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
"@babel/preset-typescript": "^7.28.5",
6464
"@babel/runtime": "^7.28.4",
6565
"@babel/runtime-corejs3": "^7.28.4",
66-
"@biomejs/biome": "2.4.6",
66+
"@biomejs/biome": "2.4.11",
6767
"@rspack/cli": "^1.7.0",
6868
"@rspack/core": "^1.7.0",
6969
"@types/ace": "^0.0.52",

src/cm/lsp/codeActions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,4 +415,4 @@ export async function performQuickFix(view: EditorView): Promise<boolean> {
415415
return showCodeActionsMenu(view);
416416
}
417417

418-
export { CODE_ACTION_KINDS, getCodeActionIcon, formatCodeActionKind };
418+
export { CODE_ACTION_KINDS, formatCodeActionKind, getCodeActionIcon };

src/cm/lsp/types.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ export type {
2121
LSPClient,
2222
LSPClientConfig,
2323
LSPClientExtension,
24+
LSPDiagnostic,
25+
LSPFormattingOptions,
26+
Position,
27+
Range,
28+
TextEdit,
2429
Transport,
2530
Workspace,
2631
WorkspaceFile,
27-
TextEdit,
28-
LSPFormattingOptions,
29-
LSPDiagnostic,
30-
Range,
31-
Position,
3232
};
3333

3434
export interface WorkspaceFileUpdate {

src/components/lspInfoDialog/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -704,5 +704,5 @@ function hasConnectedServers() {
704704
return relevantServers.length > 0;
705705
}
706706

707-
export { showLspInfoDialog, hasConnectedServers, addLspLog, getLspLogs };
707+
export { addLspLog, getLspLogs, hasConnectedServers, showLspInfoDialog };
708708
export default showLspInfoDialog;

src/components/terminal/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import TerminalManager from "./terminalManager";
88
import TerminalThemeManager from "./terminalThemeManager";
99

1010
export {
11+
DEFAULT_TERMINAL_SETTINGS,
1112
TerminalComponent,
1213
TerminalManager,
1314
TerminalThemeManager,
14-
DEFAULT_TERMINAL_SETTINGS,
1515
};
1616

1717
export default {

src/test/tester.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { runAceCompatibilityTests } from "./ace.test";
22
import { runCodeMirrorTests } from "./editor.tests";
33
import { runExecutorTests } from "./exec.tests";
44
import { runSanityTests } from "./sanity.tests";
5+
import { runUrlTests } from "./url.tests";
56

67
export async function runAllTests() {
78
const terminal = acode.require("terminal");
@@ -20,6 +21,7 @@ export async function runAllTests() {
2021
await runCodeMirrorTests(write);
2122
await runAceCompatibilityTests(write);
2223
await runExecutorTests(write);
24+
await runUrlTests(write);
2325

2426
write("\x1b[36m\x1b[1mTests completed!\x1b[0m\n");
2527
} catch (error) {

src/test/url.tests.js

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import Url from "../utils/Url";
2+
import { TestRunner } from "./tester";
3+
4+
const JOIN_CASES = [
5+
{
6+
name: "Android SAF join",
7+
folderUrl:
8+
"content://com.android.externalstorage.documents/tree/primary%3ATesthtml",
9+
activeLocation:
10+
"content://com.android.externalstorage.documents/tree/primary%3ATesthtml::primary:Testhtml/Styles/",
11+
expectedJoined:
12+
"content://com.android.externalstorage.documents/tree/primary%3ATesthtml::primary:Testhtml/Styles/index.html",
13+
},
14+
{
15+
name: "Termux SAF join",
16+
folderUrl:
17+
"content://com.termux.documents/tree/%2Fdata%2Fdata%2Fcom.termux%2Ffiles%2Fhome%2Facode-site-ui",
18+
activeLocation:
19+
"content://com.termux.documents/tree/%2Fdata%2Fdata%2Fcom.termux%2Ffiles%2Fhome%2Facode-site-ui::/data/data/com.termux/files/home/acode-site-ui/",
20+
expectedJoined:
21+
"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",
22+
},
23+
{
24+
name: "Acode SAF join",
25+
folderUrl:
26+
"content://com.foxdebug.acode.documents/tree/%2Fdata%2Fuser%2F0%2Fcom.foxdebug.acode%2Ffiles%2Fpublic",
27+
activeLocation:
28+
"content://com.foxdebug.acode.documents/tree/%2Fdata%2Fuser%2F0%2Fcom.foxdebug.acode%2Ffiles%2Fpublic::/data/user/0/com.foxdebug.acode/files/public/",
29+
expectedJoined:
30+
"content://com.foxdebug.acode.documents/tree/%2Fdata%2Fuser%2F0%2Fcom.foxdebug.acode%2Ffiles%2Fpublic::/data/user/0/com.foxdebug.acode/files/public/index.html",
31+
},
32+
];
33+
34+
const TRAILING_SLASH_CASES = [
35+
{
36+
name: "Android SAF trailing slash",
37+
a: "content://com.android.externalstorage.documents/tree/primary%3ATesthtml/",
38+
b: "content://com.android.externalstorage.documents/tree/primary%3ATesthtml",
39+
},
40+
{
41+
name: "Termux SAF trailing slash",
42+
a: "content://com.termux.documents/tree/%2Fdata%2Fdata%2Fcom.termux%2Ffiles%2Fhome%2Facode-site-ui/",
43+
b: "content://com.termux.documents/tree/%2Fdata%2Fdata%2Fcom.termux%2Ffiles%2Fhome%2Facode-site-ui",
44+
},
45+
{
46+
name: "Acode SAF trailing slash",
47+
a: "content://com.foxdebug.acode.documents/tree/%2Fdata%2Fuser%2F0%2Fcom.foxdebug.acode%2Ffiles%2Fpublic/",
48+
b: "content://com.foxdebug.acode.documents/tree/%2Fdata%2Fuser%2F0%2Fcom.foxdebug.acode%2Ffiles%2Fpublic",
49+
},
50+
];
51+
52+
function assertJoinCase(
53+
test,
54+
{ folderUrl, activeLocation, expectedJoined, segment },
55+
) {
56+
const joined = Url.join(activeLocation, segment || "index.html");
57+
58+
test.assert(joined !== null, "Joining the SAF URL should return a value");
59+
test.assertEqual(
60+
joined,
61+
expectedJoined,
62+
"Joined URL should match the expected SAF file URI",
63+
);
64+
test.assert(
65+
!Url.areSame(folderUrl, joined),
66+
"Folder URL and joined file URL should not be considered the same",
67+
);
68+
}
69+
70+
export async function runUrlTests(writeOutput) {
71+
const runner = new TestRunner("URL / SAF URIs");
72+
73+
for (const joinCase of JOIN_CASES) {
74+
runner.test(joinCase.name, (test) => {
75+
assertJoinCase(test, joinCase);
76+
});
77+
}
78+
79+
for (const trailingSlashCase of TRAILING_SLASH_CASES) {
80+
runner.test(trailingSlashCase.name, (test) => {
81+
test.assert(
82+
Url.areSame(trailingSlashCase.a, trailingSlashCase.b),
83+
"Folder URLs differing only by a trailing slash should be same",
84+
);
85+
});
86+
}
87+
88+
runner.test("Android SAF leading slash", (test) => {
89+
assertJoinCase(test, {
90+
...JOIN_CASES[0],
91+
segment: "/index.html",
92+
});
93+
});
94+
95+
return await runner.run(writeOutput);
96+
}

src/utils/Url.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export default {
7676
if (pathnames[1].startsWith("/")) pathnames[1] = pathnames[1].slice(1);
7777
const contentUri = Uri.parse(url);
7878
let [root, pathname] = contentUri.docId.split(":");
79-
const newDocId = path.join(pathname, ...pathnames.slice(1));
79+
let newDocId = path.join(pathname, ...pathnames.slice(1));
8080
if (/^content:\/\/com.termux/.test(url)) {
8181
const rootCondition = root.endsWith("/");
8282
const newDocIdCondition = newDocId.startsWith("/");
@@ -87,6 +87,21 @@ export default {
8787
}
8888
return `${contentUri.rootUri}::${root}${newDocId}${query}`;
8989
}
90+
91+
// if pathname is undefined, meaning a docId/volume (e.g :primary:)
92+
// has not been detected, so no newDocId's ":" will be added.
93+
if (!pathname) {
94+
// Ensure proper path separator between root and newDocId
95+
let separator = "";
96+
if (root.endsWith("/") && newDocId.startsWith("/")) {
97+
// Both have separator, strip one from newDocId
98+
newDocId = newDocId.slice(1);
99+
} else if (!root.endsWith("/") && !newDocId.startsWith("/")) {
100+
// Neither has separator, add one
101+
separator = "/";
102+
}
103+
return `${contentUri.rootUri}::${root}${separator}${newDocId}${query}`;
104+
}
90105
return `${contentUri.rootUri}::${root}:${newDocId}${query}`;
91106
} catch (error) {
92107
return null;

0 commit comments

Comments
 (0)