| b69ab31 | | | 1 | /** |
| b69ab31 | | | 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. |
| b69ab31 | | | 3 | * |
| b69ab31 | | | 4 | * This source code is licensed under the MIT license found in the |
| b69ab31 | | | 5 | * LICENSE file in the root directory of this source tree. |
| b69ab31 | | | 6 | */ |
| b69ab31 | | | 7 | |
| b69ab31 | | | 8 | import type {CommitInfo, RepoInfo, RepoRelativePath} from './types'; |
| b69ab31 | | | 9 | |
| b69ab31 | | | 10 | import {atom, useAtomValue} from 'jotai'; |
| b69ab31 | | | 11 | import {atomResetOnDepChange, localStorageBackedAtom} from './jotaiUtils'; |
| b69ab31 | | | 12 | import platform from './platform'; |
| b69ab31 | | | 13 | |
| b69ab31 | | | 14 | export const repositoryData = atom<{info?: RepoInfo; cwd?: string}>({}); |
| b69ab31 | | | 15 | |
| b69ab31 | | | 16 | /** "cwd" is not always the repo root. It can be a path inside the repo. */ |
| b69ab31 | | | 17 | export const serverCwd = atom<string>(get => { |
| b69ab31 | | | 18 | const data = get(repositoryData); |
| b69ab31 | | | 19 | if (data.info?.type === 'cwdNotARepository') { |
| b69ab31 | | | 20 | return data.info.cwd; |
| b69ab31 | | | 21 | } |
| b69ab31 | | | 22 | return data?.cwd ?? platform.initialUrlParams?.get('cwd') ?? ''; |
| b69ab31 | | | 23 | }); |
| b69ab31 | | | 24 | |
| b69ab31 | | | 25 | export const repoRootAtom = atom(get => { |
| b69ab31 | | | 26 | const data = get(repositoryData); |
| b69ab31 | | | 27 | return data.info?.type === 'success' ? data.info.repoRoot : ''; |
| b69ab31 | | | 28 | }); |
| b69ab31 | | | 29 | |
| b69ab31 | | | 30 | export const repoRelativeCwd = atom<RepoRelativePath>(get => { |
| b69ab31 | | | 31 | const cwd = get(serverCwd); |
| b69ab31 | | | 32 | const root = get(repoRootAtom); |
| b69ab31 | | | 33 | return cwd.startsWith(root) ? cwd.slice(root.length + 1) + '/' : cwd; |
| b69ab31 | | | 34 | }); |
| b69ab31 | | | 35 | |
| b69ab31 | | | 36 | /** |
| b69ab31 | | | 37 | * Returns true if the commit is irrelevant to the current cwd, |
| b69ab31 | | | 38 | * due to it modifying only files in folders that are not under the cwd. |
| b69ab31 | | | 39 | * If the max prefix is "inside" the cwd, it is NOT irrelevant. |
| b69ab31 | | | 40 | * > if the max prefix is `addons/isl` and the cwd is `addons`, it is NOT irrelevant. |
| b69ab31 | | | 41 | * If the max prefix is "above" the cwd, it is NOT irrelevant. |
| b69ab31 | | | 42 | * > if the max prefix is `addons` and the cwd is `addons/isl`, it is NOT irrelevant. |
| b69ab31 | | | 43 | * Only if the max prefix contains portions that do not match the cwd is it irrelevant. |
| b69ab31 | | | 44 | * > if the max prefix is `addons/isl` and the cwd is `www`, it IS irrelevant. |
| b69ab31 | | | 45 | * Thus, if the cwd is the repo root, it is never irrelevant. |
| b69ab31 | | | 46 | * |
| b69ab31 | | | 47 | * If a commit has only irrelevant files, but then a relevant file is added, the commit |
| b69ab31 | | | 48 | * is guaranteed to become relevant, since the common portion of the paths will |
| b69ab31 | | | 49 | * be a prefix of the relevant file. |
| b69ab31 | | | 50 | */ |
| b69ab31 | | | 51 | export const useIsIrrelevantToCwd = (commit: CommitInfo) => { |
| b69ab31 | | | 52 | const isEnabled = useAtomValue(irrelevantCwdDeemphasisEnabled); |
| b69ab31 | | | 53 | const cwd = useAtomValue(repoRelativeCwd); |
| b69ab31 | | | 54 | if (!isEnabled) { |
| b69ab31 | | | 55 | return false; |
| b69ab31 | | | 56 | } |
| b69ab31 | | | 57 | return isIrrelevantToCwd(commit, cwd); |
| b69ab31 | | | 58 | }; |
| b69ab31 | | | 59 | |
| b69ab31 | | | 60 | export function isIrrelevantToCwd(commit: CommitInfo, repoRelativeCwd: RepoRelativePath): boolean { |
| b69ab31 | | | 61 | return ( |
| b69ab31 | | | 62 | repoRelativeCwd !== '/' && |
| b69ab31 | | | 63 | !commit.maxCommonPathPrefix.startsWith(repoRelativeCwd) && |
| b69ab31 | | | 64 | !repoRelativeCwd.startsWith(commit.maxCommonPathPrefix) |
| b69ab31 | | | 65 | ); |
| b69ab31 | | | 66 | } |
| b69ab31 | | | 67 | export const __TEST__ = {isIrrelevantToCwd}; |
| b69ab31 | | | 68 | |
| b69ab31 | | | 69 | export const irrelevantCwdDeemphasisEnabled = localStorageBackedAtom<boolean>( |
| b69ab31 | | | 70 | 'isl.deemphasize-cwd-irrelevant-commits', |
| b69ab31 | | | 71 | true, |
| b69ab31 | | | 72 | ); |
| b69ab31 | | | 73 | |
| b69ab31 | | | 74 | export const hideIrrelevantCwdStacks = localStorageBackedAtom<boolean>( |
| b69ab31 | | | 75 | 'isl.hide-cwd-irrelevant-stacks', |
| b69ab31 | | | 76 | false, |
| b69ab31 | | | 77 | ); |
| b69ab31 | | | 78 | |
| b69ab31 | | | 79 | /** |
| b69ab31 | | | 80 | * Derived atom that combines the irrelevant CWD display settings into a single value. |
| b69ab31 | | | 81 | * - 'show': Show irrelevant commits normally (deemphasis disabled) |
| b69ab31 | | | 82 | * - 'deemphasize': Show irrelevant commits but with reduced visual prominence |
| b69ab31 | | | 83 | * - 'hide': Don't show irrelevant commits at all |
| b69ab31 | | | 84 | */ |
| b69ab31 | | | 85 | export const irrelevantCwdDisplayModeAtom = atom( |
| b69ab31 | | | 86 | get => { |
| b69ab31 | | | 87 | const deemphasizeEnabled = get(irrelevantCwdDeemphasisEnabled); |
| b69ab31 | | | 88 | const hideEnabled = get(hideIrrelevantCwdStacks); |
| b69ab31 | | | 89 | |
| b69ab31 | | | 90 | if (hideEnabled) { |
| b69ab31 | | | 91 | return 'hide'; |
| b69ab31 | | | 92 | } |
| b69ab31 | | | 93 | if (deemphasizeEnabled) { |
| b69ab31 | | | 94 | return 'deemphasize'; |
| b69ab31 | | | 95 | } |
| b69ab31 | | | 96 | return 'show'; |
| b69ab31 | | | 97 | }, |
| b69ab31 | | | 98 | (_get, set, newValue: 'show' | 'deemphasize' | 'hide') => { |
| b69ab31 | | | 99 | switch (newValue) { |
| b69ab31 | | | 100 | case 'show': |
| b69ab31 | | | 101 | set(irrelevantCwdDeemphasisEnabled, false); |
| b69ab31 | | | 102 | set(hideIrrelevantCwdStacks, false); |
| b69ab31 | | | 103 | break; |
| b69ab31 | | | 104 | case 'deemphasize': |
| b69ab31 | | | 105 | set(irrelevantCwdDeemphasisEnabled, true); |
| b69ab31 | | | 106 | set(hideIrrelevantCwdStacks, false); |
| b69ab31 | | | 107 | break; |
| b69ab31 | | | 108 | case 'hide': |
| b69ab31 | | | 109 | set(irrelevantCwdDeemphasisEnabled, true); |
| b69ab31 | | | 110 | set(hideIrrelevantCwdStacks, true); |
| b69ab31 | | | 111 | break; |
| b69ab31 | | | 112 | } |
| b69ab31 | | | 113 | }, |
| b69ab31 | | | 114 | ); |
| b69ab31 | | | 115 | |
| b69ab31 | | | 116 | /** |
| b69ab31 | | | 117 | * A string of repo root and the "cwd". Note a same "cwd" does not infer the same repo, |
| b69ab31 | | | 118 | * when there are nested (ex. submodule) repos. |
| b69ab31 | | | 119 | */ |
| b69ab31 | | | 120 | export const repoRootAndCwd = atom<string>(get => `${get(serverCwd)}\n${get(repoRootAtom)}`); |
| b69ab31 | | | 121 | |
| b69ab31 | | | 122 | /** A specific version of `atomResetOnDepChange`. */ |
| b69ab31 | | | 123 | export function atomResetOnCwdChange<T>(defaultValue: T) { |
| b69ab31 | | | 124 | return atomResetOnDepChange(defaultValue, repoRootAndCwd); |
| b69ab31 | | | 125 | } |