diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 5b601de..da9d3fa 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -17,15 +17,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - with: - ref: ${{ github.head_ref }} - token: ${{ secrets.GITHUB_TOKEN }} - repository: ${{ github.repository }} - - name: Set Git origin - run: | - git config --global user.name "github-actions" - git config --global user.email "github-actions@github.com" - git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }} - uses: denoland/setup-deno@v2 with: deno-version: v2.x @@ -34,9 +25,4 @@ jobs: run: | deno install --allow-scripts deno lint --fix - deno fmt - - name: Push formatting changes (if any) - run: | - git diff --quiet || ( - git commit -a -m "style: code formatting" && - git push) + deno fmt --check diff --git a/convert.ts b/convert.ts index 0a481fb..41b7bb5 100644 --- a/convert.ts +++ b/convert.ts @@ -132,7 +132,7 @@ export function pathObjectFrom( let root: PathRecord = {}; for (const element of iterable) { - let [next, other, isLast] = shiftPath(element); + let { next, other, isLast } = shiftPath(element); let parent: PathRecord = root; if (isLast) { @@ -145,7 +145,12 @@ export function pathObjectFrom( while (!isLast || (parent[next] = "")) { parent = child as { [key: string]: PathRecord }; - [next, other, isLast] = shiftPath(other); + const { + next: nextNew, + other: otherNew, + isLast: isLastNew, + } = shiftPath(other); + [next, other, isLast] = [nextNew, otherNew, isLastNew]; child = parent[next] ?? {}; parent[next] = child; } diff --git a/shift.ts b/shift.ts index 3e14f57..1c58737 100644 --- a/shift.ts +++ b/shift.ts @@ -1,3 +1,9 @@ +export type ShiftResult = { + next: string; + other: string; + isLast: boolean; +}; + /** * @example * "path/to/the/file" -> ["path", "to/the/file", false] @@ -6,10 +12,17 @@ */ export function shiftPath( p: string, -): [next: string, other: string, isLast: boolean] { +): ShiftResult { const slashIndex = p.search(/[/\\]/); const next = p.slice(0, Math.max(0, slashIndex)); const other = p.slice(Math.max(0, slashIndex + 1)); - const isLast = next === ""; - return [slashIndex < 0 ? other : next, other, isLast]; + const r: ShiftResult = { + next: next, + other: other, + isLast: next == "", + }; + if (slashIndex < 0) { + r.next = r.other; + } + return r; } diff --git a/sort-cmp.ts b/sort-cmp.ts index ff941f8..a0450b2 100644 --- a/sort-cmp.ts +++ b/sort-cmp.ts @@ -45,24 +45,21 @@ export function isSortName(value: unknown): value is SortName { * Folders are displayed before files. */ export function cmpFirstFolders(a: string, b: string): number { + if (a === b) return 0; let comp = 0; - for (; comp === 0;) { - const [next1, post1, last1] = shiftPath(a); + while (comp === 0) { + const { next: next1, other: post1, isLast: last1 } = shiftPath(a); a = post1; - const [next2, post2, last2] = shiftPath(b); + const { next: next2, other: post2, isLast: last2 } = shiftPath(b); b = post2; - comp = cmpMixed(next1, next2); - if (last1 || last2) { - if (last1 === last2) { - break; - } - if (!last1) { - return -1; - } + comp = cmpMixed(next1, next2); - return +1; - } + if (comp) break; + if (!last1 && !last2) continue; + if (last1 && last2) break; + if (!last1) return -1; + return +1; } return comp; @@ -73,24 +70,21 @@ export function cmpFirstFolders(a: string, b: string): number { * Files are displayed before folders. */ export function cmpFirstFiles(a: string, b: string): number { + if (a === b) return 0; let comp = 0; - for (; comp === 0;) { - const [next1, post1, last1] = shiftPath(a); + while (comp === 0) { + const { next: next1, other: post1, isLast: last1 } = shiftPath(a); a = post1; - const [next2, post2, last2] = shiftPath(b); + const { next: next2, other: post2, isLast: last2 } = shiftPath(b); b = post2; - comp = cmpMixed(next1, next2); - if (last1 || last2) { - if (last1 === last2) { - break; - } - if (last1) { - return -1; - } + comp = cmpMixed(next1, next2); - return +1; - } + if (comp) break; + if (!last1 && !last2) continue; + if (last1 && last2) break; + if (last1) return -1; + return +1; } return comp; @@ -106,22 +100,23 @@ export function cmpModified( timea: number = 0, timeb: number = 0, ): number { - if (a === b) { - return 0; - } - - while (true) { - const [next1, post1, last1] = shiftPath(a); + let comp = 0; + while (comp === 0) { + const { next: next1, other: post1, isLast: last1 } = shiftPath(a); a = post1; - const [next2, post2, last2] = shiftPath(b); + const { next: next2, other: post2, isLast: last2 } = shiftPath(b); b = post2; - let comp = (timeb - timea) || cmpFirstFolders(next1, next2); - if (comp) return comp; + comp = timeb - timea || cmpMixed(next1, next2); - if (last1) return +1; - if (last2) return -1; + if (comp) break; + if (!last1 && !last2) continue; + if (last1 && last2) break; + if (!last1) return -1; + return +1; } + + return comp || cmpFirstFolders(a, b); } /** @@ -129,26 +124,23 @@ export function cmpModified( * Folders are displayed before files. */ export function cmpFileType(a: string, b: string): number { + if (a === b) return 0; let comp = 0; - for (; comp === 0;) { - const [next1, post1, last1] = shiftPath(a); + while (comp === 0) { + const { next: next1, other: post1, isLast: last1 } = shiftPath(a); a = post1; - const [next2, post2, last2] = shiftPath(b); + const { next: next2, other: post2, isLast: last2 } = shiftPath(b); b = post2; + const ppa = path.parse(next1); const ppb = path.parse(next2); comp = cmpMixed(ppa.ext, ppb.ext) || cmpMixed(ppa.name, ppb.name); - if (last1 || last2) { - if (last1 === last2) { - break; - } - - if (!last1) { - return -1; - } - return +1; - } + if (comp) break; + if (!last1 && !last2) continue; + if (last1 && last2) break; + if (!last1) return -1; + return +1; } return comp; @@ -159,5 +151,6 @@ export function cmpFileType(a: string, b: string): number { * Files are interwoven with folders. */ export function cmpMixed(a: string, b: string): number { + if (a === b) return 0; return a.localeCompare(b, undefined, { ignorePunctuation: false }); } diff --git a/sort.test.ts b/sort.test.ts index 98d19a9..e755aea 100644 --- a/sort.test.ts +++ b/sort.test.ts @@ -8,9 +8,17 @@ import * as sort from "./sort.ts"; import { shiftPath } from "./shift.ts"; Deno.test("shiftPath - examples", () => { - assertEquals(shiftPath("path/to/the/file"), ["path", "to/the/file", false]); - assertEquals(shiftPath("file"), ["file", "file", true]); - assertEquals(shiftPath("file/"), ["file", "", false]); + assertEquals(shiftPath("path/to/the/file"), { + next: "path", + other: "to/the/file", + isLast: false, + }); + assertEquals(shiftPath("file"), { + next: "file", + other: "file", + isLast: true, + }); + assertEquals(shiftPath("file/"), { next: "file", other: "", isLast: false }); }); Deno.test("sort.isSortName - true/false", () => { @@ -19,30 +27,36 @@ Deno.test("sort.isSortName - true/false", () => { }); Deno.test("mixed comparator orders strings", () => { - const a = sort.cmpMixed("a", "b"); - const b = sort.cmpMixed("b", "a"); - assertLess(a, 0); - assertGreater(b, 0); + assertLess(sort.cmpMixed("a", "b"), 0); + assertGreater(sort.cmpMixed("b", "a"), 0); assertEquals(sort.cmpMixed("same", "same"), 0); }); Deno.test("type comparator groups by extension then name", () => { - const cmp = sort.cmpFileType("file.md", "file.txt"); - assertLess(cmp, 0); - const cmp2 = sort.cmpFileType("a.txt", "b.txt"); - assertLess(cmp2, 0); + assertLess(sort.cmpFileType("file.md", "file.txt"), 0); + assertLess(sort.cmpFileType("a.txt", "b.txt"), 0); }); Deno.test("sort.cmpFirstFolders puts folders before files", () => { - const arr = ["dir", "dir/file"]; - arr.sort(sort.cmpFirstFolders); - assertEquals(arr, ["dir/file", "dir"]); + assertEquals(["dir", "dir/file"].sort(sort.cmpFirstFolders), [ + "dir/file", + "dir", + ]); + assertEquals( + ["src/targets/yarn.ts", "src/...+16"].sort(sort.cmpFirstFolders), + ["src/...+16", "src/targets/yarn.ts"], + ); }); Deno.test("sort.cmpFirstFiles puts files before folders", () => { - const arr = ["dir", "dir/file"]; - arr.sort(sort.cmpFirstFiles); - assertEquals(arr, ["dir", "dir/file"]); + assertEquals(["dir", "dir/file"].sort(sort.cmpFirstFiles), [ + "dir", + "dir/file", + ]); + assertEquals(["src/targets/yarn.ts", "src/...+16"].sort(sort.cmpFirstFiles), [ + "src/...+16", + "src/targets/yarn.ts", + ]); }); Deno.test("sort.isSortName - non-string returns false", () => { @@ -67,8 +81,8 @@ Deno.test("modified comparator", () => { assertGreater(sort.cmpModified("a/file", "b/file", 100, 200), 0); // File vs folder (folders first) - assertLess(sort.cmpModified("a/file", "a/folder", 200, 100), 1); - assertGreater(sort.cmpModified("a/folder", "a/file", 100, 200), -1); + assertLess(sort.cmpModified("a/file", "a/folder", 200, 100), 0); + assertGreater(sort.cmpModified("a/folder", "a/file", 100, 200), 0); // Folder vs folder, different dates assertLess(sort.cmpModified("a/folder1", "a/folder2", 200, 100), 0); @@ -109,27 +123,23 @@ Deno.test("sort.cmpFirstFolders/sort.cmpFirstFiles equal names return 0", () => }); Deno.test("sort-iterable: sortFirstFolders basic", () => { - const input = ["dir", "dir/file"]; - const out = sort.sortFirstFolders(input); - assertEquals(out, ["dir/file", "dir"]); + assertEquals(sort.sortFirstFolders(["dir", "dir/file"]), ["dir/file", "dir"]); }); Deno.test("sort-iterable: sortsort.cmpFirstFiles basic", () => { - const input = ["dir", "dir/file"]; - const out = sort.sortFirstFiles(input); - assertEquals(out, ["dir", "dir/file"]); + assertEquals(sort.sortFirstFiles(["dir", "dir/file"]), ["dir", "dir/file"]); }); Deno.test("sort-iterable: sortFileType basic", () => { - const input = ["a.txt", "b.md", "a.md"]; - const out = sort.sortFileType(input); - assertEquals(out, ["a.md", "b.md", "a.txt"]); + assertEquals(sort.sortFileType(["a.a", "b.a", "a.b"]), [ + "a.a", + "b.a", + "a.b", + ]); }); Deno.test("sort-iterable: sortMixed basic", () => { - const input = ["b", "a", "c"]; - const out = sort.sortMixed(input); - assertEquals(out, ["a", "b", "c"]); + assertEquals(sort.sortMixed(["b", "a", "c"]), ["a", "b", "c"]); }); Deno.test("sort-iterable: sortModified basic", () => {