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