summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src/context/file/path.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/app/src/context/file/path.ts')
-rw-r--r--packages/app/src/context/file/path.ts119
1 files changed, 119 insertions, 0 deletions
diff --git a/packages/app/src/context/file/path.ts b/packages/app/src/context/file/path.ts
new file mode 100644
index 000000000..ced30d0fd
--- /dev/null
+++ b/packages/app/src/context/file/path.ts
@@ -0,0 +1,119 @@
+export function stripFileProtocol(input: string) {
+ if (!input.startsWith("file://")) return input
+ return input.slice("file://".length)
+}
+
+export function stripQueryAndHash(input: string) {
+ const hashIndex = input.indexOf("#")
+ const queryIndex = input.indexOf("?")
+
+ if (hashIndex !== -1 && queryIndex !== -1) {
+ return input.slice(0, Math.min(hashIndex, queryIndex))
+ }
+
+ if (hashIndex !== -1) return input.slice(0, hashIndex)
+ if (queryIndex !== -1) return input.slice(0, queryIndex)
+ return input
+}
+
+export function unquoteGitPath(input: string) {
+ if (!input.startsWith('"')) return input
+ if (!input.endsWith('"')) return input
+ const body = input.slice(1, -1)
+ const bytes: number[] = []
+
+ for (let i = 0; i < body.length; i++) {
+ const char = body[i]!
+ if (char !== "\\") {
+ bytes.push(char.charCodeAt(0))
+ continue
+ }
+
+ const next = body[i + 1]
+ if (!next) {
+ bytes.push("\\".charCodeAt(0))
+ continue
+ }
+
+ if (next >= "0" && next <= "7") {
+ const chunk = body.slice(i + 1, i + 4)
+ const match = chunk.match(/^[0-7]{1,3}/)
+ if (!match) {
+ bytes.push(next.charCodeAt(0))
+ i++
+ continue
+ }
+ bytes.push(parseInt(match[0], 8))
+ i += match[0].length
+ continue
+ }
+
+ const escaped =
+ next === "n"
+ ? "\n"
+ : next === "r"
+ ? "\r"
+ : next === "t"
+ ? "\t"
+ : next === "b"
+ ? "\b"
+ : next === "f"
+ ? "\f"
+ : next === "v"
+ ? "\v"
+ : next === "\\" || next === '"'
+ ? next
+ : undefined
+
+ bytes.push((escaped ?? next).charCodeAt(0))
+ i++
+ }
+
+ return new TextDecoder().decode(new Uint8Array(bytes))
+}
+
+export function createPathHelpers(scope: () => string) {
+ const normalize = (input: string) => {
+ const root = scope()
+ const prefix = root.endsWith("/") ? root : root + "/"
+
+ let path = unquoteGitPath(stripQueryAndHash(stripFileProtocol(input)))
+
+ if (path.startsWith(prefix)) {
+ path = path.slice(prefix.length)
+ }
+
+ if (path.startsWith(root)) {
+ path = path.slice(root.length)
+ }
+
+ if (path.startsWith("./")) {
+ path = path.slice(2)
+ }
+
+ if (path.startsWith("/")) {
+ path = path.slice(1)
+ }
+
+ return path
+ }
+
+ const tab = (input: string) => {
+ const path = normalize(input)
+ return `file://${path}`
+ }
+
+ const pathFromTab = (tabValue: string) => {
+ if (!tabValue.startsWith("file://")) return
+ return normalize(tabValue)
+ }
+
+ const normalizeDir = (input: string) => normalize(input).replace(/\/+$/, "")
+
+ return {
+ normalize,
+ tab,
+ pathFromTab,
+ normalizeDir,
+ }
+}