From 7b5c5ffbe4ed2932945ac00d4daeb26e225ee32d Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Sun, 6 Feb 2022 22:46:55 -0500 Subject: [PATCH] Avoids more circular references --- src/annotations/annotations.ts | 8 +- src/annotations/blameAnnotationProvider.ts | 2 +- src/annotations/gutterBlameAnnotationProvider.ts | 16 +- src/api/actionRunners.ts | 6 +- src/commands/addAuthors.ts | 3 +- src/commands/base.ts | 17 - src/commands/browseRepoAtRevision.ts | 4 +- src/commands/closeUnchangedFiles.ts | 6 +- src/commands/closeView.ts | 3 +- src/commands/compareWith.ts | 5 +- src/commands/copyCurrentBranch.ts | 5 +- src/commands/copyMessageToClipboard.ts | 6 +- src/commands/copyShaToClipboard.ts | 6 +- src/commands/createPullRequestOnRemote.ts | 4 +- src/commands/diffLineWithPrevious.ts | 4 +- src/commands/diffLineWithWorking.ts | 4 +- src/commands/diffWith.ts | 4 +- src/commands/diffWithNext.ts | 4 +- src/commands/diffWithPrevious.ts | 4 +- src/commands/diffWithRevision.ts | 11 +- src/commands/diffWithRevisionFrom.ts | 13 +- src/commands/diffWithWorking.ts | 4 +- src/commands/externalDiff.ts | 8 +- src/commands/git/branch.ts | 9 +- src/commands/git/cherry-pick.ts | 2 +- src/commands/git/coauthors.ts | 4 +- src/commands/git/fetch.ts | 2 +- src/commands/git/log.ts | 10 +- src/commands/git/merge.ts | 15 +- src/commands/git/pull.ts | 3 +- src/commands/git/push.ts | 3 +- src/commands/git/rebase.ts | 11 +- src/commands/git/reset.ts | 2 +- src/commands/git/revert.ts | 2 +- src/commands/git/search.ts | 10 +- src/commands/git/show.ts | 4 +- src/commands/git/stash.ts | 11 +- src/commands/git/status.ts | 7 +- src/commands/git/switch.ts | 9 +- src/commands/git/tag.ts | 10 +- src/commands/gitCommands.actions.ts | 2 +- src/commands/gitCommands.ts | 143 ++----- src/commands/gitCommands.utils.ts | 125 ++++++ src/commands/inviteToLiveShare.ts | 3 +- src/commands/logging.ts | 3 +- src/commands/openAssociatedPullRequestOnRemote.ts | 4 +- src/commands/openBranchOnRemote.ts | 8 +- src/commands/openBranchesOnRemote.ts | 6 +- src/commands/openChangedFiles.ts | 9 +- src/commands/openCommitOnRemote.ts | 3 +- src/commands/openComparisonOnRemote.ts | 4 +- src/commands/openDirectoryCompare.ts | 6 +- src/commands/openFileAtRevision.ts | 10 +- src/commands/openFileAtRevisionFrom.ts | 12 +- src/commands/openFileFromRemote.ts | 3 +- src/commands/openFileOnRemote.ts | 13 +- src/commands/openIssueOnRemote.ts | 3 +- src/commands/openOnRemote.ts | 37 +- src/commands/openPullRequestOnRemote.ts | 3 +- src/commands/openRepoOnRemote.ts | 6 +- src/commands/openRevisionFile.ts | 3 +- src/commands/openWorkingFile.ts | 3 +- src/commands/quickCommand.steps.ts | 24 +- src/commands/quickCommand.ts | 2 +- src/commands/rebaseEditor.ts | 3 +- src/commands/refreshHover.ts | 4 +- src/commands/remoteProviders.ts | 9 +- src/commands/repositories.ts | 3 +- src/commands/resetAvatarCache.ts | 3 +- src/commands/resetSuppressedWarnings.ts | 3 +- src/commands/searchCommits.ts | 3 +- src/commands/setViewsLayout.ts | 4 +- src/commands/showCommitsInView.ts | 9 +- src/commands/showLastQuickPick.ts | 3 +- src/commands/showQuickBranchHistory.ts | 3 +- src/commands/showQuickCommit.ts | 9 +- src/commands/showQuickCommitFile.ts | 9 +- src/commands/showQuickFileHistory.ts | 5 +- src/commands/showQuickRepoStatus.ts | 3 +- src/commands/showQuickStashList.ts | 3 +- src/commands/showView.ts | 4 +- src/commands/stashApply.ts | 4 +- src/commands/stashSave.ts | 2 +- src/commands/switchMode.ts | 7 +- src/commands/toggleCodeLens.ts | 3 +- src/commands/toggleFileAnnotations.ts | 3 +- src/commands/toggleLineBlame.ts | 3 +- src/constants.ts | 21 ++ src/extension.ts | 3 +- src/git/formatters/commitFormatter.ts | 16 +- src/git/formatters/formatter.ts | 43 ++- src/git/parsers/branchParser.ts | 2 +- src/git/parsers/logParser.ts | 5 +- src/git/parsers/reflogParser.ts | 2 +- src/git/parsers/remoteParser.ts | 2 +- src/git/parsers/tagParser.ts | 2 +- src/git/parsers/treeParser.ts | 2 +- src/git/remotes/custom.ts | 28 +- src/quickpicks.ts | 15 - src/quickpicks/commitPicker.ts | 11 +- src/quickpicks/commitQuickPickItems.ts | 327 ---------------- src/quickpicks/gitQuickPickItems.ts | 439 --------------------- src/quickpicks/items/commits.ts | 327 ++++++++++++++++ src/quickpicks/items/common.ts | 123 ++++++ src/quickpicks/items/directive.ts | 59 +++ src/quickpicks/items/flags.ts | 19 + src/quickpicks/items/gitCommands.ts | 440 ++++++++++++++++++++++ src/quickpicks/quickPicksItems.ts | 195 ---------- src/quickpicks/referencePicker.ts | 3 +- src/quickpicks/remoteProviderPicker.ts | 3 +- src/quickpicks/repositoryPicker.ts | 8 +- src/storage.ts | 4 +- src/system.ts | 26 -- src/system/command.ts | 26 +- src/system/stopwatch.ts | 2 +- src/system/utils.ts | 5 + src/trackers/gitLineTracker.ts | 2 +- src/views/nodes/autolinkedItemsNode.ts | 3 +- src/views/nodes/branchOrTagFolderNode.ts | 4 +- src/views/nodes/branchTrackingStatusFilesNode.ts | 5 +- src/views/nodes/branchesNode.ts | 6 +- src/views/nodes/commitNode.ts | 7 +- src/views/nodes/compareBranchNode.ts | 17 +- src/views/nodes/compareResultsNode.ts | 16 +- src/views/nodes/contributorNode.ts | 14 +- src/views/nodes/fileHistoryTrackerNode.ts | 2 +- src/views/nodes/folderNode.ts | 9 +- src/views/nodes/lineHistoryTrackerNode.ts | 2 +- src/views/nodes/mergeStatusNode.ts | 15 +- src/views/nodes/rebaseStatusNode.ts | 21 +- src/views/nodes/reflogNode.ts | 3 +- src/views/nodes/reflogRecordNode.ts | 6 +- src/views/nodes/remoteNode.ts | 5 +- src/views/nodes/remotesNode.ts | 3 +- src/views/nodes/repositoryNode.ts | 15 +- src/views/nodes/resultsCommitsNode.ts | 6 +- src/views/nodes/resultsFilesNode.ts | 12 +- src/views/nodes/searchResultsNode.ts | 8 +- src/views/nodes/stashNode.ts | 7 +- src/views/nodes/stashesNode.ts | 6 +- src/views/nodes/statusFileNode.ts | 4 +- src/views/nodes/tagNode.ts | 9 +- src/views/nodes/tagsNode.ts | 6 +- src/views/repositoriesView.ts | 2 +- src/views/searchAndCompareView.ts | 3 +- src/views/viewCommands.ts | 2 +- src/vsls/guest.ts | 2 +- src/webviews/rebaseEditor.ts | 8 +- 148 files changed, 1631 insertions(+), 1555 deletions(-) create mode 100644 src/commands/gitCommands.utils.ts delete mode 100644 src/quickpicks.ts delete mode 100644 src/quickpicks/commitQuickPickItems.ts delete mode 100644 src/quickpicks/gitQuickPickItems.ts create mode 100644 src/quickpicks/items/commits.ts create mode 100644 src/quickpicks/items/common.ts create mode 100644 src/quickpicks/items/directive.ts create mode 100644 src/quickpicks/items/flags.ts create mode 100644 src/quickpicks/items/gitCommands.ts delete mode 100644 src/quickpicks/quickPicksItems.ts delete mode 100644 src/system.ts diff --git a/src/annotations/annotations.ts b/src/annotations/annotations.ts index 63b46a3..56dd8a1 100644 --- a/src/annotations/annotations.ts +++ b/src/annotations/annotations.ts @@ -16,7 +16,7 @@ import { Colors, GlyphChars } from '../constants'; import { Container } from '../container'; import { CommitFormatOptions, CommitFormatter } from '../git/formatters'; import { GitCommit } from '../git/models'; -import { Strings } from '../system'; +import { getWidth, interpolate, pad } from '../system/string'; import { toRgba } from '../webviews/apps/shared/colors'; export interface ComputedHeatmap { @@ -157,7 +157,7 @@ export class Annotations { } const message = CommitFormatter.fromTemplate(format, commit, dateFormatOrFormatOptions); - decoration.renderOptions!.before!.contentText = Strings.pad(message.replace(/ /g, GlyphChars.Space), 1, 1); + decoration.renderOptions!.before!.contentText = pad(message.replace(/ /g, GlyphChars.Space), 1, 1); return decoration; } @@ -185,7 +185,7 @@ export class Annotations { if (chars >= 0) { // Add the chars of the template string (without tokens) - chars += Strings.getWidth(Strings.interpolate(format, undefined)); + chars += getWidth(interpolate(format, undefined)); // If we have chars, add a bit of padding if (chars > 0) { chars += 3; @@ -251,7 +251,7 @@ export class Annotations { after: { backgroundColor: new ThemeColor(Colors.TrailingLineBackgroundColor), color: new ThemeColor(Colors.TrailingLineForegroundColor), - contentText: Strings.pad(message.replace(/ /g, GlyphChars.Space), 1, 1), + contentText: pad(message.replace(/ /g, GlyphChars.Space), 1, 1), fontWeight: 'normal', fontStyle: 'normal', // Pull the decoration out of the document flow if we want to be scrollable diff --git a/src/annotations/blameAnnotationProvider.ts b/src/annotations/blameAnnotationProvider.ts index f28cf80..2573c0b 100644 --- a/src/annotations/blameAnnotationProvider.ts +++ b/src/annotations/blameAnnotationProvider.ts @@ -4,7 +4,7 @@ import { Container } from '../container'; import { GitUri } from '../git/gitUri'; import { GitBlame, GitCommit } from '../git/models'; import { Hovers } from '../hovers/hovers'; -import { log } from '../system'; +import { log } from '../system/decorators/log'; import { GitDocumentState, TrackedDocument } from '../trackers/gitDocumentTracker'; import { AnnotationProviderBase } from './annotationProvider'; import { ComputedHeatmap, getHeatmapColors } from './annotations'; diff --git a/src/annotations/gutterBlameAnnotationProvider.ts b/src/annotations/gutterBlameAnnotationProvider.ts index fdcdbf6..a7e56b3 100644 --- a/src/annotations/gutterBlameAnnotationProvider.ts +++ b/src/annotations/gutterBlameAnnotationProvider.ts @@ -5,9 +5,11 @@ import { Container } from '../container'; import { CommitFormatOptions, CommitFormatter } from '../git/formatters'; import { GitBlame, GitCommit } from '../git/models'; import { Logger } from '../logger'; -import { Arrays, Iterables, Strings } from '../system'; +import { filterMap } from '../system/array'; import { log } from '../system/decorators/log'; +import { first } from '../system/iterable'; import { Stopwatch } from '../system/stopwatch'; +import { getTokensFromTemplate, getWidth, TokenOptions } from '../system/string'; import { GitDocumentState } from '../trackers/gitDocumentTracker'; import { TrackedDocument } from '../trackers/trackedDocument'; import { AnnotationContext } from './annotationProvider'; @@ -44,8 +46,8 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase { const cfg = this.container.config.blame; // Precalculate the formatting options so we don't need to do it on each iteration - const tokenOptions = Strings.getTokensFromTemplate(cfg.format).reduce<{ - [token: string]: Strings.TokenOptions | undefined; + const tokenOptions = getTokensFromTemplate(cfg.format).reduce<{ + [token: string]: TokenOptions | undefined; }>((map, token) => { map[token.key] = token.options; return map; @@ -102,9 +104,7 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase { gutter.renderOptions = { before: { ...gutter.renderOptions!.before, - contentText: GlyphChars.Space.repeat( - Strings.getWidth(gutter.renderOptions!.before!.contentText!), - ), + contentText: GlyphChars.Space.repeat(getWidth(gutter.renderOptions!.before!.contentText!)), }, }; @@ -191,7 +191,7 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase { sha = commitLine?.sha; } } else { - sha = Iterables.first(blame.commits.values()).sha; + sha = first(blame.commits.values()).sha; } if (!sha) { @@ -199,7 +199,7 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase { return; } - const highlightDecorationRanges = Arrays.filterMap(blame.lines, l => + const highlightDecorationRanges = filterMap(blame.lines, l => l.sha === sha ? // editor lines are 0-based this.editor.document.validateRange(new Range(l.line - 1, 0, l.line - 1, Number.MAX_SAFE_INTEGER)) diff --git a/src/api/actionRunners.ts b/src/api/actionRunners.ts index a77c5f3..a31df6e 100644 --- a/src/api/actionRunners.ts +++ b/src/api/actionRunners.ts @@ -3,8 +3,8 @@ import { Config, configuration } from '../configuration'; import { Commands, ContextKeys } from '../constants'; import { Container } from '../container'; import { setContext } from '../context'; -import { getQuickPickIgnoreFocusOut } from '../quickpicks'; -import { Strings } from '../system'; +import { sortCompare } from '../system/string'; +import { getQuickPickIgnoreFocusOut } from '../system/utils'; import type { Action, ActionContext, ActionRunner } from './gitlens'; type Actions = ActionContext['type']; @@ -255,7 +255,7 @@ export class ActionRunners implements Disposable { if (runners.length > 1 || runners.every(r => r.type !== ActionRunnerType.BuiltIn)) { const items: (ActionRunnerQuickPickItem | NoActionRunnersQuickPickItem)[] = runners // .filter(r => r.when(context)) - .sort((a, b) => a.order - b.order || Strings.sortCompare(a.name, b.name)) + .sort((a, b) => a.order - b.order || sortCompare(a.name, b.name)) .map(r => new ActionRunnerQuickPickItem(r, context)); if (items.length === 0) { diff --git a/src/commands/addAuthors.ts b/src/commands/addAuthors.ts index b282818..00cef4d 100644 --- a/src/commands/addAuthors.ts +++ b/src/commands/addAuthors.ts @@ -1,7 +1,8 @@ import { SourceControl } from 'vscode'; import { Commands } from '../constants'; import type { Container } from '../container'; -import { command, Command } from './base'; +import { command } from '../system/command'; +import { Command } from './base'; import { executeGitCommand } from './gitCommands.actions'; @command() diff --git a/src/commands/base.ts b/src/commands/base.ts index 938975d..827fe1b 100644 --- a/src/commands/base.ts +++ b/src/commands/base.ts @@ -12,7 +12,6 @@ import { } from 'vscode'; import type { ActionContext } from '../api/gitlens'; import { Commands } from '../constants'; -import { Container } from '../container'; import { GitBranch, GitCommit, @@ -26,22 +25,6 @@ import { } from '../git/models'; import { ViewNode, ViewRefNode } from '../views/nodes'; -interface CommandConstructor { - new (container: Container): Command; -} - -const registrableCommands: CommandConstructor[] = []; - -export function command(): ClassDecorator { - return (target: any) => { - registrableCommands.push(target); - }; -} - -export function registerCommands(container: Container): Disposable[] { - return registrableCommands.map(c => new c(container)); -} - export function getCommandUri(uri?: Uri, editor?: TextEditor): Uri | undefined { // Always use the editor.uri (if we have one), so we are correct for a split diff return editor?.document?.uri ?? uri; diff --git a/src/commands/browseRepoAtRevision.ts b/src/commands/browseRepoAtRevision.ts index 69a364c..7dadd9d 100644 --- a/src/commands/browseRepoAtRevision.ts +++ b/src/commands/browseRepoAtRevision.ts @@ -4,10 +4,10 @@ import type { Container } from '../container'; import { GitUri } from '../git/gitUri'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { executeCoreCommand } from '../system/command'; +import { command, executeCoreCommand } from '../system/command'; import { basename } from '../system/path'; import { openWorkspace, OpenWorkspaceLocation } from '../system/utils'; -import { ActiveEditorCommand, command, CommandContext, getCommandUri } from './base'; +import { ActiveEditorCommand, CommandContext, getCommandUri } from './base'; export interface BrowseRepoAtRevisionCommandArgs { uri?: Uri; diff --git a/src/commands/closeUnchangedFiles.ts b/src/commands/closeUnchangedFiles.ts index 5e1e273..5de8ced 100644 --- a/src/commands/closeUnchangedFiles.ts +++ b/src/commands/closeUnchangedFiles.ts @@ -4,10 +4,10 @@ import { Commands, CoreCommands } from '../constants'; import type { Container } from '../container'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { RepositoryPicker } from '../quickpicks'; -import { executeCoreCommand } from '../system/command'; +import { RepositoryPicker } from '../quickpicks/repositoryPicker'; +import { command, executeCoreCommand } from '../system/command'; import { debounce } from '../system/function'; -import { Command, command } from './base'; +import { Command } from './base'; export interface CloseUnchangedFilesCommandArgs { uris?: Uri[]; diff --git a/src/commands/closeView.ts b/src/commands/closeView.ts index b2fac6c..21d01c0 100644 --- a/src/commands/closeView.ts +++ b/src/commands/closeView.ts @@ -2,7 +2,8 @@ import { Commands, ContextKeys } from '../constants'; import type { Container } from '../container'; import { setContext } from '../context'; import { SyncedState } from '../storage'; -import { command, Command, CommandContext } from './base'; +import { command } from '../system/command'; +import { Command, CommandContext } from './base'; @command() export class CloseViewCommand extends Command { diff --git a/src/commands/compareWith.ts b/src/commands/compareWith.ts index e446579..8e7028f 100644 --- a/src/commands/compareWith.ts +++ b/src/commands/compareWith.ts @@ -3,8 +3,9 @@ import { Commands } from '../constants'; import type { Container } from '../container'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { RepositoryPicker } from '../quickpicks'; -import { ActiveEditorCommand, command, CommandContext, getCommandUri } from './base'; +import { RepositoryPicker } from '../quickpicks/repositoryPicker'; +import { command } from '../system/command'; +import { ActiveEditorCommand, CommandContext, getCommandUri } from './base'; export interface CompareWithCommandArgs { ref1?: string; diff --git a/src/commands/copyCurrentBranch.ts b/src/commands/copyCurrentBranch.ts index 62d388d..66d1fcd 100644 --- a/src/commands/copyCurrentBranch.ts +++ b/src/commands/copyCurrentBranch.ts @@ -3,8 +3,9 @@ import { Commands } from '../constants'; import type { Container } from '../container'; import { GitUri } from '../git/gitUri'; import { Logger } from '../logger'; -import { RepositoryPicker } from '../quickpicks'; -import { ActiveEditorCommand, command, getCommandUri } from './base'; +import { RepositoryPicker } from '../quickpicks/repositoryPicker'; +import { command } from '../system/command'; +import { ActiveEditorCommand, getCommandUri } from './base'; @command() export class CopyCurrentBranchCommand extends ActiveEditorCommand { diff --git a/src/commands/copyMessageToClipboard.ts b/src/commands/copyMessageToClipboard.ts index ed97715..9a607f0 100644 --- a/src/commands/copyMessageToClipboard.ts +++ b/src/commands/copyMessageToClipboard.ts @@ -4,10 +4,10 @@ import type { Container } from '../container'; import { GitUri } from '../git/gitUri'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { Iterables } from '../system'; +import { command } from '../system/command'; +import { first } from '../system/iterable'; import { ActiveEditorCommand, - command, CommandContext, getCommandUri, isCommandContextViewNodeHasBranch, @@ -60,7 +60,7 @@ export class CopyMessageToClipboardCommand extends ActiveEditorCommand { const log = await this.container.git.getLog(repoPath, { limit: 1 }); if (log == null) return; - const commit = Iterables.first(log.commits.values()); + const commit = first(log.commits.values()); if (commit?.message == null) return; args.message = commit.message; diff --git a/src/commands/copyShaToClipboard.ts b/src/commands/copyShaToClipboard.ts index b5b1b05..1d558e7 100644 --- a/src/commands/copyShaToClipboard.ts +++ b/src/commands/copyShaToClipboard.ts @@ -4,10 +4,10 @@ import type { Container } from '../container'; import { GitUri } from '../git/gitUri'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { Iterables } from '../system'; +import { command } from '../system/command'; +import { first } from '../system/iterable'; import { ActiveEditorCommand, - command, CommandContext, getCommandUri, isCommandContextViewNodeHasBranch, @@ -58,7 +58,7 @@ export class CopyShaToClipboardCommand extends ActiveEditorCommand { const log = await this.container.git.getLog(repoPath, { limit: 1 }); if (log == null) return; - args.sha = Iterables.first(log.commits.values()).sha; + args.sha = first(log.commits.values()).sha; } else if (args.sha == null) { const blameline = editor?.selection.active.line ?? 0; if (blameline < 0) return; diff --git a/src/commands/createPullRequestOnRemote.ts b/src/commands/createPullRequestOnRemote.ts index 81fe13d..e534ede 100644 --- a/src/commands/createPullRequestOnRemote.ts +++ b/src/commands/createPullRequestOnRemote.ts @@ -2,8 +2,8 @@ import { Commands } from '../constants'; import type { Container } from '../container'; import { GitRemote } from '../git/models'; import { RemoteProvider, RemoteResource, RemoteResourceType } from '../git/remotes/provider'; -import { executeCommand } from '../system/command'; -import { Command, command } from './base'; +import { command, executeCommand } from '../system/command'; +import { Command } from './base'; import { OpenOnRemoteCommandArgs } from './openOnRemote'; export interface CreatePullRequestOnRemoteCommandArgs { diff --git a/src/commands/diffLineWithPrevious.ts b/src/commands/diffLineWithPrevious.ts index 2b0e8c9..ac2b0d2 100644 --- a/src/commands/diffLineWithPrevious.ts +++ b/src/commands/diffLineWithPrevious.ts @@ -5,8 +5,8 @@ import { GitUri } from '../git/gitUri'; import { GitCommit } from '../git/models'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { executeCommand } from '../system/command'; -import { ActiveEditorCommand, command, getCommandUri } from './base'; +import { command, executeCommand } from '../system/command'; +import { ActiveEditorCommand, getCommandUri } from './base'; import { DiffWithCommandArgs } from './diffWith'; export interface DiffLineWithPreviousCommandArgs { diff --git a/src/commands/diffLineWithWorking.ts b/src/commands/diffLineWithWorking.ts index 33ce952..3e56c65 100644 --- a/src/commands/diffLineWithWorking.ts +++ b/src/commands/diffLineWithWorking.ts @@ -5,8 +5,8 @@ import { GitUri } from '../git/gitUri'; import { GitCommit, GitRevision } from '../git/models'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { executeCommand } from '../system/command'; -import { ActiveEditorCommand, command, getCommandUri } from './base'; +import { command, executeCommand } from '../system/command'; +import { ActiveEditorCommand, getCommandUri } from './base'; import { DiffWithCommandArgs } from './diffWith'; export interface DiffLineWithWorkingCommandArgs { diff --git a/src/commands/diffWith.ts b/src/commands/diffWith.ts index 7775a05..632f410 100644 --- a/src/commands/diffWith.ts +++ b/src/commands/diffWith.ts @@ -4,9 +4,9 @@ import type { Container } from '../container'; import { GitCommit, GitRevision } from '../git/models'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { executeCoreCommand } from '../system/command'; +import { command, executeCoreCommand } from '../system/command'; import { basename } from '../system/path'; -import { command, Command } from './base'; +import { Command } from './base'; export interface DiffWithCommandArgsRevision { sha: string; diff --git a/src/commands/diffWithNext.ts b/src/commands/diffWithNext.ts index b082ba3..85ba2e7 100644 --- a/src/commands/diffWithNext.ts +++ b/src/commands/diffWithNext.ts @@ -5,8 +5,8 @@ import { GitUri } from '../git/gitUri'; import { GitCommit } from '../git/models'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { executeCommand } from '../system/command'; -import { ActiveEditorCommand, command, CommandContext, getCommandUri } from './base'; +import { command, executeCommand } from '../system/command'; +import { ActiveEditorCommand, CommandContext, getCommandUri } from './base'; import { DiffWithCommandArgs } from './diffWith'; export interface DiffWithNextCommandArgs { diff --git a/src/commands/diffWithPrevious.ts b/src/commands/diffWithPrevious.ts index 35efb80..377fc13 100644 --- a/src/commands/diffWithPrevious.ts +++ b/src/commands/diffWithPrevious.ts @@ -5,9 +5,9 @@ import { GitUri } from '../git/gitUri'; import { GitCommit, GitRevision } from '../git/models'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { executeCommand } from '../system/command'; +import { command, executeCommand } from '../system/command'; import { findOrOpenEditor } from '../system/utils'; -import { ActiveEditorCommand, command, CommandContext, getCommandUri } from './base'; +import { ActiveEditorCommand, CommandContext, getCommandUri } from './base'; import { DiffWithCommandArgs } from './diffWith'; export interface DiffWithPreviousCommandArgs { diff --git a/src/commands/diffWithRevision.ts b/src/commands/diffWithRevision.ts index d1f9e39..43929a1 100644 --- a/src/commands/diffWithRevision.ts +++ b/src/commands/diffWithRevision.ts @@ -5,10 +5,11 @@ import { GitUri } from '../git/gitUri'; import { GitRevision } from '../git/models'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { CommandQuickPickItem, CommitPicker } from '../quickpicks'; -import { Strings } from '../system'; -import { executeCommand } from '../system/command'; -import { ActiveEditorCommand, command, getCommandUri } from './base'; +import { CommitPicker } from '../quickpicks/commitPicker'; +import { CommandQuickPickItem } from '../quickpicks/items/common'; +import { command, executeCommand } from '../system/command'; +import { pad } from '../system/string'; +import { ActiveEditorCommand, getCommandUri } from './base'; import { DiffWithCommandArgs } from './diffWith'; import { DiffWithRevisionFromCommandArgs } from './diffWithRevisionFrom'; @@ -45,7 +46,7 @@ export class DiffWithRevisionCommand extends ActiveEditorCommand { : undefined), ); - const title = `Open Changes with Revision${Strings.pad(GlyphChars.Dot, 2, 2)}`; + const title = `Open Changes with Revision${pad(GlyphChars.Dot, 2, 2)}`; const pick = await CommitPicker.show( log, `${title}${gitUri.getFormattedFileName({ diff --git a/src/commands/diffWithRevisionFrom.ts b/src/commands/diffWithRevisionFrom.ts index 9ac924d..0e293da 100644 --- a/src/commands/diffWithRevisionFrom.ts +++ b/src/commands/diffWithRevisionFrom.ts @@ -4,11 +4,12 @@ import type { Container } from '../container'; import { GitUri } from '../git/gitUri'; import { GitReference, GitRevision } from '../git/models'; import { Messages } from '../messages'; -import { ReferencePicker, StashPicker } from '../quickpicks'; -import { Strings } from '../system'; -import { executeCommand } from '../system/command'; +import { StashPicker } from '../quickpicks/commitPicker'; +import { ReferencePicker } from '../quickpicks/referencePicker'; +import { command, executeCommand } from '../system/command'; import { basename } from '../system/path'; -import { ActiveEditorCommand, command, getCommandUri } from './base'; +import { pad } from '../system/string'; +import { ActiveEditorCommand, getCommandUri } from './base'; import { DiffWithCommandArgs } from './diffWith'; export interface DiffWithRevisionFromCommandArgs { @@ -44,7 +45,7 @@ export class DiffWithRevisionFromCommand extends ActiveEditorCommand { let ref; let sha; if (args?.stash) { - const title = `Open Changes with Stash${Strings.pad(GlyphChars.Dot, 2, 2)}`; + const title = `Open Changes with Stash${pad(GlyphChars.Dot, 2, 2)}`; const pick = await StashPicker.show( this.container.git.getStash(gitUri.repoPath), `${title}${gitUri.getFormattedFileName({ truncateTo: quickPickTitleMaxChars - title.length })}`, @@ -60,7 +61,7 @@ export class DiffWithRevisionFromCommand extends ActiveEditorCommand { ref = pick.ref; sha = ref; } else { - const title = `Open Changes with Branch or Tag${Strings.pad(GlyphChars.Dot, 2, 2)}`; + const title = `Open Changes with Branch or Tag${pad(GlyphChars.Dot, 2, 2)}`; const pick = await ReferencePicker.show( gitUri.repoPath, `${title}${gitUri.getFormattedFileName({ truncateTo: quickPickTitleMaxChars - title.length })}`, diff --git a/src/commands/diffWithWorking.ts b/src/commands/diffWithWorking.ts index 6f9d336..c1d741b 100644 --- a/src/commands/diffWithWorking.ts +++ b/src/commands/diffWithWorking.ts @@ -5,8 +5,8 @@ import { GitUri } from '../git/gitUri'; import { GitRevision } from '../git/models'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { executeCommand } from '../system/command'; -import { ActiveEditorCommand, command, getCommandUri } from './base'; +import { command, executeCommand } from '../system/command'; +import { ActiveEditorCommand, getCommandUri } from './base'; import { DiffWithCommandArgs } from './diffWith'; export interface DiffWithWorkingCommandArgs { diff --git a/src/commands/externalDiff.ts b/src/commands/externalDiff.ts index 40b50eb..e933aab 100644 --- a/src/commands/externalDiff.ts +++ b/src/commands/externalDiff.ts @@ -7,10 +7,10 @@ import { GitUri } from '../git/gitUri'; import { GitRevision } from '../git/models'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { RepositoryPicker } from '../quickpicks'; -import { Arrays } from '../system'; +import { RepositoryPicker } from '../quickpicks/repositoryPicker'; +import { filterMap } from '../system/array'; +import { command } from '../system/command'; import { - command, Command, CommandContext, isCommandContextViewNodeHasFileCommit, @@ -74,7 +74,7 @@ export class ExternalDiffCommand extends Command { staged: (r as ScmResource).resourceGroupType === ScmResourceGroupType.Index, })); } else if (context.type === 'scm-groups') { - args.files = Arrays.filterMap(context.scmResourceGroups[0].resourceStates, r => + args.files = filterMap(context.scmResourceGroups[0].resourceStates, r => this.isModified(r) ? { uri: r.resourceUri, diff --git a/src/commands/git/branch.ts b/src/commands/git/branch.ts index 4a4243a..735e13e 100644 --- a/src/commands/git/branch.ts +++ b/src/commands/git/branch.ts @@ -1,8 +1,9 @@ import { QuickInputButtons } from 'vscode'; -import { Container } from '../../container'; +import type { Container } from '../../container'; import { GitBranchReference, GitReference, Repository } from '../../git/models'; -import { FlagsQuickPickItem, QuickPickItemOfT } from '../../quickpicks'; -import { Strings } from '../../system'; +import { QuickPickItemOfT } from '../../quickpicks/items/common'; +import { FlagsQuickPickItem } from '../../quickpicks/items/flags'; +import { pluralize } from '../../system/string'; import { ViewsWithRepositoryFolders } from '../../views/viewBase'; import { appendReposToTitle, @@ -388,7 +389,7 @@ export class BranchGitCommand extends QuickCommand { } context.title = getTitle( - Strings.pluralize('Branch', state.references.length, { only: true, plural: 'Branches' }), + pluralize('Branch', state.references.length, { only: true, plural: 'Branches' }), state.subcommand, ); diff --git a/src/commands/git/cherry-pick.ts b/src/commands/git/cherry-pick.ts index 3959501..a5d25a3 100644 --- a/src/commands/git/cherry-pick.ts +++ b/src/commands/git/cherry-pick.ts @@ -1,6 +1,6 @@ import { Container } from '../../container'; import { GitBranch, GitLog, GitReference, GitRevision, Repository } from '../../git/models'; -import { FlagsQuickPickItem } from '../../quickpicks'; +import { FlagsQuickPickItem } from '../../quickpicks/items/flags'; import { ViewsWithRepositoryFolders } from '../../views/viewBase'; import { appendReposToTitle, diff --git a/src/commands/git/coauthors.ts b/src/commands/git/coauthors.ts index 1e49d66..2128e4d 100644 --- a/src/commands/git/coauthors.ts +++ b/src/commands/git/coauthors.ts @@ -1,6 +1,6 @@ import { CoreCommands } from '../../constants'; -import { Container } from '../../container'; -import { GitContributor, Repository } from '../../git/models'; +import type { Container } from '../../container'; +import type { GitContributor, Repository } from '../../git/models'; import { executeCoreCommand } from '../../system/command'; import { normalizePath } from '../../system/path'; import { ViewsWithRepositoryFolders } from '../../views/viewBase'; diff --git a/src/commands/git/fetch.ts b/src/commands/git/fetch.ts index 08ca2e9..e1f77fe 100644 --- a/src/commands/git/fetch.ts +++ b/src/commands/git/fetch.ts @@ -1,7 +1,7 @@ import { GlyphChars } from '../../constants'; import { Container } from '../../container'; import { GitBranchReference, GitReference, Repository } from '../../git/models'; -import { FlagsQuickPickItem } from '../../quickpicks'; +import { FlagsQuickPickItem } from '../../quickpicks/items/flags'; import { isStringArray } from '../../system/array'; import { fromNow } from '../../system/date'; import { pad } from '../../system/string'; diff --git a/src/commands/git/log.ts b/src/commands/git/log.ts index 6452a83..8414f2b 100644 --- a/src/commands/git/log.ts +++ b/src/commands/git/log.ts @@ -1,10 +1,10 @@ import { GlyphChars, quickPickTitleMaxChars } from '../../constants'; import { Container } from '../../container'; import { GitCommit, GitLog, GitReference, Repository } from '../../git/models'; -import { Strings } from '../../system'; import { formatPath } from '../../system/formatPath'; +import { pad } from '../../system/string'; import { ViewsWithRepositoryFolders } from '../../views/viewBase'; -import { GitCommandsCommand } from '../gitCommands'; +import { getSteps } from '../gitCommands.utils'; import { PartialStepState, pickBranchOrTagStep, @@ -146,13 +146,13 @@ export class LogGitCommand extends QuickCommand { context.selectedBranchOrTag = state.reference; } - context.title = `${this.title}${Strings.pad(GlyphChars.Dot, 2, 2)}${GitReference.toString( + context.title = `${this.title}${pad(GlyphChars.Dot, 2, 2)}${GitReference.toString( context.selectedBranchOrTag, { icon: false }, )}`; if (state.fileName) { - context.title += `${Strings.pad(GlyphChars.Dot, 2, 2)}${formatPath(state.fileName, { + context.title += `${pad(GlyphChars.Dot, 2, 2)}${formatPath(state.fileName, { fileOnly: true, truncateTo: quickPickTitleMaxChars - context.title.length - 3, })}`; @@ -191,7 +191,7 @@ export class LogGitCommand extends QuickCommand { state.reference = await this.container.git.getCommit(state.repo.path, state.reference.ref); } - const result = yield* GitCommandsCommand.getSteps( + const result = yield* getSteps( this.container, { command: 'show', diff --git a/src/commands/git/merge.ts b/src/commands/git/merge.ts index 3079016..6df7ce5 100644 --- a/src/commands/git/merge.ts +++ b/src/commands/git/merge.ts @@ -1,7 +1,8 @@ import { Container } from '../../container'; import { GitBranch, GitLog, GitReference, GitRevision, Repository } from '../../git/models'; -import { Directive, DirectiveQuickPickItem, FlagsQuickPickItem } from '../../quickpicks'; -import { Strings } from '../../system'; +import { Directive, DirectiveQuickPickItem } from '../../quickpicks/items/directive'; +import { FlagsQuickPickItem } from '../../quickpicks/items/flags'; +import { pluralize } from '../../system/string'; import { ViewsWithRepositoryFolders } from '../../views/viewBase'; import { appendReposToTitle, @@ -225,28 +226,28 @@ export class MergeGitCommand extends QuickCommand { [ FlagsQuickPickItem.create(state.flags, [], { label: this.title, - detail: `Will merge ${Strings.pluralize('commit', count)} from ${GitReference.toString( + detail: `Will merge ${pluralize('commit', count)} from ${GitReference.toString( state.reference, )} into ${GitReference.toString(context.destination)}`, }), FlagsQuickPickItem.create(state.flags, ['--ff-only'], { label: `Fast-forward ${this.title}`, description: '--ff-only', - detail: `Will fast-forward merge ${Strings.pluralize('commit', count)} from ${GitReference.toString( + detail: `Will fast-forward merge ${pluralize('commit', count)} from ${GitReference.toString( state.reference, )} into ${GitReference.toString(context.destination)}`, }), FlagsQuickPickItem.create(state.flags, ['--squash'], { label: `Squash ${this.title}`, description: '--squash', - detail: `Will squash ${Strings.pluralize('commit', count)} from ${GitReference.toString( + detail: `Will squash ${pluralize('commit', count)} from ${GitReference.toString( state.reference, )} into one when merging into ${GitReference.toString(context.destination)}`, }), FlagsQuickPickItem.create(state.flags, ['--no-ff'], { label: `${this.title} without Fast-Forwarding`, description: '--no-ff', - detail: `Will create a merge commit when merging ${Strings.pluralize( + detail: `Will create a merge commit when merging ${pluralize( 'commit', count, )} from ${GitReference.toString(state.reference)} into ${GitReference.toString( @@ -256,7 +257,7 @@ export class MergeGitCommand extends QuickCommand { FlagsQuickPickItem.create(state.flags, ['--no-ff', '--no-commit'], { label: `${this.title} without Fast-Forwarding or Committing`, description: '--no-ff --no-commit', - detail: `Will merge ${Strings.pluralize('commit', count)} from ${GitReference.toString( + detail: `Will merge ${pluralize('commit', count)} from ${GitReference.toString( state.reference, )} into ${GitReference.toString(context.destination)} without Committing`, }), diff --git a/src/commands/git/pull.ts b/src/commands/git/pull.ts index 3be5441..1eaadc8 100644 --- a/src/commands/git/pull.ts +++ b/src/commands/git/pull.ts @@ -1,7 +1,8 @@ import { GlyphChars } from '../../constants'; import { Container } from '../../container'; import { GitBranch, GitBranchReference, GitReference, Repository } from '../../git/models'; -import { Directive, DirectiveQuickPickItem, FlagsQuickPickItem } from '../../quickpicks'; +import { Directive, DirectiveQuickPickItem } from '../../quickpicks/items/directive'; +import { FlagsQuickPickItem } from '../../quickpicks/items/flags'; import { isStringArray } from '../../system/array'; import { fromNow } from '../../system/date'; import { pad, pluralize } from '../../system/string'; diff --git a/src/commands/git/push.ts b/src/commands/git/push.ts index d062046..d9e6336 100644 --- a/src/commands/git/push.ts +++ b/src/commands/git/push.ts @@ -2,7 +2,8 @@ import { configuration } from '../../configuration'; import { CoreGitConfiguration, GlyphChars } from '../../constants'; import { Container } from '../../container'; import { GitBranch, GitBranchReference, GitReference, Repository } from '../../git/models'; -import { Directive, DirectiveQuickPickItem, FlagsQuickPickItem } from '../../quickpicks'; +import { Directive, DirectiveQuickPickItem } from '../../quickpicks/items/directive'; +import { FlagsQuickPickItem } from '../../quickpicks/items/flags'; import { isStringArray } from '../../system/array'; import { fromNow } from '../../system/date'; import { pad, pluralize } from '../../system/string'; diff --git a/src/commands/git/rebase.ts b/src/commands/git/rebase.ts index 68c2ff9..a38987d 100644 --- a/src/commands/git/rebase.ts +++ b/src/commands/git/rebase.ts @@ -1,8 +1,9 @@ import { env } from 'vscode'; import { Container } from '../../container'; import { GitBranch, GitLog, GitReference, GitRevision, Repository } from '../../git/models'; -import { Directive, DirectiveQuickPickItem, FlagsQuickPickItem } from '../../quickpicks'; -import { Strings } from '../../system'; +import { Directive, DirectiveQuickPickItem } from '../../quickpicks/items/directive'; +import { FlagsQuickPickItem } from '../../quickpicks/items/flags'; +import { pluralize } from '../../system/string'; import { ViewsWithRepositoryFolders } from '../../views/viewBase'; import { appendReposToTitle, @@ -252,7 +253,7 @@ export class RebaseGitCommand extends QuickCommand { [ FlagsQuickPickItem.create(state.flags, [], { label: this.title, - detail: `Will update ${GitReference.toString(context.destination)} by applying ${Strings.pluralize( + detail: `Will update ${GitReference.toString(context.destination)} by applying ${pluralize( 'commit', count, )} on top of ${GitReference.toString(state.reference)}`, @@ -262,9 +263,7 @@ export class RebaseGitCommand extends QuickCommand { description: '--interactive', detail: `Will interactively update ${GitReference.toString( context.destination, - )} by applying ${Strings.pluralize('commit', count)} on top of ${GitReference.toString( - state.reference, - )}`, + )} by applying ${pluralize('commit', count)} on top of ${GitReference.toString(state.reference)}`, }), ], ); diff --git a/src/commands/git/reset.ts b/src/commands/git/reset.ts index cbb0457..208b28f 100644 --- a/src/commands/git/reset.ts +++ b/src/commands/git/reset.ts @@ -1,6 +1,6 @@ import { Container } from '../../container'; import { GitBranch, GitLog, GitReference, GitRevisionReference, Repository } from '../../git/models'; -import { FlagsQuickPickItem } from '../../quickpicks'; +import { FlagsQuickPickItem } from '../../quickpicks/items/flags'; import { ViewsWithRepositoryFolders } from '../../views/viewBase'; import { appendReposToTitle, diff --git a/src/commands/git/revert.ts b/src/commands/git/revert.ts index 0138ff4..e03cc5c 100644 --- a/src/commands/git/revert.ts +++ b/src/commands/git/revert.ts @@ -1,6 +1,6 @@ import { Container } from '../../container'; import { GitBranch, GitLog, GitReference, GitRevisionReference, Repository } from '../../git/models'; -import { FlagsQuickPickItem } from '../../quickpicks'; +import { FlagsQuickPickItem } from '../../quickpicks/items/flags'; import { ViewsWithRepositoryFolders } from '../../views/viewBase'; import { appendReposToTitle, diff --git a/src/commands/git/search.ts b/src/commands/git/search.ts index f93ddc1..99d382e 100644 --- a/src/commands/git/search.ts +++ b/src/commands/git/search.ts @@ -2,11 +2,11 @@ import { GlyphChars } from '../../constants'; import { Container } from '../../container'; import { GitCommit, GitLog, Repository } from '../../git/models'; import { searchOperators, SearchOperators, SearchPattern } from '../../git/search'; -import { ActionQuickPickItem, QuickPickItemOfT } from '../../quickpicks'; -import { Strings } from '../../system'; +import { ActionQuickPickItem, QuickPickItemOfT } from '../../quickpicks/items/common'; +import { pluralize } from '../../system/string'; import { SearchResultsNode } from '../../views/nodes'; import { ViewsWithRepositoryFolders } from '../../views/viewBase'; -import { GitCommandsCommand } from '../gitCommands'; +import { getSteps } from '../gitCommands.utils'; import { appendReposToTitle, PartialStepState, @@ -191,7 +191,7 @@ export class SearchGitCommand extends QuickCommand { placeholder: (context, log) => log == null ? `No results for ${state.pattern}` - : `${Strings.pluralize('result', log.count, { + : `${pluralize('result', log.count, { format: c => (log.hasMore ? `${c}+` : undefined), })} for ${state.pattern}`, picked: context.commit?.ref, @@ -238,7 +238,7 @@ export class SearchGitCommand extends QuickCommand { context.commit = result; } - const result = yield* GitCommandsCommand.getSteps( + const result = yield* getSteps( this.container, { command: 'show', diff --git a/src/commands/git/show.ts b/src/commands/git/show.ts index bcab9d9..4518ca7 100644 --- a/src/commands/git/show.ts +++ b/src/commands/git/show.ts @@ -1,6 +1,8 @@ import { Container } from '../../container'; import { GitCommit, GitRevisionReference, GitStashCommit, Repository } from '../../git/models'; -import { CommandQuickPickItem, CommitFilesQuickPickItem, GitCommandQuickPickItem } from '../../quickpicks'; +import { CommitFilesQuickPickItem } from '../../quickpicks/items/commits'; +import { CommandQuickPickItem } from '../../quickpicks/items/common'; +import { GitCommandQuickPickItem } from '../../quickpicks/items/gitCommands'; import { ViewsWithRepositoryFolders } from '../../views/viewBase'; import { PartialStepState, diff --git a/src/commands/git/stash.ts b/src/commands/git/stash.ts index 4bb8e1d..00e49f2 100644 --- a/src/commands/git/stash.ts +++ b/src/commands/git/stash.ts @@ -5,12 +5,13 @@ import { StashApplyError, StashApplyErrorReason } from '../../git/errors'; import { GitReference, GitStashCommit, GitStashReference, Repository } from '../../git/models'; import { Logger } from '../../logger'; import { Messages } from '../../messages'; -import { FlagsQuickPickItem, QuickPickItemOfT } from '../../quickpicks'; -import { Strings } from '../../system'; +import { QuickPickItemOfT } from '../../quickpicks/items/common'; +import { FlagsQuickPickItem } from '../../quickpicks/items/flags'; import { formatPath } from '../../system/formatPath'; +import { pad } from '../../system/string'; import { ViewsWithRepositoryFolders } from '../../views/viewBase'; -import { GitCommandsCommand } from '../gitCommands'; import { GitActions } from '../gitCommands.actions'; +import { getSteps } from '../gitCommands.utils'; import { appendReposToTitle, AsyncStepResultGenerator, @@ -432,7 +433,7 @@ export class StashGitCommand extends QuickCommand { state.reference = result; } - const result = yield* GitCommandsCommand.getSteps( + const result = yield* getSteps( this.container, { command: 'show', @@ -504,7 +505,7 @@ export class StashGitCommand extends QuickCommand { state, context, state.uris != null - ? `${Strings.pad(GlyphChars.Dot, 2, 2)}${ + ? `${pad(GlyphChars.Dot, 2, 2)}${ state.uris.length === 1 ? formatPath(state.uris[0], { fileOnly: true }) : `${state.uris.length} files` diff --git a/src/commands/git/status.ts b/src/commands/git/status.ts index 9c0fb68..6d0328f 100644 --- a/src/commands/git/status.ts +++ b/src/commands/git/status.ts @@ -1,8 +1,9 @@ import { GlyphChars } from '../../constants'; import { Container } from '../../container'; import { GitReference, GitStatus, Repository } from '../../git/models'; -import { CommandQuickPickItem, GitCommandQuickPickItem } from '../../quickpicks'; -import { Strings } from '../../system'; +import { CommandQuickPickItem } from '../../quickpicks/items/common'; +import { GitCommandQuickPickItem } from '../../quickpicks/items/gitCommands'; +import { pad } from '../../system/string'; import { ViewsWithRepositoryFolders } from '../../views/viewBase'; import { PartialStepState, @@ -88,7 +89,7 @@ export class StatusGitCommand extends QuickCommand { context.status = (await state.repo.getStatus())!; if (context.status == null) return; - context.title = `${this.title}${Strings.pad(GlyphChars.Dot, 2, 2)}${GitReference.toString( + context.title = `${this.title}${pad(GlyphChars.Dot, 2, 2)}${GitReference.toString( GitReference.create(context.status.branch, state.repo.path, { refType: 'branch', name: context.status.branch, diff --git a/src/commands/git/switch.ts b/src/commands/git/switch.ts index e18fc1e..acf820b 100644 --- a/src/commands/git/switch.ts +++ b/src/commands/git/switch.ts @@ -2,7 +2,7 @@ import { ProgressLocation, QuickPickItem, window } from 'vscode'; import { BranchSorting } from '../../config'; import { Container } from '../../container'; import { GitReference, Repository } from '../../git/models'; -import { Arrays } from '../../system'; +import { isStringArray } from '../../system/array'; import { ViewsWithRepositoryFolders } from '../../views/viewBase'; import { appendReposToTitle, @@ -104,12 +104,7 @@ export class SwitchGitCommand extends QuickCommand { while (this.canStepsContinue(state)) { context.title = this.title; - if ( - state.counter < 1 || - state.repos == null || - state.repos.length === 0 || - Arrays.isStringArray(state.repos) - ) { + if (state.counter < 1 || state.repos == null || state.repos.length === 0 || isStringArray(state.repos)) { skippedStepOne = false; if (context.repos.length === 1) { skippedStepOne = true; diff --git a/src/commands/git/tag.ts b/src/commands/git/tag.ts index 9e31f46..0bf51be 100644 --- a/src/commands/git/tag.ts +++ b/src/commands/git/tag.ts @@ -1,8 +1,9 @@ import { QuickInputButtons, QuickPickItem } from 'vscode'; import { Container } from '../../container'; import { GitReference, GitTagReference, Repository } from '../../git/models'; -import { FlagsQuickPickItem, QuickPickItemOfT } from '../../quickpicks'; -import { Strings } from '../../system'; +import { QuickPickItemOfT } from '../../quickpicks/items/common'; +import { FlagsQuickPickItem } from '../../quickpicks/items/flags'; +import { pluralize } from '../../system/string'; import { ViewsWithRepositoryFolders } from '../../views/viewBase'; import { appendReposToTitle, @@ -351,10 +352,7 @@ export class TagGitCommand extends QuickCommand { state.references = result; } - context.title = getTitle( - Strings.pluralize('Tag', state.references.length, { only: true }), - state.subcommand, - ); + context.title = getTitle(pluralize('Tag', state.references.length, { only: true }), state.subcommand); const result = yield* this.deleteCommandConfirmStep(state, context); if (result === StepResult.Break) continue; diff --git a/src/commands/gitCommands.actions.ts b/src/commands/gitCommands.actions.ts index e5421ba..d2f35b5 100644 --- a/src/commands/gitCommands.actions.ts +++ b/src/commands/gitCommands.actions.ts @@ -23,7 +23,7 @@ import { GitTagReference, Repository, } from '../git/models'; -import { RepositoryPicker } from '../quickpicks'; +import { RepositoryPicker } from '../quickpicks/repositoryPicker'; import { executeCommand, executeEditorCommand } from '../system/command'; import { findOrOpenEditor, findOrOpenEditors } from '../system/utils'; import { ViewsWithRepositoryFolders } from '../views/viewBase'; diff --git a/src/commands/gitCommands.ts b/src/commands/gitCommands.ts index 7907cc3..b72f610 100644 --- a/src/commands/gitCommands.ts +++ b/src/commands/gitCommands.ts @@ -1,37 +1,37 @@ import { Disposable, InputBox, QuickInputButton, QuickInputButtons, QuickPick, QuickPickItem, window } from 'vscode'; -import { configuration, GitCommandSorting } from '../configuration'; +import { configuration } from '../configuration'; import { Commands } from '../constants'; import type { Container } from '../container'; import { KeyMapping } from '../keyboard'; -import { Directive, DirectiveQuickPickItem } from '../quickpicks'; -import { Usage, WorkspaceState } from '../storage'; +import { Directive, DirectiveQuickPickItem } from '../quickpicks/items/directive'; +import { command } from '../system/command'; import { log } from '../system/decorators/log'; import { isPromise } from '../system/promise'; -import { command, Command, CommandContext } from './base'; -import { BranchGitCommand, BranchGitCommandArgs } from './git/branch'; -import { CherryPickGitCommand, CherryPickGitCommandArgs } from './git/cherry-pick'; -import { CoAuthorsGitCommand, CoAuthorsGitCommandArgs } from './git/coauthors'; -import { FetchGitCommand, FetchGitCommandArgs } from './git/fetch'; -import { LogGitCommand, LogGitCommandArgs } from './git/log'; -import { MergeGitCommand, MergeGitCommandArgs } from './git/merge'; -import { PullGitCommand, PullGitCommandArgs } from './git/pull'; -import { PushGitCommand, PushGitCommandArgs } from './git/push'; -import { RebaseGitCommand, RebaseGitCommandArgs } from './git/rebase'; -import { ResetGitCommand, ResetGitCommandArgs } from './git/reset'; -import { RevertGitCommand, RevertGitCommandArgs } from './git/revert'; -import { SearchGitCommand, SearchGitCommandArgs } from './git/search'; -import { ShowGitCommand, ShowGitCommandArgs } from './git/show'; -import { StashGitCommand, StashGitCommandArgs } from './git/stash'; -import { StatusGitCommand, StatusGitCommandArgs } from './git/status'; -import { SwitchGitCommand, SwitchGitCommandArgs } from './git/switch'; -import { TagGitCommand, TagGitCommandArgs } from './git/tag'; +import { Command, CommandContext } from './base'; +import type { BranchGitCommandArgs } from './git/branch'; +import type { CherryPickGitCommandArgs } from './git/cherry-pick'; +import type { CoAuthorsGitCommandArgs } from './git/coauthors'; +import type { FetchGitCommandArgs } from './git/fetch'; +import type { LogGitCommandArgs } from './git/log'; +import type { MergeGitCommandArgs } from './git/merge'; +import type { PullGitCommandArgs } from './git/pull'; +import type { PushGitCommandArgs } from './git/push'; +import type { RebaseGitCommandArgs } from './git/rebase'; +import type { ResetGitCommandArgs } from './git/reset'; +import type { RevertGitCommandArgs } from './git/revert'; +import type { SearchGitCommandArgs } from './git/search'; +import type { ShowGitCommandArgs } from './git/show'; +import type { StashGitCommandArgs } from './git/stash'; +import type { StatusGitCommandArgs } from './git/status'; +import type { SwitchGitCommandArgs } from './git/switch'; +import type { TagGitCommandArgs } from './git/tag'; +import { PickCommandStep } from './gitCommands.utils'; import { isQuickInputStep, isQuickPickStep, QuickCommand, QuickInputStep, QuickPickStep, - StepGenerator, StepSelection, } from './quickCommand'; import { QuickCommandButtons, ToggleQuickInputButton } from './quickCommand.buttons'; @@ -58,23 +58,8 @@ export type GitCommandsCommandArgs = | SwitchGitCommandArgs | TagGitCommandArgs; -function* nullSteps(): StepGenerator { - /* noop */ -} - @command() export class GitCommandsCommand extends Command { - static getSteps(container: Container, args: GitCommandsCommandArgs, pickedVia: 'menu' | 'command'): StepGenerator { - const commandsStep = new PickCommandStep(container, args); - - const command = commandsStep.find(args.command); - if (command == null) return nullSteps(); - - commandsStep.setCommand(command, pickedVia); - - return command.executeSteps(); - } - private startedWith: 'menu' | 'command' = 'menu'; constructor(private readonly container: Container) { @@ -736,87 +721,3 @@ export class GitCommandsCommand extends Command { } } } - -class PickCommandStep implements QuickPickStep { - readonly buttons = []; - private readonly hiddenItems: QuickCommand[]; - ignoreFocusOut = false; - readonly items: QuickCommand[]; - readonly matchOnDescription = true; - readonly placeholder = 'Choose a git command'; - readonly title = 'GitLens'; - - constructor(private readonly container: Container, args?: GitCommandsCommandArgs) { - this.items = [ - new BranchGitCommand(container, args?.command === 'branch' ? args : undefined), - new CherryPickGitCommand(container, args?.command === 'cherry-pick' ? args : undefined), - new CoAuthorsGitCommand(container, args?.command === 'co-authors' ? args : undefined), - new FetchGitCommand(container, args?.command === 'fetch' ? args : undefined), - new LogGitCommand(container, args?.command === 'log' ? args : undefined), - new MergeGitCommand(container, args?.command === 'merge' ? args : undefined), - new PullGitCommand(container, args?.command === 'pull' ? args : undefined), - new PushGitCommand(container, args?.command === 'push' ? args : undefined), - new RebaseGitCommand(container, args?.command === 'rebase' ? args : undefined), - new ResetGitCommand(container, args?.command === 'reset' ? args : undefined), - new RevertGitCommand(container, args?.command === 'revert' ? args : undefined), - new SearchGitCommand(container, args?.command === 'search' || args?.command === 'grep' ? args : undefined), - new ShowGitCommand(container, args?.command === 'show' ? args : undefined), - new StashGitCommand(container, args?.command === 'stash' ? args : undefined), - new StatusGitCommand(container, args?.command === 'status' ? args : undefined), - new SwitchGitCommand( - container, - args?.command === 'switch' || args?.command === 'checkout' ? args : undefined, - ), - new TagGitCommand(container, args?.command === 'tag' ? args : undefined), - ]; - - if (this.container.config.gitCommands.sortBy === GitCommandSorting.Usage) { - const usage = this.container.storage.getWorkspace(WorkspaceState.GitCommandPaletteUsage); - if (usage != null) { - this.items.sort((a, b) => (usage[b.key] ?? 0) - (usage[a.key] ?? 0)); - } - } - - this.hiddenItems = []; - } - - private _command: QuickCommand | undefined; - get command(): QuickCommand | undefined { - return this._command; - } - - find(commandName: string, fuzzy: boolean = false) { - if (fuzzy) { - const cmd = commandName.toLowerCase(); - return this.items.find(c => c.isFuzzyMatch(cmd)) ?? this.hiddenItems.find(c => c.isFuzzyMatch(cmd)); - } - - return this.items.find(c => c.isMatch(commandName)) ?? this.hiddenItems.find(c => c.isMatch(commandName)); - } - - setCommand(command: QuickCommand | undefined, via: 'menu' | 'command'): void { - if (this._command != null) { - this._command.picked = false; - } - - if (command != null) { - command.picked = true; - command.pickedVia = via; - } - - this._command = command; - if (command != null) { - void this.updateCommandUsage(command.key, Date.now()); - } - } - - private async updateCommandUsage(id: string, timestamp: number) { - let usage = this.container.storage.getWorkspace(WorkspaceState.GitCommandPaletteUsage); - if (usage === undefined) { - usage = Object.create(null) as Usage; - } - - usage[id] = timestamp; - await this.container.storage.storeWorkspace(WorkspaceState.GitCommandPaletteUsage, usage); - } -} diff --git a/src/commands/gitCommands.utils.ts b/src/commands/gitCommands.utils.ts new file mode 100644 index 0000000..1ece78f --- /dev/null +++ b/src/commands/gitCommands.utils.ts @@ -0,0 +1,125 @@ +import { GitCommandSorting } from '../config'; +import type { Container } from '../container'; +import { Usage, WorkspaceState } from '../storage'; +import { BranchGitCommand } from './git/branch'; +import { CherryPickGitCommand } from './git/cherry-pick'; +import { CoAuthorsGitCommand } from './git/coauthors'; +import { FetchGitCommand } from './git/fetch'; +import { LogGitCommand } from './git/log'; +import { MergeGitCommand } from './git/merge'; +import { PullGitCommand } from './git/pull'; +import { PushGitCommand } from './git/push'; +import { RebaseGitCommand } from './git/rebase'; +import { ResetGitCommand } from './git/reset'; +import { RevertGitCommand } from './git/revert'; +import { SearchGitCommand } from './git/search'; +import { ShowGitCommand } from './git/show'; +import { StashGitCommand } from './git/stash'; +import { StatusGitCommand } from './git/status'; +import { SwitchGitCommand } from './git/switch'; +import { TagGitCommand } from './git/tag'; +import type { GitCommandsCommandArgs } from './gitCommands'; +import type { QuickCommand, QuickPickStep, StepGenerator } from './quickCommand'; + +function* nullSteps(): StepGenerator { + /* noop */ +} + +export function getSteps( + container: Container, + args: GitCommandsCommandArgs, + pickedVia: 'menu' | 'command', +): StepGenerator { + const commandsStep = new PickCommandStep(container, args); + + const command = commandsStep.find(args.command); + if (command == null) return nullSteps(); + + commandsStep.setCommand(command, pickedVia); + + return command.executeSteps(); +} + +export class PickCommandStep implements QuickPickStep { + readonly buttons = []; + private readonly hiddenItems: QuickCommand[]; + ignoreFocusOut = false; + readonly items: QuickCommand[]; + readonly matchOnDescription = true; + readonly placeholder = 'Choose a git command'; + readonly title = 'GitLens'; + + constructor(private readonly container: Container, args?: GitCommandsCommandArgs) { + this.items = [ + new BranchGitCommand(container, args?.command === 'branch' ? args : undefined), + new CherryPickGitCommand(container, args?.command === 'cherry-pick' ? args : undefined), + new CoAuthorsGitCommand(container, args?.command === 'co-authors' ? args : undefined), + new FetchGitCommand(container, args?.command === 'fetch' ? args : undefined), + new LogGitCommand(container, args?.command === 'log' ? args : undefined), + new MergeGitCommand(container, args?.command === 'merge' ? args : undefined), + new PullGitCommand(container, args?.command === 'pull' ? args : undefined), + new PushGitCommand(container, args?.command === 'push' ? args : undefined), + new RebaseGitCommand(container, args?.command === 'rebase' ? args : undefined), + new ResetGitCommand(container, args?.command === 'reset' ? args : undefined), + new RevertGitCommand(container, args?.command === 'revert' ? args : undefined), + new SearchGitCommand(container, args?.command === 'search' || args?.command === 'grep' ? args : undefined), + new ShowGitCommand(container, args?.command === 'show' ? args : undefined), + new StashGitCommand(container, args?.command === 'stash' ? args : undefined), + new StatusGitCommand(container, args?.command === 'status' ? args : undefined), + new SwitchGitCommand( + container, + args?.command === 'switch' || args?.command === 'checkout' ? args : undefined, + ), + new TagGitCommand(container, args?.command === 'tag' ? args : undefined), + ]; + + if (this.container.config.gitCommands.sortBy === GitCommandSorting.Usage) { + const usage = this.container.storage.getWorkspace(WorkspaceState.GitCommandPaletteUsage); + if (usage != null) { + this.items.sort((a, b) => (usage[b.key] ?? 0) - (usage[a.key] ?? 0)); + } + } + + this.hiddenItems = []; + } + + private _command: QuickCommand | undefined; + get command(): QuickCommand | undefined { + return this._command; + } + + find(commandName: string, fuzzy: boolean = false) { + if (fuzzy) { + const cmd = commandName.toLowerCase(); + return this.items.find(c => c.isFuzzyMatch(cmd)) ?? this.hiddenItems.find(c => c.isFuzzyMatch(cmd)); + } + + return this.items.find(c => c.isMatch(commandName)) ?? this.hiddenItems.find(c => c.isMatch(commandName)); + } + + setCommand(command: QuickCommand | undefined, via: 'menu' | 'command'): void { + if (this._command != null) { + this._command.picked = false; + } + + if (command != null) { + command.picked = true; + command.pickedVia = via; + } + + this._command = command; + if (command != null) { + void this.updateCommandUsage(command.key, Date.now()); + } + } + + private async updateCommandUsage(id: string, timestamp: number) { + let usage = this.container.storage.getWorkspace(WorkspaceState.GitCommandPaletteUsage); + if (usage === undefined) { + usage = Object.create(null) as Usage; + } + + usage[id] = timestamp; + await this.container.storage.storeWorkspace(WorkspaceState.GitCommandPaletteUsage, usage); + } +} diff --git a/src/commands/inviteToLiveShare.ts b/src/commands/inviteToLiveShare.ts index 402574a..8dc564a 100644 --- a/src/commands/inviteToLiveShare.ts +++ b/src/commands/inviteToLiveShare.ts @@ -1,6 +1,7 @@ import { Commands } from '../constants'; import type { Container } from '../container'; -import { command, Command, CommandContext, isCommandContextViewNodeHasContributor } from './base'; +import { command } from '../system/command'; +import { Command, CommandContext, isCommandContextViewNodeHasContributor } from './base'; export interface InviteToLiveShareCommandArgs { email?: string; diff --git a/src/commands/logging.ts b/src/commands/logging.ts index 5327ebc..3ec8fe1 100644 --- a/src/commands/logging.ts +++ b/src/commands/logging.ts @@ -1,7 +1,8 @@ import { configuration, OutputLevel } from '../configuration'; import { Commands } from '../constants'; import type { Container } from '../container'; -import { command, Command } from './base'; +import { command } from '../system/command'; +import { Command } from './base'; @command() export class EnableDebugLoggingCommand extends Command { diff --git a/src/commands/openAssociatedPullRequestOnRemote.ts b/src/commands/openAssociatedPullRequestOnRemote.ts index bd87c4e..9ecd867 100644 --- a/src/commands/openAssociatedPullRequestOnRemote.ts +++ b/src/commands/openAssociatedPullRequestOnRemote.ts @@ -3,8 +3,8 @@ import { Commands } from '../constants'; import type { Container } from '../container'; import { GitUri } from '../git/gitUri'; import { Logger } from '../logger'; -import { executeCommand } from '../system/command'; -import { ActiveEditorCommand, command, getCommandUri } from './base'; +import { command, executeCommand } from '../system/command'; +import { ActiveEditorCommand, getCommandUri } from './base'; import { OpenPullRequestOnRemoteCommandArgs } from './openPullRequestOnRemote'; @command() diff --git a/src/commands/openBranchOnRemote.ts b/src/commands/openBranchOnRemote.ts index da0b16d..59bd54e 100644 --- a/src/commands/openBranchOnRemote.ts +++ b/src/commands/openBranchOnRemote.ts @@ -4,9 +4,11 @@ import type { Container } from '../container'; import { GitUri } from '../git/gitUri'; import { RemoteResourceType } from '../git/remotes/provider'; import { Logger } from '../logger'; -import { CommandQuickPickItem, ReferencePicker, ReferencesQuickPickIncludes, RepositoryPicker } from '../quickpicks'; -import { executeCommand } from '../system/command'; -import { ActiveEditorCommand, command, CommandContext, getCommandUri, isCommandContextViewNodeHasBranch } from './base'; +import { CommandQuickPickItem } from '../quickpicks/items/common'; +import { ReferencePicker, ReferencesQuickPickIncludes } from '../quickpicks/referencePicker'; +import { RepositoryPicker } from '../quickpicks/repositoryPicker'; +import { command, executeCommand } from '../system/command'; +import { ActiveEditorCommand, CommandContext, getCommandUri, isCommandContextViewNodeHasBranch } from './base'; import { OpenOnRemoteCommandArgs } from './openOnRemote'; export interface OpenBranchOnRemoteCommandArgs { diff --git a/src/commands/openBranchesOnRemote.ts b/src/commands/openBranchesOnRemote.ts index 565a8c7..996e60e 100644 --- a/src/commands/openBranchesOnRemote.ts +++ b/src/commands/openBranchesOnRemote.ts @@ -4,9 +4,9 @@ import type { Container } from '../container'; import { GitUri } from '../git/gitUri'; import { RemoteResourceType } from '../git/remotes/provider'; import { Logger } from '../logger'; -import { RepositoryPicker } from '../quickpicks'; -import { executeCommand } from '../system/command'; -import { ActiveEditorCommand, command, CommandContext, getCommandUri, isCommandContextViewNodeHasRemote } from './base'; +import { RepositoryPicker } from '../quickpicks/repositoryPicker'; +import { command, executeCommand } from '../system/command'; +import { ActiveEditorCommand, CommandContext, getCommandUri, isCommandContextViewNodeHasRemote } from './base'; import { OpenOnRemoteCommandArgs } from './openOnRemote'; export interface OpenBranchesOnRemoteCommandArgs { diff --git a/src/commands/openChangedFiles.ts b/src/commands/openChangedFiles.ts index 2e32727..fa8e4e3 100644 --- a/src/commands/openChangedFiles.ts +++ b/src/commands/openChangedFiles.ts @@ -3,10 +3,11 @@ import { Commands } from '../constants'; import type { Container } from '../container'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { RepositoryPicker } from '../quickpicks'; -import { Arrays } from '../system'; +import { RepositoryPicker } from '../quickpicks/repositoryPicker'; +import { filterMap } from '../system/array'; +import { command } from '../system/command'; import { findOrOpenEditors } from '../system/utils'; -import { Command, command } from './base'; +import { Command } from './base'; export interface OpenChangedFilesCommandArgs { uris?: Uri[]; @@ -33,7 +34,7 @@ export class OpenChangedFilesCommand extends Command { return; } - args.uris = Arrays.filterMap(status.files, f => (f.status !== 'D' ? f.uri : undefined)); + args.uris = filterMap(status.files, f => (f.status !== 'D' ? f.uri : undefined)); } findOrOpenEditors(args.uris); diff --git a/src/commands/openCommitOnRemote.ts b/src/commands/openCommitOnRemote.ts index 98c0784..49473cb 100644 --- a/src/commands/openCommitOnRemote.ts +++ b/src/commands/openCommitOnRemote.ts @@ -6,10 +6,9 @@ import { GitRevision } from '../git/models'; import { RemoteResourceType } from '../git/remotes/provider'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { executeCommand } from '../system/command'; +import { command, executeCommand } from '../system/command'; import { ActiveEditorCommand, - command, CommandContext, getCommandUri, isCommandContextGitTimelineItem, diff --git a/src/commands/openComparisonOnRemote.ts b/src/commands/openComparisonOnRemote.ts index 536116d..9d1f8a8 100644 --- a/src/commands/openComparisonOnRemote.ts +++ b/src/commands/openComparisonOnRemote.ts @@ -3,9 +3,9 @@ import { Commands } from '../constants'; import type { Container } from '../container'; import { RemoteResourceType } from '../git/remotes/provider'; import { Logger } from '../logger'; -import { executeCommand } from '../system/command'; +import { command, executeCommand } from '../system/command'; import { ResultsCommitsNode } from '../views/nodes'; -import { Command, command, CommandContext } from './base'; +import { Command, CommandContext } from './base'; import { OpenOnRemoteCommandArgs } from './openOnRemote'; export interface OpenComparisonOnRemoteCommandArgs { diff --git a/src/commands/openDirectoryCompare.ts b/src/commands/openDirectoryCompare.ts index 0e45eef..aa078a0 100644 --- a/src/commands/openDirectoryCompare.ts +++ b/src/commands/openDirectoryCompare.ts @@ -4,9 +4,11 @@ import { Commands } from '../constants'; import type { Container } from '../container'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { ReferencePicker, RepositoryPicker } from '../quickpicks'; +import { ReferencePicker } from '../quickpicks/referencePicker'; +import { RepositoryPicker } from '../quickpicks/repositoryPicker'; +import { command } from '../system/command'; import { CompareResultsNode } from '../views/nodes'; -import { ActiveEditorCommand, command, CommandContext, getCommandUri, isCommandContextViewNodeHasRef } from './base'; +import { ActiveEditorCommand, CommandContext, getCommandUri, isCommandContextViewNodeHasRef } from './base'; export interface OpenDirectoryCompareCommandArgs { ref1?: string; diff --git a/src/commands/openFileAtRevision.ts b/src/commands/openFileAtRevision.ts index eb5a3bb..0039aec 100644 --- a/src/commands/openFileAtRevision.ts +++ b/src/commands/openFileAtRevision.ts @@ -6,9 +6,11 @@ import { GitUri } from '../git/gitUri'; import { GitRevision } from '../git/models'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { CommandQuickPickItem, CommitPicker } from '../quickpicks'; -import { Strings } from '../system'; -import { ActiveEditorCommand, command, CommandContext, getCommandUri } from './base'; +import { CommitPicker } from '../quickpicks/commitPicker'; +import { CommandQuickPickItem } from '../quickpicks/items/common'; +import { command } from '../system/command'; +import { pad } from '../system/string'; +import { ActiveEditorCommand, CommandContext, getCommandUri } from './base'; import { GitActions } from './gitCommands.actions'; import { OpenFileAtRevisionFromCommandArgs } from './openFileAtRevisionFrom'; @@ -117,7 +119,7 @@ export class OpenFileAtRevisionCommand extends ActiveEditorCommand { const title = `Open ${ args.annotationType === FileAnnotationType.Blame ? 'Blame' : 'File' - } at Revision${Strings.pad(GlyphChars.Dot, 2, 2)}`; + } at Revision${pad(GlyphChars.Dot, 2, 2)}`; const pick = await CommitPicker.show( log, `${title}${gitUri.getFormattedFileName({ diff --git a/src/commands/openFileAtRevisionFrom.ts b/src/commands/openFileAtRevisionFrom.ts index df440bf..01bdb29 100644 --- a/src/commands/openFileAtRevisionFrom.ts +++ b/src/commands/openFileAtRevisionFrom.ts @@ -5,9 +5,11 @@ import type { Container } from '../container'; import { GitUri } from '../git/gitUri'; import { GitReference } from '../git/models'; import { Messages } from '../messages'; -import { ReferencePicker, StashPicker } from '../quickpicks'; -import { Strings } from '../system'; -import { ActiveEditorCommand, command, getCommandUri } from './base'; +import { StashPicker } from '../quickpicks/commitPicker'; +import { ReferencePicker } from '../quickpicks/referencePicker'; +import { command } from '../system/command'; +import { pad } from '../system/string'; +import { ActiveEditorCommand, getCommandUri } from './base'; import { GitActions } from './gitCommands.actions'; export interface OpenFileAtRevisionFromCommandArgs { @@ -44,7 +46,7 @@ export class OpenFileAtRevisionFromCommand extends ActiveEditorCommand { if (args?.stash) { const path = this.container.git.getRelativePath(gitUri, gitUri.repoPath); - const title = `Open Changes with Stash${Strings.pad(GlyphChars.Dot, 2, 2)}`; + const title = `Open Changes with Stash${pad(GlyphChars.Dot, 2, 2)}`; const pick = await StashPicker.show( this.container.git.getStash(gitUri.repoPath), `${title}${gitUri.getFormattedFileName({ truncateTo: quickPickTitleMaxChars - title.length })}`, @@ -56,7 +58,7 @@ export class OpenFileAtRevisionFromCommand extends ActiveEditorCommand { args.reference = pick; } else { - const title = `Open File at Branch or Tag${Strings.pad(GlyphChars.Dot, 2, 2)}`; + const title = `Open File at Branch or Tag${pad(GlyphChars.Dot, 2, 2)}`; const pick = await ReferencePicker.show( gitUri.repoPath, `${title}${gitUri.getFormattedFileName({ truncateTo: quickPickTitleMaxChars - title.length })}`, diff --git a/src/commands/openFileFromRemote.ts b/src/commands/openFileFromRemote.ts index 67e800a..b5d6238 100644 --- a/src/commands/openFileFromRemote.ts +++ b/src/commands/openFileFromRemote.ts @@ -1,8 +1,9 @@ import { env, Range, Uri, window } from 'vscode'; import { Commands } from '../constants'; import type { Container } from '../container'; +import { command } from '../system/command'; import { openEditor } from '../system/utils'; -import { command, Command } from './base'; +import { Command } from './base'; @command() export class OpenFileFromRemoteCommand extends Command { diff --git a/src/commands/openFileOnRemote.ts b/src/commands/openFileOnRemote.ts index b7b901e..8267896 100644 --- a/src/commands/openFileOnRemote.ts +++ b/src/commands/openFileOnRemote.ts @@ -7,13 +7,12 @@ import { GitUri } from '../git/gitUri'; import { GitBranch, GitRevision } from '../git/models'; import { RemoteResourceType } from '../git/remotes/provider'; import { Logger } from '../logger'; -import { ReferencePicker } from '../quickpicks'; -import { Strings } from '../system'; -import { executeCommand } from '../system/command'; +import { ReferencePicker } from '../quickpicks/referencePicker'; +import { command, executeCommand } from '../system/command'; +import { pad, splitSingle } from '../system/string'; import { StatusFileNode } from '../views/nodes'; import { ActiveEditorCommand, - command, CommandContext, getCommandUri, isCommandContextViewNodeHasBranch, @@ -127,7 +126,7 @@ export class OpenFileOnRemoteCommand extends ActiveEditorCommand { let sha = args.sha ?? gitUri.sha; if (args.branchOrTag == null && sha != null && !GitRevision.isSha(sha) && remotes.length !== 0) { - const [remoteName, branchName] = Strings.splitSingle(sha, '/'); + const [remoteName, branchName] = splitSingle(sha, '/'); if (branchName != null) { const remote = remotes.find(r => r.name === remoteName); if (remote != null) { @@ -149,8 +148,8 @@ export class OpenFileOnRemoteCommand extends ActiveEditorCommand { const pick = await ReferencePicker.show( gitUri.repoPath, args.clipboard - ? `Copy Remote File Url From${Strings.pad(GlyphChars.Dot, 2, 2)}${gitUri.relativePath}` - : `Open File on Remote From${Strings.pad(GlyphChars.Dot, 2, 2)}${gitUri.relativePath}`, + ? `Copy Remote File Url From${pad(GlyphChars.Dot, 2, 2)}${gitUri.relativePath}` + : `Open File on Remote From${pad(GlyphChars.Dot, 2, 2)}${gitUri.relativePath}`, `Choose a branch or tag to ${args.clipboard ? 'copy' : 'open'} the file revision from`, { allowEnteringRefs: true, diff --git a/src/commands/openIssueOnRemote.ts b/src/commands/openIssueOnRemote.ts index 669e408..767f2bc 100644 --- a/src/commands/openIssueOnRemote.ts +++ b/src/commands/openIssueOnRemote.ts @@ -1,8 +1,9 @@ import { env, Uri } from 'vscode'; import { Commands } from '../constants'; import type { Container } from '../container'; +import { command } from '../system/command'; import { AutolinkedItemNode } from '../views/nodes/autolinkedItemNode'; -import { Command, command, CommandContext } from './base'; +import { Command, CommandContext } from './base'; export interface OpenIssueOnRemoteCommandArgs { clipboard?: boolean; diff --git a/src/commands/openOnRemote.ts b/src/commands/openOnRemote.ts index 46069db..a1c57a7 100644 --- a/src/commands/openOnRemote.ts +++ b/src/commands/openOnRemote.ts @@ -4,9 +4,10 @@ import { GitRemote, GitRevision } from '../git/models'; import { RemoteProvider, RemoteResource, RemoteResourceType } from '../git/remotes/provider'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { RemoteProviderPicker } from '../quickpicks'; -import { Strings } from '../system'; -import { Command, command } from './base'; +import { RemoteProviderPicker } from '../quickpicks/remoteProviderPicker'; +import { command } from '../system/command'; +import { pad, splitSingle } from '../system/string'; +import { Command } from './base'; export type OpenOnRemoteCommandArgs = | { @@ -47,7 +48,7 @@ export class OpenOnRemoteCommand extends Command { try { if (args.resource.type === RemoteResourceType.Branch) { // Check to see if the remote is in the branch - const [remoteName, branchName] = Strings.splitSingle(args.resource.branch, '/'); + const [remoteName, branchName] = splitSingle(args.resource.branch, '/'); if (branchName != null) { const remote = remotes.find(r => r.name === remoteName); if (remote != null) { @@ -85,9 +86,11 @@ export class OpenOnRemoteCommand extends Command { switch (args.resource.type) { case RemoteResourceType.Branch: - title = `${ - args.clipboard ? `Copy ${provider} Branch Url` : `Open Branch on ${provider}` - }${Strings.pad(GlyphChars.Dot, 2, 2)}${args.resource.branch}`; + title = `${args.clipboard ? `Copy ${provider} Branch Url` : `Open Branch on ${provider}`}${pad( + GlyphChars.Dot, + 2, + 2, + )}${args.resource.branch}`; break; case RemoteResourceType.Branches: @@ -95,15 +98,17 @@ export class OpenOnRemoteCommand extends Command { break; case RemoteResourceType.Commit: - title = `${ - args.clipboard ? `Copy ${provider} Commit Url` : `Open Commit on ${provider}` - }${Strings.pad(GlyphChars.Dot, 2, 2)}${GitRevision.shorten(args.resource.sha)}`; + title = `${args.clipboard ? `Copy ${provider} Commit Url` : `Open Commit on ${provider}`}${pad( + GlyphChars.Dot, + 2, + 2, + )}${GitRevision.shorten(args.resource.sha)}`; break; case RemoteResourceType.Comparison: title = `${ args.clipboard ? `Copy ${provider} Comparison Url` : `Open Comparison on ${provider}` - }${Strings.pad(GlyphChars.Dot, 2, 2)}${GitRevision.createRange( + }${pad(GlyphChars.Dot, 2, 2)}${GitRevision.createRange( args.resource.base, args.resource.compare, args.resource.notation ?? '...', @@ -118,7 +123,7 @@ export class OpenOnRemoteCommand extends Command { args.clipboard ? `Copy ${provider} Create Pull Request Url` : `Create Pull Request on ${provider}` - }${Strings.pad(GlyphChars.Dot, 2, 2)}${ + }${pad(GlyphChars.Dot, 2, 2)}${ args.resource.base?.branch ? GitRevision.createRange(args.resource.base.branch, args.resource.compare.branch, '...') : args.resource.compare.branch @@ -130,7 +135,7 @@ export class OpenOnRemoteCommand extends Command { break; case RemoteResourceType.File: - title = `${args.clipboard ? `Copy ${provider} File Url` : `Open File on ${provider}`}${Strings.pad( + title = `${args.clipboard ? `Copy ${provider} File Url` : `Open File on ${provider}`}${pad( GlyphChars.Dot, 2, 2, @@ -142,13 +147,11 @@ export class OpenOnRemoteCommand extends Command { break; case RemoteResourceType.Revision: { - title = `${args.clipboard ? `Copy ${provider} File Url` : `Open File on ${provider}`}${Strings.pad( + title = `${args.clipboard ? `Copy ${provider} File Url` : `Open File on ${provider}`}${pad( GlyphChars.Dot, 2, 2, - )}${GitRevision.shorten(args.resource.sha)}${Strings.pad(GlyphChars.Dot, 1, 1)}${ - args.resource.fileName - }`; + )}${GitRevision.shorten(args.resource.sha)}${pad(GlyphChars.Dot, 1, 1)}${args.resource.fileName}`; break; } } diff --git a/src/commands/openPullRequestOnRemote.ts b/src/commands/openPullRequestOnRemote.ts index 68eae05..d2daaac 100644 --- a/src/commands/openPullRequestOnRemote.ts +++ b/src/commands/openPullRequestOnRemote.ts @@ -1,8 +1,9 @@ import { env, Uri } from 'vscode'; import { Commands } from '../constants'; import type { Container } from '../container'; +import { command } from '../system/command'; import { PullRequestNode } from '../views/nodes'; -import { Command, command, CommandContext } from './base'; +import { Command, CommandContext } from './base'; export interface OpenPullRequestOnRemoteCommandArgs { clipboard?: boolean; diff --git a/src/commands/openRepoOnRemote.ts b/src/commands/openRepoOnRemote.ts index cc4126e..3782e44 100644 --- a/src/commands/openRepoOnRemote.ts +++ b/src/commands/openRepoOnRemote.ts @@ -4,9 +4,9 @@ import type { Container } from '../container'; import { GitUri } from '../git/gitUri'; import { RemoteResourceType } from '../git/remotes/provider'; import { Logger } from '../logger'; -import { RepositoryPicker } from '../quickpicks'; -import { executeCommand } from '../system/command'; -import { ActiveEditorCommand, command, CommandContext, getCommandUri, isCommandContextViewNodeHasRemote } from './base'; +import { RepositoryPicker } from '../quickpicks/repositoryPicker'; +import { command, executeCommand } from '../system/command'; +import { ActiveEditorCommand, CommandContext, getCommandUri, isCommandContextViewNodeHasRemote } from './base'; import { OpenOnRemoteCommandArgs } from './openOnRemote'; export interface OpenRepoOnRemoteCommandArgs { diff --git a/src/commands/openRevisionFile.ts b/src/commands/openRevisionFile.ts index d51c374..ed39039 100644 --- a/src/commands/openRevisionFile.ts +++ b/src/commands/openRevisionFile.ts @@ -6,7 +6,8 @@ import { GitUri } from '../git/gitUri'; import { GitRevision } from '../git/models'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { ActiveEditorCommand, command, getCommandUri } from './base'; +import { command } from '../system/command'; +import { ActiveEditorCommand, getCommandUri } from './base'; import { GitActions } from './gitCommands.actions'; export interface OpenRevisionFileCommandArgs { diff --git a/src/commands/openWorkingFile.ts b/src/commands/openWorkingFile.ts index 624fe42..dc2ede7 100644 --- a/src/commands/openWorkingFile.ts +++ b/src/commands/openWorkingFile.ts @@ -5,8 +5,9 @@ import type { Container } from '../container'; import { GitUri } from '../git/gitUri'; import { Logger } from '../logger'; import { Messages } from '../messages'; +import { command } from '../system/command'; import { findOrOpenEditor } from '../system/utils'; -import { ActiveEditorCommand, command, getCommandUri } from './base'; +import { ActiveEditorCommand, getCommandUri } from './base'; export interface OpenWorkingFileCommandArgs { uri?: Uri; diff --git a/src/commands/quickCommand.steps.ts b/src/commands/quickCommand.steps.ts index 930939d..1926bf3 100644 --- a/src/commands/quickCommand.steps.ts +++ b/src/commands/quickCommand.steps.ts @@ -25,8 +25,6 @@ import { import { RemoteResourceType } from '../git/remotes/provider'; import { SearchPattern } from '../git/search'; import { - BranchQuickPickItem, - CommandQuickPickItem, CommitApplyFileChangesCommandQuickPickItem, CommitBrowseRepositoryFromHereCommandQuickPickItem, CommitCompareWithHEADCommandQuickPickItem, @@ -47,21 +45,25 @@ import { CommitOpenFilesCommandQuickPickItem, CommitOpenRevisionCommandQuickPickItem, CommitOpenRevisionsCommandQuickPickItem, - CommitQuickPickItem, CommitRestoreFileChangesCommandQuickPickItem, + OpenChangedFilesCommandQuickPickItem, +} from '../quickpicks/items/commits'; +import { CommandQuickPickItem, QuickPickSeparator } from '../quickpicks/items/common'; +import { Directive, DirectiveQuickPickItem } from '../quickpicks/items/directive'; +import { + BranchQuickPickItem, + CommitQuickPickItem, ContributorQuickPickItem, - CopyRemoteResourceCommandQuickPickItem, - Directive, - DirectiveQuickPickItem, GitCommandQuickPickItem, - OpenChangedFilesCommandQuickPickItem, - OpenRemoteResourceCommandQuickPickItem, - QuickPickSeparator, - ReferencesQuickPickItem, RefQuickPickItem, RepositoryQuickPickItem, TagQuickPickItem, -} from '../quickpicks'; +} from '../quickpicks/items/gitCommands'; +import { ReferencesQuickPickItem } from '../quickpicks/referencePicker'; +import { + CopyRemoteResourceCommandQuickPickItem, + OpenRemoteResourceCommandQuickPickItem, +} from '../quickpicks/remoteProviderPicker'; import { filterMap, intersection, isStringArray } from '../system/array'; import { formatPath } from '../system/formatPath'; import { map } from '../system/iterable'; diff --git a/src/commands/quickCommand.ts b/src/commands/quickCommand.ts index 90b8b99..a399bb2 100644 --- a/src/commands/quickCommand.ts +++ b/src/commands/quickCommand.ts @@ -1,7 +1,7 @@ import { InputBox, QuickInputButton, QuickPick, QuickPickItem } from 'vscode'; import type { Container } from '../container'; import { Keys } from '../keyboard'; -import { Directive, DirectiveQuickPickItem } from '../quickpicks'; +import { Directive, DirectiveQuickPickItem } from '../quickpicks/items/directive'; export * from './quickCommand.buttons'; export * from './quickCommand.steps'; diff --git a/src/commands/rebaseEditor.ts b/src/commands/rebaseEditor.ts index c4b2748..2e1d46f 100644 --- a/src/commands/rebaseEditor.ts +++ b/src/commands/rebaseEditor.ts @@ -1,6 +1,7 @@ import { Commands } from '../constants'; import type { Container } from '../container'; -import { command, Command } from './base'; +import { command } from '../system/command'; +import { Command } from './base'; @command() export class DisableRebaseEditorCommand extends Command { diff --git a/src/commands/refreshHover.ts b/src/commands/refreshHover.ts index 4e9a29a..1d9065b 100644 --- a/src/commands/refreshHover.ts +++ b/src/commands/refreshHover.ts @@ -1,7 +1,7 @@ import { Commands, CoreCommands } from '../constants'; import type { Container } from '../container'; -import { executeCoreCommand } from '../system/command'; -import { command, Command } from './base'; +import { command, executeCoreCommand } from '../system/command'; +import { Command } from './base'; @command() export class RefreshHoverCommand extends Command { diff --git a/src/commands/remoteProviders.ts b/src/commands/remoteProviders.ts index 185597d..fd926e0 100644 --- a/src/commands/remoteProviders.ts +++ b/src/commands/remoteProviders.ts @@ -3,8 +3,9 @@ import type { Container } from '../container'; import { GitCommit, GitRemote, Repository } from '../git/models'; import { RichRemoteProvider } from '../git/remotes/provider'; import { RepositoryPicker } from '../quickpicks/repositoryPicker'; -import { Iterables } from '../system'; -import { command, Command, CommandContext, isCommandContextViewNodeHasRemote } from './base'; +import { command } from '../system/command'; +import { first } from '../system/iterable'; +import { Command, CommandContext, isCommandContextViewNodeHasRemote } from './base'; export interface ConnectRemoteProviderCommandArgs { remote: string; @@ -58,7 +59,7 @@ export class ConnectRemoteProviderCommand extends Command { if (repos.size === 0) return false; if (repos.size === 1) { let repo; - [repo, remote] = Iterables.first(repos); + [repo, remote] = first(repos); repoPath = repo.path; } else { const pick = await RepositoryPicker.show( @@ -149,7 +150,7 @@ export class DisconnectRemoteProviderCommand extends Command { if (repos.size === 0) return undefined; if (repos.size === 1) { let repo; - [repo, remote] = Iterables.first(repos); + [repo, remote] = first(repos); repoPath = repo.path; } else { const pick = await RepositoryPicker.show( diff --git a/src/commands/repositories.ts b/src/commands/repositories.ts index 4edad66..d63507f 100644 --- a/src/commands/repositories.ts +++ b/src/commands/repositories.ts @@ -1,7 +1,8 @@ import { executeGitCommand } from '../commands/gitCommands.actions'; import { Commands } from '../constants'; import type { Container } from '../container'; -import { command, Command } from './base'; +import { command } from '../system/command'; +import { Command } from './base'; @command() export class FetchRepositoriesCommand extends Command { diff --git a/src/commands/resetAvatarCache.ts b/src/commands/resetAvatarCache.ts index 6099c4b..c8e8b9e 100644 --- a/src/commands/resetAvatarCache.ts +++ b/src/commands/resetAvatarCache.ts @@ -1,7 +1,8 @@ import { resetAvatarCache } from '../avatars'; import { Commands } from '../constants'; import type { Container } from '../container'; -import { command, Command } from './base'; +import { command } from '../system/command'; +import { Command } from './base'; @command() export class ResetAvatarCacheCommand extends Command { diff --git a/src/commands/resetSuppressedWarnings.ts b/src/commands/resetSuppressedWarnings.ts index e5bbc34..c264ba4 100644 --- a/src/commands/resetSuppressedWarnings.ts +++ b/src/commands/resetSuppressedWarnings.ts @@ -2,7 +2,8 @@ import { ConfigurationTarget } from 'vscode'; import { configuration } from '../configuration'; import { Commands } from '../constants'; import type { Container } from '../container'; -import { command, Command } from './base'; +import { command } from '../system/command'; +import { Command } from './base'; @command() export class ResetSuppressedWarningsCommand extends Command { diff --git a/src/commands/searchCommits.ts b/src/commands/searchCommits.ts index 0827ac1..dd82990 100644 --- a/src/commands/searchCommits.ts +++ b/src/commands/searchCommits.ts @@ -2,8 +2,9 @@ import { executeGitCommand } from '../commands/gitCommands.actions'; import { Commands } from '../constants'; import type { Container } from '../container'; import { SearchPattern } from '../git/search'; +import { command } from '../system/command'; import { SearchResultsNode } from '../views/nodes'; -import { Command, command, CommandContext, isCommandContextViewNodeHasRepository } from './base'; +import { Command, CommandContext, isCommandContextViewNodeHasRepository } from './base'; export interface SearchCommitsCommandArgs { search?: Partial; diff --git a/src/commands/setViewsLayout.ts b/src/commands/setViewsLayout.ts index 4512197..d732f01 100644 --- a/src/commands/setViewsLayout.ts +++ b/src/commands/setViewsLayout.ts @@ -2,8 +2,8 @@ import { window } from 'vscode'; import { viewsConfigKeys } from '../configuration'; import { Commands, CoreCommands } from '../constants'; import type { Container } from '../container'; -import { executeCommand, executeCoreCommand } from '../system/command'; -import { command, Command } from './base'; +import { command, executeCommand, executeCoreCommand } from '../system/command'; +import { Command } from './base'; enum ViewsLayout { GitLens = 'gitlens', diff --git a/src/commands/showCommitsInView.ts b/src/commands/showCommitsInView.ts index a243c18..fc04abf 100644 --- a/src/commands/showCommitsInView.ts +++ b/src/commands/showCommitsInView.ts @@ -6,8 +6,9 @@ import { GitUri } from '../git/gitUri'; import { SearchPattern } from '../git/search'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { Iterables } from '../system'; -import { ActiveEditorCommand, command, getCommandUri } from './base'; +import { command } from '../system/command'; +import { filterMap } from '../system/iterable'; +import { ActiveEditorCommand, getCommandUri } from './base'; export interface ShowCommitsInViewCommandArgs { refs?: string[]; @@ -45,9 +46,7 @@ export class ShowCommitsInViewCommand extends ActiveEditorCommand { return Messages.showFileNotUnderSourceControlWarningMessage('Unable to find commits'); } - args.refs = [ - ...Iterables.filterMap(blame.commits.values(), c => (c.isUncommitted ? undefined : c.ref)), - ]; + args.refs = [...filterMap(blame.commits.values(), c => (c.isUncommitted ? undefined : c.ref))]; } catch (ex) { Logger.error(ex, 'ShowCommitsInViewCommand', 'getBlameForRange'); return Messages.showGenericErrorMessage('Unable to find commits'); diff --git a/src/commands/showLastQuickPick.ts b/src/commands/showLastQuickPick.ts index f30c2ef..aae65f5 100644 --- a/src/commands/showLastQuickPick.ts +++ b/src/commands/showLastQuickPick.ts @@ -3,7 +3,8 @@ import { Commands } from '../constants'; import type { Container } from '../container'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { command, Command, getLastCommand } from './base'; +import { command } from '../system/command'; +import { Command, getLastCommand } from './base'; @command() export class ShowLastQuickPickCommand extends Command { diff --git a/src/commands/showQuickBranchHistory.ts b/src/commands/showQuickBranchHistory.ts index 6d557fb..de1ab7b 100644 --- a/src/commands/showQuickBranchHistory.ts +++ b/src/commands/showQuickBranchHistory.ts @@ -3,7 +3,8 @@ import { Commands } from '../constants'; import type { Container } from '../container'; import { GitUri } from '../git/gitUri'; import { GitReference } from '../git/models'; -import { ActiveEditorCachedCommand, command, CommandContext, getCommandUri } from './base'; +import { command } from '../system/command'; +import { ActiveEditorCachedCommand, CommandContext, getCommandUri } from './base'; import { executeGitCommand } from './gitCommands.actions'; export interface ShowQuickBranchHistoryCommandArgs { diff --git a/src/commands/showQuickCommit.ts b/src/commands/showQuickCommit.ts index 9315a8b..307d99e 100644 --- a/src/commands/showQuickCommit.ts +++ b/src/commands/showQuickCommit.ts @@ -5,13 +5,8 @@ import { GitUri } from '../git/gitUri'; import { GitCommit, GitLog, GitStashCommit } from '../git/models'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { - ActiveEditorCachedCommand, - command, - CommandContext, - getCommandUri, - isCommandContextViewNodeHasCommit, -} from './base'; +import { command } from '../system/command'; +import { ActiveEditorCachedCommand, CommandContext, getCommandUri, isCommandContextViewNodeHasCommit } from './base'; import { executeGitCommand, GitActions } from './gitCommands.actions'; export interface ShowQuickCommitCommandArgs { diff --git a/src/commands/showQuickCommitFile.ts b/src/commands/showQuickCommitFile.ts index 8811de2..638f6cf 100644 --- a/src/commands/showQuickCommitFile.ts +++ b/src/commands/showQuickCommitFile.ts @@ -5,13 +5,8 @@ import { GitUri } from '../git/gitUri'; import { GitCommit, GitLog, GitStashCommit } from '../git/models'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { - ActiveEditorCachedCommand, - command, - CommandContext, - getCommandUri, - isCommandContextViewNodeHasCommit, -} from './base'; +import { command } from '../system/command'; +import { ActiveEditorCachedCommand, CommandContext, getCommandUri, isCommandContextViewNodeHasCommit } from './base'; import { executeGitCommand } from './gitCommands.actions'; export interface ShowQuickCommitFileCommandArgs { diff --git a/src/commands/showQuickFileHistory.ts b/src/commands/showQuickFileHistory.ts index 8d238bd..23cb10a 100644 --- a/src/commands/showQuickFileHistory.ts +++ b/src/commands/showQuickFileHistory.ts @@ -3,8 +3,9 @@ import { Commands } from '../constants'; import type { Container } from '../container'; import { GitUri } from '../git/gitUri'; import { GitBranch, GitLog, GitReference, GitTag } from '../git/models'; -import { CommandQuickPickItem } from '../quickpicks'; -import { ActiveEditorCachedCommand, command, CommandContext, getCommandUri } from './base'; +import { CommandQuickPickItem } from '../quickpicks/items/common'; +import { command } from '../system/command'; +import { ActiveEditorCachedCommand, CommandContext, getCommandUri } from './base'; import { executeGitCommand } from './gitCommands.actions'; export interface ShowQuickFileHistoryCommandArgs { diff --git a/src/commands/showQuickRepoStatus.ts b/src/commands/showQuickRepoStatus.ts index b71f44b..c0babc4 100644 --- a/src/commands/showQuickRepoStatus.ts +++ b/src/commands/showQuickRepoStatus.ts @@ -1,7 +1,8 @@ import { executeGitCommand } from '../commands/gitCommands.actions'; import { Commands } from '../constants'; import type { Container } from '../container'; -import { Command, command } from './base'; +import { command } from '../system/command'; +import { Command } from './base'; export interface ShowQuickRepoStatusCommandArgs { repoPath?: string; diff --git a/src/commands/showQuickStashList.ts b/src/commands/showQuickStashList.ts index c29da89..346a840 100644 --- a/src/commands/showQuickStashList.ts +++ b/src/commands/showQuickStashList.ts @@ -1,7 +1,8 @@ import { executeGitCommand } from '../commands/gitCommands.actions'; import { Commands } from '../constants'; import type { Container } from '../container'; -import { Command, command } from './base'; +import { command } from '../system/command'; +import { Command } from './base'; export interface ShowQuickStashListCommandArgs { repoPath?: string; diff --git a/src/commands/showView.ts b/src/commands/showView.ts index 57128b7..c7465e8 100644 --- a/src/commands/showView.ts +++ b/src/commands/showView.ts @@ -2,8 +2,8 @@ import { Commands, ContextKeys } from '../constants'; import type { Container } from '../container'; import { setContext } from '../context'; import { SyncedState } from '../storage'; -import { executeCommand } from '../system/command'; -import { command, Command, CommandContext } from './base'; +import { command, executeCommand } from '../system/command'; +import { Command, CommandContext } from './base'; @command() export class ShowViewCommand extends Command { diff --git a/src/commands/stashApply.ts b/src/commands/stashApply.ts index 868ec3d..7e929df 100644 --- a/src/commands/stashApply.ts +++ b/src/commands/stashApply.ts @@ -2,9 +2,9 @@ import { GitActions } from '../commands/gitCommands.actions'; import { Commands } from '../constants'; import type { Container } from '../container'; import { GitStashCommit, GitStashReference } from '../git/models'; -import { CommandQuickPickItem } from '../quickpicks'; +import { CommandQuickPickItem } from '../quickpicks/items/common'; +import { command } from '../system/command'; import { - command, Command, CommandContext, isCommandContextViewNodeHasCommit, diff --git a/src/commands/stashSave.ts b/src/commands/stashSave.ts index cd0d13b..e9a4c9a 100644 --- a/src/commands/stashSave.ts +++ b/src/commands/stashSave.ts @@ -5,9 +5,9 @@ import { GitActions } from '../commands/gitCommands.actions'; import { Commands } from '../constants'; import type { Container } from '../container'; import { GitUri } from '../git/gitUri'; +import { command } from '../system/command'; import { Command, - command, CommandContext, isCommandContextViewNodeHasFile, isCommandContextViewNodeHasRepoPath, diff --git a/src/commands/switchMode.ts b/src/commands/switchMode.ts index a1d952c..fb1d9bc 100644 --- a/src/commands/switchMode.ts +++ b/src/commands/switchMode.ts @@ -3,9 +3,10 @@ import { configuration } from '../configuration'; import { Commands } from '../constants'; import type { Container } from '../container'; import { Logger } from '../logger'; -import { ModePicker } from '../quickpicks'; -import { log } from '../system'; -import { command, Command } from './base'; +import { ModePicker } from '../quickpicks/modePicker'; +import { command } from '../system/command'; +import { log } from '../system/decorators/log'; +import { Command } from './base'; @command() export class SwitchModeCommand extends Command { diff --git a/src/commands/toggleCodeLens.ts b/src/commands/toggleCodeLens.ts index e57b986..fdffd11 100644 --- a/src/commands/toggleCodeLens.ts +++ b/src/commands/toggleCodeLens.ts @@ -1,6 +1,7 @@ import { Commands } from '../constants'; import type { Container } from '../container'; -import { command, Command } from './base'; +import { command } from '../system/command'; +import { Command } from './base'; @command() export class ToggleCodeLensCommand extends Command { diff --git a/src/commands/toggleFileAnnotations.ts b/src/commands/toggleFileAnnotations.ts index 8029502..97423b3 100644 --- a/src/commands/toggleFileAnnotations.ts +++ b/src/commands/toggleFileAnnotations.ts @@ -7,8 +7,9 @@ import { Commands } from '../constants'; import type { Container } from '../container'; import { Logger } from '../logger'; import { Messages } from '../messages'; +import { command } from '../system/command'; import { isTextEditor } from '../system/utils'; -import { ActiveEditorCommand, command, EditorCommand } from './base'; +import { ActiveEditorCommand, EditorCommand } from './base'; @command() export class ClearFileAnnotationsCommand extends EditorCommand { diff --git a/src/commands/toggleLineBlame.ts b/src/commands/toggleLineBlame.ts index ce5e820..e6e9892 100644 --- a/src/commands/toggleLineBlame.ts +++ b/src/commands/toggleLineBlame.ts @@ -2,7 +2,8 @@ import { TextEditor, Uri, window } from 'vscode'; import { Commands } from '../constants'; import type { Container } from '../container'; import { Logger } from '../logger'; -import { ActiveEditorCommand, command } from './base'; +import { command } from '../system/command'; +import { ActiveEditorCommand } from './base'; @command() export class ToggleLineBlameCommand extends ActiveEditorCommand { diff --git a/src/constants.ts b/src/constants.ts index 2aad3c1..14db104 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,3 +1,24 @@ +declare global { + export type PartialDeep = T extends Record ? { [K in keyof T]?: PartialDeep } : T; + export type PickPartialDeep = Omit, K> & { [P in K]?: Partial }; + + export type Mutable = { -readonly [P in keyof T]: T[P] }; + export type PickMutable = Omit & { -readonly [P in K]: T[P] }; + + export type ExcludeSome = Omit & { [P in K]-?: Exclude }; + + export type ExtractAll = { [K in keyof T]: T[K] extends U ? T[K] : never }; + export type ExtractSome = Omit & { [P in K]-?: Extract }; + + export type RequireSome = Omit & { [P in K]-?: T[P] }; + + export type AllNonNullable = { [P in keyof T]-?: NonNullable }; + export type SomeNonNullable = Omit & { [P in K]-?: NonNullable }; + + export type NarrowRepo = ExcludeSome; + export type NarrowRepos = ExcludeSome; +} + export const quickPickTitleMaxChars = 80; export const ImageMimetypes: Record = { '.png': 'image/png', diff --git a/src/extension.ts b/src/extension.ts index 0770500..ea100de 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -3,7 +3,6 @@ import { isWeb } from '@env/platform'; import { Api } from './api/api'; import type { CreatePullRequestActionContext, GitLensApi, OpenPullRequestActionContext } from './api/gitlens'; import type { CreatePullRequestOnRemoteCommandArgs, OpenPullRequestOnRemoteCommandArgs } from './commands'; -import { registerCommands } from './commands/base'; import { configuration, Configuration, OutputLevel } from './configuration'; import { Commands, ContextKeys } from './constants'; import { Container } from './container'; @@ -14,7 +13,7 @@ import { Logger, LogLevel } from './logger'; import { Messages } from './messages'; import { registerPartnerActionRunners } from './partners'; import { GlobalState, SyncedState } from './storage'; -import { executeCommand } from './system/command'; +import { executeCommand, registerCommands } from './system/command'; import { once } from './system/event'; import { Stopwatch } from './system/stopwatch'; import { compare } from './system/version'; diff --git a/src/git/formatters/commitFormatter.ts b/src/git/formatters/commitFormatter.ts index 605d357..f53ff90 100644 --- a/src/git/formatters/commitFormatter.ts +++ b/src/git/formatters/commitFormatter.ts @@ -1,5 +1,10 @@ import { Uri } from 'vscode'; -import type { HoverCommandsActionContext, OpenPullRequestActionContext } from '../../api/gitlens'; +import type { + Action, + ActionContext, + HoverCommandsActionContext, + OpenPullRequestActionContext, +} from '../../api/gitlens'; import { getPresenceDataUri } from '../../avatars'; import { ConnectRemoteProviderCommand, @@ -9,11 +14,11 @@ import { ShowQuickCommitCommand, ShowQuickCommitFileCommand, } from '../../commands'; +import { Command } from '../../commands/base'; import { DateStyle, FileAnnotationType } from '../../configuration'; import { Commands, GlyphChars } from '../../constants'; import { Container } from '../../container'; import { emojify } from '../../emojis'; -import { getMarkdownActionCommand } from '../../system/command'; import { join, map } from '../../system/iterable'; import { PromiseCancelledError } from '../../system/promise'; import { escapeMarkdown, getSuperscript, TokenOptions } from '../../system/string'; @@ -662,3 +667,10 @@ export class CommitFormatter extends Formatter { return super.has(template, ...tokens); } } + +function getMarkdownActionCommand(action: Action, args: Omit): string { + return Command.getMarkdownCommandArgsCore(`${Commands.ActionPrefix}${action}`, { + ...args, + type: action, + }); +} diff --git a/src/git/formatters/formatter.ts b/src/git/formatters/formatter.ts index 96b4744..1350045 100644 --- a/src/git/formatters/formatter.ts +++ b/src/git/formatters/formatter.ts @@ -1,10 +1,17 @@ -import { Strings } from '../../system'; - -const emptyStr = ''; +import { + getTokensFromTemplate, + getWidth, + interpolate, + interpolateAsync, + padLeft, + padRight, + TokenOptions, + truncate, +} from '../../system/string'; export interface FormatOptions { dateFormat?: string | null; - tokenOptions?: Record; + tokenOptions?: Record; } type Constructor> = new (...args: any[]) => T; @@ -46,7 +53,7 @@ export abstract class Formatter 0) { if (options.collapseWhitespace) { @@ -75,20 +82,20 @@ export abstract class Formatter((map, token) => { map[token.key] = token.options; return map; @@ -133,7 +140,7 @@ export abstract class Formatter { // Preserve spaces template = template.replace(spaceReplacementRegex, '\u00a0'); - if (formatter instanceof Formatter) return Strings.interpolateAsync(template, formatter); + if (formatter instanceof Formatter) return interpolateAsync(template, formatter); let options: Options | undefined = undefined; if (dateFormatOrOptions == null || typeof dateFormatOrOptions === 'string') { @@ -161,8 +168,8 @@ export abstract class Formatter((map, token) => { map[token.key] = token.options; return map; @@ -177,7 +184,7 @@ export abstract class Formatter( diff --git a/src/git/parsers/branchParser.ts b/src/git/parsers/branchParser.ts index fdb8ff0..8ba3cd6 100644 --- a/src/git/parsers/branchParser.ts +++ b/src/git/parsers/branchParser.ts @@ -1,4 +1,4 @@ -import { debug } from '../../system'; +import { debug } from '../../system/decorators/log'; import { GitBranch } from '../models/branch'; const branchWithTrackingRegex = diff --git a/src/git/parsers/logParser.ts b/src/git/parsers/logParser.ts index 10a6f3d..03e8ff5 100644 --- a/src/git/parsers/logParser.ts +++ b/src/git/parsers/logParser.ts @@ -1,6 +1,7 @@ import { Range } from 'vscode'; import type { Container } from '../../container'; -import { Arrays, debug } from '../../system'; +import { filterMap } from '../../system/array'; +import { debug } from '../../system/decorators/log'; import { normalizePath, relative } from '../../system/path'; import { getLines } from '../../system/string'; import { @@ -525,7 +526,7 @@ export class GitLogParser { } if (entry.files !== undefined) { - entry.path = Arrays.filterMap(entry.files, f => (f.path ? f.path : undefined)).join(', '); + entry.path = filterMap(entry.files, f => (f.path ? f.path : undefined)).join(', '); } if (first && repoPath === undefined && type === LogType.LogFile && fileName !== undefined) { diff --git a/src/git/parsers/reflogParser.ts b/src/git/parsers/reflogParser.ts index f294a09..fd329d8 100644 --- a/src/git/parsers/reflogParser.ts +++ b/src/git/parsers/reflogParser.ts @@ -1,4 +1,4 @@ -import { debug } from '../../system'; +import { debug } from '../../system/decorators/log'; import { GitReflog, GitReflogRecord } from '../models/reflog'; const reflogRegex = /^(.+)(.+?)@{(.+)}(\w*)(.*?)(?::(.*))?$/gm; diff --git a/src/git/parsers/remoteParser.ts b/src/git/parsers/remoteParser.ts index f44b931..44f9a98 100644 --- a/src/git/parsers/remoteParser.ts +++ b/src/git/parsers/remoteParser.ts @@ -1,4 +1,4 @@ -import { debug } from '../../system'; +import { debug } from '../../system/decorators/log'; import { GitRemote } from '../models'; import { GitRemoteType } from '../models/remote'; import { RemoteProvider } from '../remotes/provider'; diff --git a/src/git/parsers/tagParser.ts b/src/git/parsers/tagParser.ts index 32efc8b..b3c895e 100644 --- a/src/git/parsers/tagParser.ts +++ b/src/git/parsers/tagParser.ts @@ -1,4 +1,4 @@ -import { debug } from '../../system'; +import { debug } from '../../system/decorators/log'; import { GitTag } from '../models'; const tagRegex = /^(.+)<\*r>(.*)(.*)(.*)(.*)(.*)$/gm; diff --git a/src/git/parsers/treeParser.ts b/src/git/parsers/treeParser.ts index 20c613f..8174d49 100644 --- a/src/git/parsers/treeParser.ts +++ b/src/git/parsers/treeParser.ts @@ -1,4 +1,4 @@ -import { debug } from '../../system'; +import { debug } from '../../system/decorators/log'; import { GitTreeEntry } from '../models'; const emptyStr = ''; diff --git a/src/git/remotes/custom.ts b/src/git/remotes/custom.ts index da3e99d..d2d1a96 100644 --- a/src/git/remotes/custom.ts +++ b/src/git/remotes/custom.ts @@ -1,6 +1,6 @@ import { Range, Uri } from 'vscode'; import { RemotesUrlsConfig } from '../../configuration'; -import { Strings } from '../../system'; +import { interpolate } from '../../system/string'; import { Repository } from '../models/repository'; import { RemoteProvider } from './provider'; @@ -28,29 +28,26 @@ export class CustomRemote extends RemoteProvider { } protected override getUrlForRepository(): string { - return this.encodeUrl(Strings.interpolate(this.urls.repository, this.getContext())); + return this.encodeUrl(interpolate(this.urls.repository, this.getContext())); } protected getUrlForBranches(): string { - return this.encodeUrl(Strings.interpolate(this.urls.branches, this.getContext())); + return this.encodeUrl(interpolate(this.urls.branches, this.getContext())); } protected getUrlForBranch(branch: string): string { - return this.encodeUrl(Strings.interpolate(this.urls.branch, this.getContext({ branch: branch }))); + return this.encodeUrl(interpolate(this.urls.branch, this.getContext({ branch: branch }))); } protected getUrlForCommit(sha: string): string { - return this.encodeUrl(Strings.interpolate(this.urls.commit, this.getContext({ id: sha }))); + return this.encodeUrl(interpolate(this.urls.commit, this.getContext({ id: sha }))); } protected override getUrlForComparison(base: string, compare: string, notation: '..' | '...'): string | undefined { if (this.urls.comparison == null) return undefined; return this.encodeUrl( - Strings.interpolate( - this.urls.comparison, - this.getContext({ ref1: base, ref2: compare, notation: notation }), - ), + interpolate(this.urls.comparison, this.getContext({ ref1: base, ref2: compare, notation: notation })), ); } @@ -58,9 +55,9 @@ export class CustomRemote extends RemoteProvider { let line; if (range != null) { if (range.start.line === range.end.line) { - line = Strings.interpolate(this.urls.fileLine, { line: range.start.line }); + line = interpolate(this.urls.fileLine, { line: range.start.line }); } else { - line = Strings.interpolate(this.urls.fileRange, { start: range.start.line, end: range.end.line }); + line = interpolate(this.urls.fileRange, { start: range.start.line, end: range.end.line }); } } else { line = ''; @@ -68,14 +65,11 @@ export class CustomRemote extends RemoteProvider { let url; if (sha) { - url = Strings.interpolate(this.urls.fileInCommit, this.getContext({ id: sha, file: fileName, line: line })); + url = interpolate(this.urls.fileInCommit, this.getContext({ id: sha, file: fileName, line: line })); } else if (branch) { - url = Strings.interpolate( - this.urls.fileInBranch, - this.getContext({ branch: branch, file: fileName, line: line }), - ); + url = interpolate(this.urls.fileInBranch, this.getContext({ branch: branch, file: fileName, line: line })); } else { - url = Strings.interpolate(this.urls.file, this.getContext({ file: fileName, line: line })); + url = interpolate(this.urls.file, this.getContext({ file: fileName, line: line })); } const decodeHash = url.includes('#'); diff --git a/src/quickpicks.ts b/src/quickpicks.ts deleted file mode 100644 index 222b0a0..0000000 --- a/src/quickpicks.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { configuration } from './configuration'; - -export function getQuickPickIgnoreFocusOut() { - return !configuration.get('advanced.quickPick.closeOnFocusOut'); -} - -export * from './quickpicks/quickPicksItems'; -export * from './quickpicks/gitQuickPickItems'; -export * from './quickpicks/commitQuickPickItems'; - -export * from './quickpicks/commitPicker'; -export * from './quickpicks/modePicker'; -export * from './quickpicks/referencePicker'; -export * from './quickpicks/remoteProviderPicker'; -export * from './quickpicks/repositoryPicker'; diff --git a/src/quickpicks/commitPicker.ts b/src/quickpicks/commitPicker.ts index 3438aa8..79650f0 100644 --- a/src/quickpicks/commitPicker.ts +++ b/src/quickpicks/commitPicker.ts @@ -3,15 +3,12 @@ import { configuration } from '../configuration'; import { Container } from '../container'; import { GitCommit, GitLog, GitStash, GitStashCommit } from '../git/models'; import { KeyboardScope, Keys } from '../keyboard'; -import { - CommandQuickPickItem, - CommitQuickPickItem, - Directive, - DirectiveQuickPickItem, - getQuickPickIgnoreFocusOut, -} from '../quickpicks'; +import { CommandQuickPickItem } from '../quickpicks/items/common'; import { filter, map } from '../system/iterable'; import { isPromise } from '../system/promise'; +import { getQuickPickIgnoreFocusOut } from '../system/utils'; +import { Directive, DirectiveQuickPickItem } from './items/directive'; +import { CommitQuickPickItem } from './items/gitCommands'; export namespace CommitPicker { export async function show( diff --git a/src/quickpicks/commitQuickPickItems.ts b/src/quickpicks/commitQuickPickItems.ts deleted file mode 100644 index f13c507..0000000 --- a/src/quickpicks/commitQuickPickItems.ts +++ /dev/null @@ -1,327 +0,0 @@ -import { QuickPickItem, window } from 'vscode'; -import { GitActions } from '../commands/gitCommands.actions'; -import { OpenChangedFilesCommandArgs } from '../commands/openChangedFiles'; -import { QuickCommandButtons } from '../commands/quickCommand.buttons'; -import { Commands, GlyphChars } from '../constants'; -import { Container } from '../container'; -import { CommitFormatter } from '../git/formatters'; -import { GitCommit, GitFile, GitFileChange, GitStatusFile } from '../git/models'; -import { Keys } from '../keyboard'; -import { basename } from '../system/path'; -import { pad } from '../system/string'; -import { CommandQuickPickItem } from './quickPicksItems'; - -export class CommitFilesQuickPickItem extends CommandQuickPickItem { - constructor( - readonly commit: GitCommit, - options?: { - file?: GitFileChange; - unpublished?: boolean | undefined; - picked?: boolean; - hint?: string; - }, - ) { - super( - { - label: commit.summary, - description: `${CommitFormatter.fromTemplate(`\${author}, \${ago} $(git-commit) \${id}`, commit)}${ - options?.unpublished ? ' (unpublished)' : '' - }`, - detail: `${ - options?.file != null - ? `$(file) ${basename(options.file.path)}${options.file.formatStats({ - expand: true, - separator: ', ', - prefix: ` ${GlyphChars.Dot} `, - })}` - : `$(files) ${commit.formatStats({ - expand: true, - separator: ', ', - empty: 'No files changed', - })}` - }${options?.hint != null ? `${pad(GlyphChars.Dash, 4, 2, GlyphChars.Space)}${options.hint}` : ''}`, - alwaysShow: true, - picked: options?.picked ?? true, - buttons: GitCommit.isStash(commit) - ? [QuickCommandButtons.RevealInSideBar] - : [QuickCommandButtons.RevealInSideBar, QuickCommandButtons.SearchInSideBar], - }, - undefined, - undefined, - { suppressKeyPress: true }, - ); - } - - get sha(): string { - return this.commit.sha; - } -} - -export class CommitFileQuickPickItem extends CommandQuickPickItem { - constructor(readonly commit: GitCommit, readonly file: GitFile, picked?: boolean) { - super({ - label: `${pad(GitFile.getStatusCodicon(file.status), 0, 2)}${basename(file.path)}`, - description: GitFile.getFormattedDirectory(file, true), - picked: picked, - }); - - // TODO@eamodio - add line diff details - // this.detail = this.commit.getFormattedDiffStatus({ expand: true }); - } - - get sha(): string { - return this.commit.sha; - } - - override execute(options?: { preserveFocus?: boolean; preview?: boolean }): Promise { - return GitActions.Commit.openChanges(this.file, this.commit, options); - // const fileCommit = await this.commit.getCommitForFile(this.file)!; - - // if (fileCommit.previousSha === undefined) { - // void (await findOrOpenEditor( - // GitUri.toRevisionUri(fileCommit.sha, this.file, fileCommit.repoPath), - // options, - // )); - - // return; - // } - - // const commandArgs: DiffWithPreviousCommandArgs = { - // commit: fileCommit, - // showOptions: options, - // }; - // void (await executeCommand(Commands.DiffWithPrevious, fileCommit.toGitUri(), commandArgs)); - } -} - -export class CommitBrowseRepositoryFromHereCommandQuickPickItem extends CommandQuickPickItem { - constructor( - private readonly commit: GitCommit, - private readonly executeOptions?: { - before?: boolean; - openInNewWindow: boolean; - }, - item?: QuickPickItem, - ) { - super( - item ?? - `$(folder-opened) Browse Repository from${executeOptions?.before ? ' Before' : ''} Here${ - executeOptions?.openInNewWindow ? ' in New Window' : '' - }`, - ); - } - - override execute(_options: { preserveFocus?: boolean; preview?: boolean }): Promise { - return GitActions.browseAtRevision(this.commit.getGitUri(), { - before: this.executeOptions?.before, - openInNewWindow: this.executeOptions?.openInNewWindow, - }); - } -} - -export class CommitCompareWithHEADCommandQuickPickItem extends CommandQuickPickItem { - constructor(private readonly commit: GitCommit, item?: QuickPickItem) { - super(item ?? '$(compare-changes) Compare with HEAD'); - } - - override execute(_options: { preserveFocus?: boolean; preview?: boolean }): Promise { - return Container.instance.searchAndCompareView.compare(this.commit.repoPath, this.commit.ref, 'HEAD'); - } -} - -export class CommitCompareWithWorkingCommandQuickPickItem extends CommandQuickPickItem { - constructor(private readonly commit: GitCommit, item?: QuickPickItem) { - super(item ?? '$(compare-changes) Compare with Working Tree'); - } - - override execute(_options: { preserveFocus?: boolean; preview?: boolean }): Promise { - return Container.instance.searchAndCompareView.compare(this.commit.repoPath, this.commit.ref, ''); - } -} - -export class CommitCopyIdQuickPickItem extends CommandQuickPickItem { - constructor(private readonly commit: GitCommit, item?: QuickPickItem) { - super(item ?? '$(copy) Copy SHA'); - } - - override execute(): Promise { - return GitActions.Commit.copyIdToClipboard(this.commit); - } - - override async onDidPressKey(key: Keys): Promise { - await super.onDidPressKey(key); - void window.showInformationMessage('Commit SHA copied to the clipboard'); - } -} - -export class CommitCopyMessageQuickPickItem extends CommandQuickPickItem { - constructor(private readonly commit: GitCommit, item?: QuickPickItem) { - super(item ?? '$(copy) Copy Message'); - } - - override execute(): Promise { - return GitActions.Commit.copyMessageToClipboard(this.commit); - } - - override async onDidPressKey(key: Keys): Promise { - await super.onDidPressKey(key); - void window.showInformationMessage( - `${this.commit.stashName ? 'Stash' : 'Commit'} Message copied to the clipboard`, - ); - } -} - -export class CommitOpenAllChangesCommandQuickPickItem extends CommandQuickPickItem { - constructor(private readonly commit: GitCommit, item?: QuickPickItem) { - super(item ?? '$(git-compare) Open All Changes'); - } - - override execute(options: { preserveFocus?: boolean; preview?: boolean }): Promise { - return GitActions.Commit.openAllChanges(this.commit, options); - } -} - -export class CommitOpenAllChangesWithDiffToolCommandQuickPickItem extends CommandQuickPickItem { - constructor(private readonly commit: GitCommit, item?: QuickPickItem) { - super(item ?? '$(git-compare) Open All Changes (difftool)'); - } - - override execute(): Promise { - return GitActions.Commit.openAllChangesWithDiffTool(this.commit); - } -} - -export class CommitOpenAllChangesWithWorkingCommandQuickPickItem extends CommandQuickPickItem { - constructor(private readonly commit: GitCommit, item?: QuickPickItem) { - super(item ?? '$(git-compare) Open All Changes with Working Tree'); - } - - override execute(options: { preserveFocus?: boolean; preview?: boolean }): Promise { - return GitActions.Commit.openAllChangesWithWorking(this.commit, options); - } -} - -export class CommitOpenChangesCommandQuickPickItem extends CommandQuickPickItem { - constructor(private readonly commit: GitCommit, private readonly file: string | GitFile, item?: QuickPickItem) { - super(item ?? '$(git-compare) Open Changes'); - } - - override execute(options: { preserveFocus?: boolean; preview?: boolean }): Promise { - return GitActions.Commit.openChanges(this.file, this.commit, options); - } -} - -export class CommitOpenChangesWithDiffToolCommandQuickPickItem extends CommandQuickPickItem { - constructor(private readonly commit: GitCommit, private readonly file: string | GitFile, item?: QuickPickItem) { - super(item ?? '$(git-compare) Open Changes (difftool)'); - } - - override execute(): Promise { - return GitActions.Commit.openChangesWithDiffTool(this.file, this.commit); - } -} - -export class CommitOpenChangesWithWorkingCommandQuickPickItem extends CommandQuickPickItem { - constructor(private readonly commit: GitCommit, private readonly file: string | GitFile, item?: QuickPickItem) { - super(item ?? '$(git-compare) Open Changes with Working File'); - } - - override execute(options: { preserveFocus?: boolean; preview?: boolean }): Promise { - return GitActions.Commit.openChangesWithWorking(this.file, this.commit, options); - } -} - -export class CommitOpenDirectoryCompareCommandQuickPickItem extends CommandQuickPickItem { - constructor(private readonly commit: GitCommit, item?: QuickPickItem) { - super(item ?? '$(git-compare) Open Directory Compare'); - } - - override execute(): Promise { - return GitActions.Commit.openDirectoryCompareWithPrevious(this.commit); - } -} - -export class CommitOpenDirectoryCompareWithWorkingCommandQuickPickItem extends CommandQuickPickItem { - constructor(private readonly commit: GitCommit, item?: QuickPickItem) { - super(item ?? '$(git-compare) Open Directory Compare with Working Tree'); - } - - override execute(): Promise { - return GitActions.Commit.openDirectoryCompareWithWorking(this.commit); - } -} - -export class CommitOpenFilesCommandQuickPickItem extends CommandQuickPickItem { - constructor(private readonly commit: GitCommit, item?: QuickPickItem) { - super(item ?? '$(files) Open Files'); - } - - override execute(_options: { preserveFocus?: boolean; preview?: boolean }): Promise { - return GitActions.Commit.openFiles(this.commit); - } -} - -export class CommitOpenFileCommandQuickPickItem extends CommandQuickPickItem { - constructor(private readonly commit: GitCommit, private readonly file: string | GitFile, item?: QuickPickItem) { - super(item ?? '$(file) Open File'); - } - - override execute(options?: { preserveFocus?: boolean; preview?: boolean }): Promise { - return GitActions.Commit.openFile(this.file, this.commit, options); - } -} - -export class CommitOpenRevisionsCommandQuickPickItem extends CommandQuickPickItem { - constructor(private readonly commit: GitCommit, item?: QuickPickItem) { - super(item ?? '$(files) Open Files at Revision'); - } - - override execute(_options: { preserveFocus?: boolean; preview?: boolean }): Promise { - return GitActions.Commit.openFilesAtRevision(this.commit); - } -} - -export class CommitOpenRevisionCommandQuickPickItem extends CommandQuickPickItem { - constructor(private readonly commit: GitCommit, private readonly file: string | GitFile, item?: QuickPickItem) { - super(item ?? '$(file) Open File at Revision'); - } - - override execute(options?: { preserveFocus?: boolean; preview?: boolean }): Promise { - return GitActions.Commit.openFileAtRevision(this.file, this.commit, options); - } -} - -export class CommitApplyFileChangesCommandQuickPickItem extends CommandQuickPickItem { - constructor(private readonly commit: GitCommit, private readonly file: string | GitFile, item?: QuickPickItem) { - super(item ?? 'Apply Changes'); - } - - override async execute(): Promise { - return GitActions.Commit.applyChanges(this.file, this.commit); - } -} - -export class CommitRestoreFileChangesCommandQuickPickItem extends CommandQuickPickItem { - constructor(private readonly commit: GitCommit, private readonly file: string | GitFile, item?: QuickPickItem) { - super( - item ?? { - label: 'Restore', - description: 'aka checkout', - }, - ); - } - - override execute(): Promise { - return GitActions.Commit.restoreFile(this.file, this.commit); - } -} - -export class OpenChangedFilesCommandQuickPickItem extends CommandQuickPickItem { - constructor(files: GitStatusFile[], item?: QuickPickItem) { - const commandArgs: OpenChangedFilesCommandArgs = { - uris: files.map(f => f.uri), - }; - - super(item ?? '$(files) Open All Changed Files', Commands.OpenChangedFiles, [commandArgs]); - } -} diff --git a/src/quickpicks/gitQuickPickItems.ts b/src/quickpicks/gitQuickPickItems.ts deleted file mode 100644 index 58acc32..0000000 --- a/src/quickpicks/gitQuickPickItems.ts +++ /dev/null @@ -1,439 +0,0 @@ -import { QuickInputButton, QuickPickItem } from 'vscode'; -import { GitCommandsCommand, GitCommandsCommandArgs } from '../commands/gitCommands'; -import { Commands, GlyphChars } from '../constants'; -import { Container } from '../container'; -import { emojify } from '../emojis'; -import { - GitBranch, - GitCommit, - GitContributor, - GitReference, - GitRemoteType, - GitRevision, - GitTag, - Repository, -} from '../git/models'; -import { fromNow } from '../system/date'; -import { pad } from '../system/string'; -import { CommandQuickPickItem, QuickPickItemOfT } from './quickPicksItems'; - -export class GitCommandQuickPickItem extends CommandQuickPickItem<[GitCommandsCommandArgs]> { - constructor(label: string, args: GitCommandsCommandArgs); - constructor(item: QuickPickItem, args: GitCommandsCommandArgs); - constructor(labelOrItem: string | QuickPickItem, args: GitCommandsCommandArgs) { - super(labelOrItem, Commands.GitCommands, [args], { suppressKeyPress: true }); - } - - executeSteps(pickedVia: 'menu' | 'command') { - return GitCommandsCommand.getSteps(Container.instance, this.args![0], pickedVia); - } -} - -export interface BranchQuickPickItem extends QuickPickItemOfT { - readonly current: boolean; - readonly ref: string; - readonly remote: boolean; -} - -export namespace BranchQuickPickItem { - export async function create( - branch: GitBranch, - picked?: boolean, - options?: { - alwaysShow?: boolean; - buttons?: QuickInputButton[]; - checked?: boolean; - current?: boolean | 'checkmark'; - ref?: boolean; - status?: boolean; - type?: boolean | 'remote'; - }, - ): Promise { - let description = ''; - if (options?.type === true) { - if (options?.current === true && branch.current) { - description = 'current branch'; - } else { - description = 'branch'; - } - } else if (options?.type === 'remote') { - if (branch.remote) { - description = 'remote branch'; - } - } else if (options?.current === true && branch.current) { - description = 'current branch'; - } - - if (options?.status && !branch.remote && branch.upstream != null) { - let arrows = GlyphChars.Dash; - - if (!branch.upstream.missing) { - const remote = await branch.getRemote(); - if (remote != null) { - let left; - let right; - for (const { type } of remote.urls) { - if (type === GitRemoteType.Fetch) { - left = true; - - if (right) break; - } else if (type === GitRemoteType.Push) { - right = true; - - if (left) break; - } - } - - if (left && right) { - arrows = GlyphChars.ArrowsRightLeft; - } else if (right) { - arrows = GlyphChars.ArrowRight; - } else if (left) { - arrows = GlyphChars.ArrowLeft; - } - } - } else { - arrows = GlyphChars.Warning; - } - - const status = `${branch.getTrackingStatus({ suffix: `${GlyphChars.Space} ` })}${arrows}${ - GlyphChars.Space - } ${branch.upstream.name}`; - description = `${description ? `${description}${GlyphChars.Space.repeat(2)}${status}` : status}`; - } - - if (options?.ref) { - if (branch.sha) { - description = description - ? `${description}${pad('$(git-commit)', 2, 2)}${GitRevision.shorten(branch.sha)}` - : `${pad('$(git-commit)', 0, 2)}${GitRevision.shorten(branch.sha)}`; - } - - if (branch.date !== undefined) { - description = description - ? `${description}${pad(GlyphChars.Dot, 2, 2)}${branch.formattedDate}` - : branch.formattedDate; - } - } - - const checked = - options?.checked || (options?.checked == null && options?.current === 'checkmark' && branch.current); - const item: BranchQuickPickItem = { - label: `${pad('$(git-branch)', 0, 2)}${branch.starred ? '$(star-full) ' : ''}${branch.name}${ - checked ? `${GlyphChars.Space.repeat(2)}$(check)${GlyphChars.Space}` : '' - }`, - description: description, - alwaysShow: options?.alwaysShow, - buttons: options?.buttons, - picked: picked ?? branch.current, - item: branch, - current: branch.current, - ref: branch.name, - remote: branch.remote, - }; - - return item; - } -} - -export class CommitLoadMoreQuickPickItem implements QuickPickItem { - readonly label = 'Load more'; - readonly alwaysShow = true; -} - -export type CommitQuickPickItem = QuickPickItemOfT; - -export namespace CommitQuickPickItem { - export function create( - commit: T, - picked?: boolean, - options: { alwaysShow?: boolean; buttons?: QuickInputButton[]; compact?: boolean; icon?: boolean } = {}, - ) { - if (GitCommit.isStash(commit)) { - const number = commit.number == null ? '' : `${commit.number}: `; - - if (options.compact) { - const item: CommitQuickPickItem = { - label: `${options.icon ? pad('$(archive)', 0, 2) : ''}${number}${commit.summary}`, - description: `${commit.formattedDate}${pad(GlyphChars.Dot, 2, 2)}${commit.formatStats({ - compact: true, - })}`, - alwaysShow: options.alwaysShow, - buttons: options.buttons, - picked: picked, - item: commit, - }; - - return item; - } - - const item: CommitQuickPickItem = { - label: `${options.icon ? pad('$(archive)', 0, 2) : ''}${number}${commit.summary}`, - description: '', - detail: `${GlyphChars.Space.repeat(2)}${commit.formattedDate}${pad( - GlyphChars.Dot, - 2, - 2, - )}${commit.formatStats({ compact: true })}`, - alwaysShow: options.alwaysShow, - buttons: options.buttons, - picked: picked, - item: commit, - }; - - return item; - } - - if (options.compact) { - const item: CommitQuickPickItem = { - label: `${options.icon ? pad('$(git-commit)', 0, 2) : ''}${commit.summary}`, - description: `${commit.author.name}, ${commit.formattedDate}${pad('$(git-commit)', 2, 2)}${ - commit.shortSha - }${pad(GlyphChars.Dot, 2, 2)}${commit.formatStats({ compact: true })}`, - alwaysShow: options.alwaysShow, - buttons: options.buttons, - picked: picked, - item: commit, - }; - return item; - } - - const item: CommitQuickPickItem = { - label: `${options.icon ? pad('$(git-commit)', 0, 2) : ''}${commit.summary}`, - description: '', - detail: `${GlyphChars.Space.repeat(2)}${commit.author.name}, ${commit.formattedDate}${pad( - '$(git-commit)', - 2, - 2, - )}${commit.shortSha}${pad(GlyphChars.Dot, 2, 2)}${commit.formatStats({ - compact: true, - })}`, - alwaysShow: options.alwaysShow, - buttons: options.buttons, - picked: picked, - item: commit, - }; - return item; - } -} - -export type ContributorQuickPickItem = QuickPickItemOfT; - -export namespace ContributorQuickPickItem { - export function create( - contributor: GitContributor, - picked?: boolean, - options: { alwaysShow?: boolean; buttons?: QuickInputButton[] } = {}, - ): ContributorQuickPickItem { - const item: ContributorQuickPickItem = { - label: contributor.label, - description: contributor.email, - alwaysShow: options.alwaysShow, - buttons: options.buttons, - picked: picked, - item: contributor, - }; - return item; - } -} - -export interface RefQuickPickItem extends QuickPickItemOfT { - readonly current: boolean; - readonly ref: string; - readonly remote: boolean; -} - -export namespace RefQuickPickItem { - export function create( - ref: string | GitReference, - repoPath: string, - picked?: boolean, - options: { alwaysShow?: boolean; buttons?: QuickInputButton[]; icon?: boolean; ref?: boolean } = {}, - ): RefQuickPickItem { - if (ref === '') { - return { - label: `${options.icon ? pad('$(file-directory)', 0, 2) : ''}Working Tree`, - description: '', - alwaysShow: options.alwaysShow, - buttons: options.buttons, - picked: picked, - item: GitReference.create(ref, repoPath, { refType: 'revision', name: 'Working Tree' }), - current: false, - ref: ref, - remote: false, - }; - } - - if (ref === 'HEAD') { - return { - label: `${options.icon ? pad('$(git-branch)', 0, 2) : ''}HEAD`, - description: '', - alwaysShow: options.alwaysShow, - buttons: options.buttons, - picked: picked, - item: GitReference.create(ref, repoPath, { refType: 'revision', name: 'HEAD' }), - current: false, - ref: ref, - remote: false, - }; - } - - let gitRef; - if (typeof ref === 'string') { - gitRef = GitReference.create(ref, repoPath); - } else { - gitRef = ref; - ref = gitRef.ref; - } - - if (GitRevision.isRange(ref)) { - return { - label: `Range ${gitRef.name}`, - description: '', - alwaysShow: options.alwaysShow, - buttons: options.buttons, - picked: picked, - item: gitRef, - current: false, - ref: ref, - remote: false, - }; - } - - const item: RefQuickPickItem = { - label: `Commit ${gitRef.name}`, - description: options.ref ? `$(git-commit) ${ref}` : '', - alwaysShow: options.alwaysShow, - buttons: options.buttons, - picked: picked, - item: gitRef, - current: false, - ref: ref, - remote: false, - }; - - return item; - } -} - -export interface RepositoryQuickPickItem extends QuickPickItemOfT { - readonly repoPath: string; -} - -export namespace RepositoryQuickPickItem { - export async function create( - repository: Repository, - picked?: boolean, - options: { - alwaysShow?: boolean; - branch?: boolean; - buttons?: QuickInputButton[]; - fetched?: boolean; - status?: boolean; - } = {}, - ) { - let repoStatus; - if (options.branch || options.status) { - repoStatus = await repository.getStatus(); - } - - let description = ''; - if (options.branch && repoStatus != null) { - description = repoStatus.branch; - } - - if (options.status && repoStatus != null) { - let workingStatus = ''; - if (repoStatus.files.length !== 0) { - workingStatus = repoStatus.getFormattedDiffStatus({ - compact: true, - prefix: pad(GlyphChars.Dot, 2, 2), - }); - } - - const upstreamStatus = repoStatus.getUpstreamStatus({ - prefix: description ? `${GlyphChars.Space} ` : '', - }); - - const status = `${upstreamStatus}${workingStatus}`; - if (status) { - description = `${description ? `${description}${status}` : status}`; - } - } - - if (options.fetched) { - const lastFetched = await repository.getLastFetched(); - if (lastFetched !== 0) { - const fetched = `Last fetched ${fromNow(new Date(lastFetched))}`; - description = `${description ? `${description}${pad(GlyphChars.Dot, 2, 2)}${fetched}` : fetched}`; - } - } - - const item: RepositoryQuickPickItem = { - label: repository.formattedName, - description: description, - alwaysShow: options.alwaysShow, - buttons: options.buttons, - picked: picked, - item: repository, - repoPath: repository.path, - }; - - return item; - } -} - -export interface TagQuickPickItem extends QuickPickItemOfT { - readonly current: boolean; - readonly ref: string; - readonly remote: boolean; -} - -export namespace TagQuickPickItem { - export function create( - tag: GitTag, - picked?: boolean, - options: { - alwaysShow?: boolean; - buttons?: QuickInputButton[]; - checked?: boolean; - message?: boolean; - ref?: boolean; - type?: boolean; - } = {}, - ) { - let description = ''; - if (options.type) { - description = 'tag'; - } - - if (options.ref) { - description = `${description}${pad('$(git-commit)', description ? 2 : 0, 2)}${GitRevision.shorten( - tag.sha, - )}`; - - description = `${description ? `${description}${pad(GlyphChars.Dot, 2, 2)}` : ''}${tag.formattedDate}`; - } - - if (options.message) { - const message = emojify(tag.message); - description = description ? `${description}${pad(GlyphChars.Dot, 2, 2)}${message}` : message; - } - - const item: TagQuickPickItem = { - label: `${pad('$(tag)', 0, 2)}${tag.name}${ - options.checked ? `${GlyphChars.Space.repeat(2)}$(check)${GlyphChars.Space}` : '' - }`, - description: description, - alwaysShow: options.alwaysShow, - buttons: options.buttons, - picked: picked, - item: tag, - current: false, - ref: tag.name, - remote: false, - }; - - return item; - } -} diff --git a/src/quickpicks/items/commits.ts b/src/quickpicks/items/commits.ts new file mode 100644 index 0000000..976bddb --- /dev/null +++ b/src/quickpicks/items/commits.ts @@ -0,0 +1,327 @@ +import { QuickPickItem, window } from 'vscode'; +import { GitActions } from '../../commands/gitCommands.actions'; +import { OpenChangedFilesCommandArgs } from '../../commands/openChangedFiles'; +import { QuickCommandButtons } from '../../commands/quickCommand.buttons'; +import { Commands, GlyphChars } from '../../constants'; +import { Container } from '../../container'; +import { CommitFormatter } from '../../git/formatters'; +import { GitCommit, GitFile, GitFileChange, GitStatusFile } from '../../git/models'; +import { Keys } from '../../keyboard'; +import { basename } from '../../system/path'; +import { pad } from '../../system/string'; +import { CommandQuickPickItem } from './common'; + +export class CommitFilesQuickPickItem extends CommandQuickPickItem { + constructor( + readonly commit: GitCommit, + options?: { + file?: GitFileChange; + unpublished?: boolean | undefined; + picked?: boolean; + hint?: string; + }, + ) { + super( + { + label: commit.summary, + description: `${CommitFormatter.fromTemplate(`\${author}, \${ago} $(git-commit) \${id}`, commit)}${ + options?.unpublished ? ' (unpublished)' : '' + }`, + detail: `${ + options?.file != null + ? `$(file) ${basename(options.file.path)}${options.file.formatStats({ + expand: true, + separator: ', ', + prefix: ` ${GlyphChars.Dot} `, + })}` + : `$(files) ${commit.formatStats({ + expand: true, + separator: ', ', + empty: 'No files changed', + })}` + }${options?.hint != null ? `${pad(GlyphChars.Dash, 4, 2, GlyphChars.Space)}${options.hint}` : ''}`, + alwaysShow: true, + picked: options?.picked ?? true, + buttons: GitCommit.isStash(commit) + ? [QuickCommandButtons.RevealInSideBar] + : [QuickCommandButtons.RevealInSideBar, QuickCommandButtons.SearchInSideBar], + }, + undefined, + undefined, + { suppressKeyPress: true }, + ); + } + + get sha(): string { + return this.commit.sha; + } +} + +export class CommitFileQuickPickItem extends CommandQuickPickItem { + constructor(readonly commit: GitCommit, readonly file: GitFile, picked?: boolean) { + super({ + label: `${pad(GitFile.getStatusCodicon(file.status), 0, 2)}${basename(file.path)}`, + description: GitFile.getFormattedDirectory(file, true), + picked: picked, + }); + + // TODO@eamodio - add line diff details + // this.detail = this.commit.getFormattedDiffStatus({ expand: true }); + } + + get sha(): string { + return this.commit.sha; + } + + override execute(options?: { preserveFocus?: boolean; preview?: boolean }): Promise { + return GitActions.Commit.openChanges(this.file, this.commit, options); + // const fileCommit = await this.commit.getCommitForFile(this.file)!; + + // if (fileCommit.previousSha === undefined) { + // void (await findOrOpenEditor( + // GitUri.toRevisionUri(fileCommit.sha, this.file, fileCommit.repoPath), + // options, + // )); + + // return; + // } + + // const commandArgs: DiffWithPreviousCommandArgs = { + // commit: fileCommit, + // showOptions: options, + // }; + // void (await executeCommand(Commands.DiffWithPrevious, fileCommit.toGitUri(), commandArgs)); + } +} + +export class CommitBrowseRepositoryFromHereCommandQuickPickItem extends CommandQuickPickItem { + constructor( + private readonly commit: GitCommit, + private readonly executeOptions?: { + before?: boolean; + openInNewWindow: boolean; + }, + item?: QuickPickItem, + ) { + super( + item ?? + `$(folder-opened) Browse Repository from${executeOptions?.before ? ' Before' : ''} Here${ + executeOptions?.openInNewWindow ? ' in New Window' : '' + }`, + ); + } + + override execute(_options: { preserveFocus?: boolean; preview?: boolean }): Promise { + return GitActions.browseAtRevision(this.commit.getGitUri(), { + before: this.executeOptions?.before, + openInNewWindow: this.executeOptions?.openInNewWindow, + }); + } +} + +export class CommitCompareWithHEADCommandQuickPickItem extends CommandQuickPickItem { + constructor(private readonly commit: GitCommit, item?: QuickPickItem) { + super(item ?? '$(compare-changes) Compare with HEAD'); + } + + override execute(_options: { preserveFocus?: boolean; preview?: boolean }): Promise { + return Container.instance.searchAndCompareView.compare(this.commit.repoPath, this.commit.ref, 'HEAD'); + } +} + +export class CommitCompareWithWorkingCommandQuickPickItem extends CommandQuickPickItem { + constructor(private readonly commit: GitCommit, item?: QuickPickItem) { + super(item ?? '$(compare-changes) Compare with Working Tree'); + } + + override execute(_options: { preserveFocus?: boolean; preview?: boolean }): Promise { + return Container.instance.searchAndCompareView.compare(this.commit.repoPath, this.commit.ref, ''); + } +} + +export class CommitCopyIdQuickPickItem extends CommandQuickPickItem { + constructor(private readonly commit: GitCommit, item?: QuickPickItem) { + super(item ?? '$(copy) Copy SHA'); + } + + override execute(): Promise { + return GitActions.Commit.copyIdToClipboard(this.commit); + } + + override async onDidPressKey(key: Keys): Promise { + await super.onDidPressKey(key); + void window.showInformationMessage('Commit SHA copied to the clipboard'); + } +} + +export class CommitCopyMessageQuickPickItem extends CommandQuickPickItem { + constructor(private readonly commit: GitCommit, item?: QuickPickItem) { + super(item ?? '$(copy) Copy Message'); + } + + override execute(): Promise { + return GitActions.Commit.copyMessageToClipboard(this.commit); + } + + override async onDidPressKey(key: Keys): Promise { + await super.onDidPressKey(key); + void window.showInformationMessage( + `${this.commit.stashName ? 'Stash' : 'Commit'} Message copied to the clipboard`, + ); + } +} + +export class CommitOpenAllChangesCommandQuickPickItem extends CommandQuickPickItem { + constructor(private readonly commit: GitCommit, item?: QuickPickItem) { + super(item ?? '$(git-compare) Open All Changes'); + } + + override execute(options: { preserveFocus?: boolean; preview?: boolean }): Promise { + return GitActions.Commit.openAllChanges(this.commit, options); + } +} + +export class CommitOpenAllChangesWithDiffToolCommandQuickPickItem extends CommandQuickPickItem { + constructor(private readonly commit: GitCommit, item?: QuickPickItem) { + super(item ?? '$(git-compare) Open All Changes (difftool)'); + } + + override execute(): Promise { + return GitActions.Commit.openAllChangesWithDiffTool(this.commit); + } +} + +export class CommitOpenAllChangesWithWorkingCommandQuickPickItem extends CommandQuickPickItem { + constructor(private readonly commit: GitCommit, item?: QuickPickItem) { + super(item ?? '$(git-compare) Open All Changes with Working Tree'); + } + + override execute(options: { preserveFocus?: boolean; preview?: boolean }): Promise { + return GitActions.Commit.openAllChangesWithWorking(this.commit, options); + } +} + +export class CommitOpenChangesCommandQuickPickItem extends CommandQuickPickItem { + constructor(private readonly commit: GitCommit, private readonly file: string | GitFile, item?: QuickPickItem) { + super(item ?? '$(git-compare) Open Changes'); + } + + override execute(options: { preserveFocus?: boolean; preview?: boolean }): Promise { + return GitActions.Commit.openChanges(this.file, this.commit, options); + } +} + +export class CommitOpenChangesWithDiffToolCommandQuickPickItem extends CommandQuickPickItem { + constructor(private readonly commit: GitCommit, private readonly file: string | GitFile, item?: QuickPickItem) { + super(item ?? '$(git-compare) Open Changes (difftool)'); + } + + override execute(): Promise { + return GitActions.Commit.openChangesWithDiffTool(this.file, this.commit); + } +} + +export class CommitOpenChangesWithWorkingCommandQuickPickItem extends CommandQuickPickItem { + constructor(private readonly commit: GitCommit, private readonly file: string | GitFile, item?: QuickPickItem) { + super(item ?? '$(git-compare) Open Changes with Working File'); + } + + override execute(options: { preserveFocus?: boolean; preview?: boolean }): Promise { + return GitActions.Commit.openChangesWithWorking(this.file, this.commit, options); + } +} + +export class CommitOpenDirectoryCompareCommandQuickPickItem extends CommandQuickPickItem { + constructor(private readonly commit: GitCommit, item?: QuickPickItem) { + super(item ?? '$(git-compare) Open Directory Compare'); + } + + override execute(): Promise { + return GitActions.Commit.openDirectoryCompareWithPrevious(this.commit); + } +} + +export class CommitOpenDirectoryCompareWithWorkingCommandQuickPickItem extends CommandQuickPickItem { + constructor(private readonly commit: GitCommit, item?: QuickPickItem) { + super(item ?? '$(git-compare) Open Directory Compare with Working Tree'); + } + + override execute(): Promise { + return GitActions.Commit.openDirectoryCompareWithWorking(this.commit); + } +} + +export class CommitOpenFilesCommandQuickPickItem extends CommandQuickPickItem { + constructor(private readonly commit: GitCommit, item?: QuickPickItem) { + super(item ?? '$(files) Open Files'); + } + + override execute(_options: { preserveFocus?: boolean; preview?: boolean }): Promise { + return GitActions.Commit.openFiles(this.commit); + } +} + +export class CommitOpenFileCommandQuickPickItem extends CommandQuickPickItem { + constructor(private readonly commit: GitCommit, private readonly file: string | GitFile, item?: QuickPickItem) { + super(item ?? '$(file) Open File'); + } + + override execute(options?: { preserveFocus?: boolean; preview?: boolean }): Promise { + return GitActions.Commit.openFile(this.file, this.commit, options); + } +} + +export class CommitOpenRevisionsCommandQuickPickItem extends CommandQuickPickItem { + constructor(private readonly commit: GitCommit, item?: QuickPickItem) { + super(item ?? '$(files) Open Files at Revision'); + } + + override execute(_options: { preserveFocus?: boolean; preview?: boolean }): Promise { + return GitActions.Commit.openFilesAtRevision(this.commit); + } +} + +export class CommitOpenRevisionCommandQuickPickItem extends CommandQuickPickItem { + constructor(private readonly commit: GitCommit, private readonly file: string | GitFile, item?: QuickPickItem) { + super(item ?? '$(file) Open File at Revision'); + } + + override execute(options?: { preserveFocus?: boolean; preview?: boolean }): Promise { + return GitActions.Commit.openFileAtRevision(this.file, this.commit, options); + } +} + +export class CommitApplyFileChangesCommandQuickPickItem extends CommandQuickPickItem { + constructor(private readonly commit: GitCommit, private readonly file: string | GitFile, item?: QuickPickItem) { + super(item ?? 'Apply Changes'); + } + + override async execute(): Promise { + return GitActions.Commit.applyChanges(this.file, this.commit); + } +} + +export class CommitRestoreFileChangesCommandQuickPickItem extends CommandQuickPickItem { + constructor(private readonly commit: GitCommit, private readonly file: string | GitFile, item?: QuickPickItem) { + super( + item ?? { + label: 'Restore', + description: 'aka checkout', + }, + ); + } + + override execute(): Promise { + return GitActions.Commit.restoreFile(this.file, this.commit); + } +} + +export class OpenChangedFilesCommandQuickPickItem extends CommandQuickPickItem { + constructor(files: GitStatusFile[], item?: QuickPickItem) { + const commandArgs: OpenChangedFilesCommandArgs = { + uris: files.map(f => f.uri), + }; + + super(item ?? '$(files) Open All Changed Files', Commands.OpenChangedFiles, [commandArgs]); + } +} diff --git a/src/quickpicks/items/common.ts b/src/quickpicks/items/common.ts new file mode 100644 index 0000000..c54030a --- /dev/null +++ b/src/quickpicks/items/common.ts @@ -0,0 +1,123 @@ +import { commands, QuickPickItem, QuickPickItemKind } from 'vscode'; +import { Commands } from '../../constants'; +import { Keys } from '../../keyboard'; + +declare module 'vscode' { + interface QuickPickItem { + onDidSelect?(): void; + onDidPressKey?(key: Keys): Promise; + } +} + +export interface QuickPickSeparator extends QuickPickItem { + kind: QuickPickItemKind.Separator; +} + +export namespace QuickPickSeparator { + export function create(label?: string): QuickPickSeparator { + return { kind: QuickPickItemKind.Separator, label: label ?? '' }; + } +} + +export interface QuickPickItemOfT extends QuickPickItem { + readonly item: T; +} + +export class CommandQuickPickItem implements QuickPickItem { + static fromCommand(label: string, command: Commands, args?: T): CommandQuickPickItem; + static fromCommand(item: QuickPickItem, command: Commands, args?: T): CommandQuickPickItem; + static fromCommand(labelOrItem: string | QuickPickItem, command: Commands, args?: T): CommandQuickPickItem { + return new CommandQuickPickItem( + typeof labelOrItem === 'string' ? { label: labelOrItem } : labelOrItem, + command, + args == null ? [] : [args], + ); + } + + static is(item: QuickPickItem): item is CommandQuickPickItem { + return item instanceof CommandQuickPickItem; + } + + label!: string; + description?: string; + detail?: string | undefined; + + constructor( + label: string, + command?: Commands, + args?: Arguments, + options?: { + onDidPressKey?: (key: Keys, result: Thenable) => void; + suppressKeyPress?: boolean; + }, + ); + constructor( + item: QuickPickItem, + command?: Commands, + args?: Arguments, + options?: { + onDidPressKey?: (key: Keys, result: Thenable) => void; + suppressKeyPress?: boolean; + }, + ); + constructor( + labelOrItem: string | QuickPickItem, + command?: Commands, + args?: Arguments, + options?: { + onDidPressKey?: (key: Keys, result: Thenable) => void; + suppressKeyPress?: boolean; + }, + ); + constructor( + labelOrItem: string | QuickPickItem, + protected readonly command?: Commands, + protected readonly args?: Arguments, + protected readonly options?: { + // onDidExecute?: ( + // options: { preserveFocus?: boolean; preview?: boolean } | undefined, + // result: Thenable, + // ) => void; + onDidPressKey?: (key: Keys, result: Thenable) => void; + suppressKeyPress?: boolean; + }, + ) { + this.command = command; + this.args = args; + + if (typeof labelOrItem === 'string') { + this.label = labelOrItem; + } else { + Object.assign(this, labelOrItem); + } + } + + execute(_options?: { preserveFocus?: boolean; preview?: boolean }): Promise { + if (this.command === undefined) return Promise.resolve(undefined); + + const result = commands.executeCommand(this.command, ...(this.args ?? [])) as Promise; + // this.options?.onDidExecute?.(options, result); + return result; + } + + async onDidPressKey(key: Keys): Promise { + if (this.options?.suppressKeyPress) return; + + const result = this.execute({ preserveFocus: true, preview: false }); + this.options?.onDidPressKey?.(key, result); + void (await result); + } +} + +export class ActionQuickPickItem extends CommandQuickPickItem { + constructor( + labelOrItem: string | QuickPickItem, + private readonly action: (options?: { preserveFocus?: boolean; preview?: boolean }) => void | Promise, + ) { + super(labelOrItem, undefined, undefined); + } + + override async execute(options?: { preserveFocus?: boolean; preview?: boolean }): Promise { + return this.action(options); + } +} diff --git a/src/quickpicks/items/directive.ts b/src/quickpicks/items/directive.ts new file mode 100644 index 0000000..149c53e --- /dev/null +++ b/src/quickpicks/items/directive.ts @@ -0,0 +1,59 @@ +export enum Directive { + Back, + Cancel, + LoadMore, + Noop, +} + +import { QuickPickItem } from 'vscode'; + +export namespace Directive { + export function is(value: Directive | T): value is Directive { + return typeof value === 'number' && Directive[value] != null; + } +} + +export interface DirectiveQuickPickItem extends QuickPickItem { + directive: Directive; +} + +export namespace DirectiveQuickPickItem { + export function create( + directive: Directive, + picked?: boolean, + options: { label?: string; description?: string; detail?: string } = {}, + ) { + let label = options.label; + if (label == null) { + switch (directive) { + case Directive.Back: + label = 'Back'; + break; + case Directive.Cancel: + label = 'Cancel'; + break; + case Directive.LoadMore: + label = 'Load more'; + break; + case Directive.Noop: + label = 'Try again'; + break; + } + } + + const item: DirectiveQuickPickItem = { + label: label, + description: options.description, + detail: options.detail, + alwaysShow: true, + picked: picked, + directive: directive, + }; + + return item; + } + + export function is(item: QuickPickItem): item is DirectiveQuickPickItem { + return item != null && 'directive' in item; + } +} diff --git a/src/quickpicks/items/flags.ts b/src/quickpicks/items/flags.ts new file mode 100644 index 0000000..d10a001 --- /dev/null +++ b/src/quickpicks/items/flags.ts @@ -0,0 +1,19 @@ +import { QuickPickItem } from 'vscode'; +import { QuickPickItemOfT } from './common'; + +export type FlagsQuickPickItem = QuickPickItemOfT; +export namespace FlagsQuickPickItem { + export function create(flags: T[], item: T[], options: QuickPickItem) { + return { ...options, item: item, picked: hasFlags(flags, item) }; + } +} +function hasFlags(flags: T[], has?: T | T[]): boolean { + if (has === undefined) { + return flags.length === 0; + } + if (!Array.isArray(has)) { + return flags.includes(has); + } + + return has.length === 0 ? flags.length === 0 : has.every(f => flags.includes(f)); +} diff --git a/src/quickpicks/items/gitCommands.ts b/src/quickpicks/items/gitCommands.ts new file mode 100644 index 0000000..c8aec7e --- /dev/null +++ b/src/quickpicks/items/gitCommands.ts @@ -0,0 +1,440 @@ +import { QuickInputButton, QuickPickItem } from 'vscode'; +import type { GitCommandsCommandArgs } from '../../commands/gitCommands'; +import { getSteps } from '../../commands/gitCommands.utils'; +import { Commands, GlyphChars } from '../../constants'; +import { Container } from '../../container'; +import { emojify } from '../../emojis'; +import { + GitBranch, + GitCommit, + GitContributor, + GitReference, + GitRemoteType, + GitRevision, + GitTag, + Repository, +} from '../../git/models'; +import { fromNow } from '../../system/date'; +import { pad } from '../../system/string'; +import { CommandQuickPickItem, QuickPickItemOfT } from './common'; + +export class GitCommandQuickPickItem extends CommandQuickPickItem<[GitCommandsCommandArgs]> { + constructor(label: string, args: GitCommandsCommandArgs); + constructor(item: QuickPickItem, args: GitCommandsCommandArgs); + constructor(labelOrItem: string | QuickPickItem, args: GitCommandsCommandArgs) { + super(labelOrItem, Commands.GitCommands, [args], { suppressKeyPress: true }); + } + + executeSteps(pickedVia: 'menu' | 'command') { + return getSteps(Container.instance, this.args![0], pickedVia); + } +} + +export interface BranchQuickPickItem extends QuickPickItemOfT { + readonly current: boolean; + readonly ref: string; + readonly remote: boolean; +} + +export namespace BranchQuickPickItem { + export async function create( + branch: GitBranch, + picked?: boolean, + options?: { + alwaysShow?: boolean; + buttons?: QuickInputButton[]; + checked?: boolean; + current?: boolean | 'checkmark'; + ref?: boolean; + status?: boolean; + type?: boolean | 'remote'; + }, + ): Promise { + let description = ''; + if (options?.type === true) { + if (options?.current === true && branch.current) { + description = 'current branch'; + } else { + description = 'branch'; + } + } else if (options?.type === 'remote') { + if (branch.remote) { + description = 'remote branch'; + } + } else if (options?.current === true && branch.current) { + description = 'current branch'; + } + + if (options?.status && !branch.remote && branch.upstream != null) { + let arrows = GlyphChars.Dash; + + if (!branch.upstream.missing) { + const remote = await branch.getRemote(); + if (remote != null) { + let left; + let right; + for (const { type } of remote.urls) { + if (type === GitRemoteType.Fetch) { + left = true; + + if (right) break; + } else if (type === GitRemoteType.Push) { + right = true; + + if (left) break; + } + } + + if (left && right) { + arrows = GlyphChars.ArrowsRightLeft; + } else if (right) { + arrows = GlyphChars.ArrowRight; + } else if (left) { + arrows = GlyphChars.ArrowLeft; + } + } + } else { + arrows = GlyphChars.Warning; + } + + const status = `${branch.getTrackingStatus({ suffix: `${GlyphChars.Space} ` })}${arrows}${ + GlyphChars.Space + } ${branch.upstream.name}`; + description = `${description ? `${description}${GlyphChars.Space.repeat(2)}${status}` : status}`; + } + + if (options?.ref) { + if (branch.sha) { + description = description + ? `${description}${pad('$(git-commit)', 2, 2)}${GitRevision.shorten(branch.sha)}` + : `${pad('$(git-commit)', 0, 2)}${GitRevision.shorten(branch.sha)}`; + } + + if (branch.date !== undefined) { + description = description + ? `${description}${pad(GlyphChars.Dot, 2, 2)}${branch.formattedDate}` + : branch.formattedDate; + } + } + + const checked = + options?.checked || (options?.checked == null && options?.current === 'checkmark' && branch.current); + const item: BranchQuickPickItem = { + label: `${pad('$(git-branch)', 0, 2)}${branch.starred ? '$(star-full) ' : ''}${branch.name}${ + checked ? `${GlyphChars.Space.repeat(2)}$(check)${GlyphChars.Space}` : '' + }`, + description: description, + alwaysShow: options?.alwaysShow, + buttons: options?.buttons, + picked: picked ?? branch.current, + item: branch, + current: branch.current, + ref: branch.name, + remote: branch.remote, + }; + + return item; + } +} + +export class CommitLoadMoreQuickPickItem implements QuickPickItem { + readonly label = 'Load more'; + readonly alwaysShow = true; +} + +export type CommitQuickPickItem = QuickPickItemOfT; + +export namespace CommitQuickPickItem { + export function create( + commit: T, + picked?: boolean, + options: { alwaysShow?: boolean; buttons?: QuickInputButton[]; compact?: boolean; icon?: boolean } = {}, + ) { + if (GitCommit.isStash(commit)) { + const number = commit.number == null ? '' : `${commit.number}: `; + + if (options.compact) { + const item: CommitQuickPickItem = { + label: `${options.icon ? pad('$(archive)', 0, 2) : ''}${number}${commit.summary}`, + description: `${commit.formattedDate}${pad(GlyphChars.Dot, 2, 2)}${commit.formatStats({ + compact: true, + })}`, + alwaysShow: options.alwaysShow, + buttons: options.buttons, + picked: picked, + item: commit, + }; + + return item; + } + + const item: CommitQuickPickItem = { + label: `${options.icon ? pad('$(archive)', 0, 2) : ''}${number}${commit.summary}`, + description: '', + detail: `${GlyphChars.Space.repeat(2)}${commit.formattedDate}${pad( + GlyphChars.Dot, + 2, + 2, + )}${commit.formatStats({ compact: true })}`, + alwaysShow: options.alwaysShow, + buttons: options.buttons, + picked: picked, + item: commit, + }; + + return item; + } + + if (options.compact) { + const item: CommitQuickPickItem = { + label: `${options.icon ? pad('$(git-commit)', 0, 2) : ''}${commit.summary}`, + description: `${commit.author.name}, ${commit.formattedDate}${pad('$(git-commit)', 2, 2)}${ + commit.shortSha + }${pad(GlyphChars.Dot, 2, 2)}${commit.formatStats({ compact: true })}`, + alwaysShow: options.alwaysShow, + buttons: options.buttons, + picked: picked, + item: commit, + }; + return item; + } + + const item: CommitQuickPickItem = { + label: `${options.icon ? pad('$(git-commit)', 0, 2) : ''}${commit.summary}`, + description: '', + detail: `${GlyphChars.Space.repeat(2)}${commit.author.name}, ${commit.formattedDate}${pad( + '$(git-commit)', + 2, + 2, + )}${commit.shortSha}${pad(GlyphChars.Dot, 2, 2)}${commit.formatStats({ + compact: true, + })}`, + alwaysShow: options.alwaysShow, + buttons: options.buttons, + picked: picked, + item: commit, + }; + return item; + } +} + +export type ContributorQuickPickItem = QuickPickItemOfT; + +export namespace ContributorQuickPickItem { + export function create( + contributor: GitContributor, + picked?: boolean, + options: { alwaysShow?: boolean; buttons?: QuickInputButton[] } = {}, + ): ContributorQuickPickItem { + const item: ContributorQuickPickItem = { + label: contributor.label, + description: contributor.email, + alwaysShow: options.alwaysShow, + buttons: options.buttons, + picked: picked, + item: contributor, + }; + return item; + } +} + +export interface RefQuickPickItem extends QuickPickItemOfT { + readonly current: boolean; + readonly ref: string; + readonly remote: boolean; +} + +export namespace RefQuickPickItem { + export function create( + ref: string | GitReference, + repoPath: string, + picked?: boolean, + options: { alwaysShow?: boolean; buttons?: QuickInputButton[]; icon?: boolean; ref?: boolean } = {}, + ): RefQuickPickItem { + if (ref === '') { + return { + label: `${options.icon ? pad('$(file-directory)', 0, 2) : ''}Working Tree`, + description: '', + alwaysShow: options.alwaysShow, + buttons: options.buttons, + picked: picked, + item: GitReference.create(ref, repoPath, { refType: 'revision', name: 'Working Tree' }), + current: false, + ref: ref, + remote: false, + }; + } + + if (ref === 'HEAD') { + return { + label: `${options.icon ? pad('$(git-branch)', 0, 2) : ''}HEAD`, + description: '', + alwaysShow: options.alwaysShow, + buttons: options.buttons, + picked: picked, + item: GitReference.create(ref, repoPath, { refType: 'revision', name: 'HEAD' }), + current: false, + ref: ref, + remote: false, + }; + } + + let gitRef; + if (typeof ref === 'string') { + gitRef = GitReference.create(ref, repoPath); + } else { + gitRef = ref; + ref = gitRef.ref; + } + + if (GitRevision.isRange(ref)) { + return { + label: `Range ${gitRef.name}`, + description: '', + alwaysShow: options.alwaysShow, + buttons: options.buttons, + picked: picked, + item: gitRef, + current: false, + ref: ref, + remote: false, + }; + } + + const item: RefQuickPickItem = { + label: `Commit ${gitRef.name}`, + description: options.ref ? `$(git-commit) ${ref}` : '', + alwaysShow: options.alwaysShow, + buttons: options.buttons, + picked: picked, + item: gitRef, + current: false, + ref: ref, + remote: false, + }; + + return item; + } +} + +export interface RepositoryQuickPickItem extends QuickPickItemOfT { + readonly repoPath: string; +} + +export namespace RepositoryQuickPickItem { + export async function create( + repository: Repository, + picked?: boolean, + options: { + alwaysShow?: boolean; + branch?: boolean; + buttons?: QuickInputButton[]; + fetched?: boolean; + status?: boolean; + } = {}, + ) { + let repoStatus; + if (options.branch || options.status) { + repoStatus = await repository.getStatus(); + } + + let description = ''; + if (options.branch && repoStatus != null) { + description = repoStatus.branch; + } + + if (options.status && repoStatus != null) { + let workingStatus = ''; + if (repoStatus.files.length !== 0) { + workingStatus = repoStatus.getFormattedDiffStatus({ + compact: true, + prefix: pad(GlyphChars.Dot, 2, 2), + }); + } + + const upstreamStatus = repoStatus.getUpstreamStatus({ + prefix: description ? `${GlyphChars.Space} ` : '', + }); + + const status = `${upstreamStatus}${workingStatus}`; + if (status) { + description = `${description ? `${description}${status}` : status}`; + } + } + + if (options.fetched) { + const lastFetched = await repository.getLastFetched(); + if (lastFetched !== 0) { + const fetched = `Last fetched ${fromNow(new Date(lastFetched))}`; + description = `${description ? `${description}${pad(GlyphChars.Dot, 2, 2)}${fetched}` : fetched}`; + } + } + + const item: RepositoryQuickPickItem = { + label: repository.formattedName, + description: description, + alwaysShow: options.alwaysShow, + buttons: options.buttons, + picked: picked, + item: repository, + repoPath: repository.path, + }; + + return item; + } +} + +export interface TagQuickPickItem extends QuickPickItemOfT { + readonly current: boolean; + readonly ref: string; + readonly remote: boolean; +} + +export namespace TagQuickPickItem { + export function create( + tag: GitTag, + picked?: boolean, + options: { + alwaysShow?: boolean; + buttons?: QuickInputButton[]; + checked?: boolean; + message?: boolean; + ref?: boolean; + type?: boolean; + } = {}, + ) { + let description = ''; + if (options.type) { + description = 'tag'; + } + + if (options.ref) { + description = `${description}${pad('$(git-commit)', description ? 2 : 0, 2)}${GitRevision.shorten( + tag.sha, + )}`; + + description = `${description ? `${description}${pad(GlyphChars.Dot, 2, 2)}` : ''}${tag.formattedDate}`; + } + + if (options.message) { + const message = emojify(tag.message); + description = description ? `${description}${pad(GlyphChars.Dot, 2, 2)}${message}` : message; + } + + const item: TagQuickPickItem = { + label: `${pad('$(tag)', 0, 2)}${tag.name}${ + options.checked ? `${GlyphChars.Space.repeat(2)}$(check)${GlyphChars.Space}` : '' + }`, + description: description, + alwaysShow: options.alwaysShow, + buttons: options.buttons, + picked: picked, + item: tag, + current: false, + ref: tag.name, + remote: false, + }; + + return item; + } +} diff --git a/src/quickpicks/quickPicksItems.ts b/src/quickpicks/quickPicksItems.ts deleted file mode 100644 index d3a4825..0000000 --- a/src/quickpicks/quickPicksItems.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { commands, QuickPickItem, QuickPickItemKind } from 'vscode'; -import { Commands } from '../constants'; -import { Keys } from '../keyboard'; - -declare module 'vscode' { - interface QuickPickItem { - onDidSelect?(): void; - onDidPressKey?(key: Keys): Promise; - } -} - -export interface QuickPickSeparator extends QuickPickItem { - kind: QuickPickItemKind.Separator; -} - -export namespace QuickPickSeparator { - export function create(label?: string): QuickPickSeparator { - return { kind: QuickPickItemKind.Separator, label: label ?? '' }; - } -} - -export interface QuickPickItemOfT extends QuickPickItem { - readonly item: T; -} - -export enum Directive { - Back, - Cancel, - LoadMore, - Noop, -} - -export namespace Directive { - export function is(value: Directive | T): value is Directive { - return typeof value === 'number' && Directive[value] != null; - } -} - -export interface DirectiveQuickPickItem extends QuickPickItem { - directive: Directive; -} - -export namespace DirectiveQuickPickItem { - export function create( - directive: Directive, - picked?: boolean, - options: { label?: string; description?: string; detail?: string } = {}, - ) { - let label = options.label; - if (label == null) { - switch (directive) { - case Directive.Back: - label = 'Back'; - break; - case Directive.Cancel: - label = 'Cancel'; - break; - case Directive.LoadMore: - label = 'Load more'; - break; - case Directive.Noop: - label = 'Try again'; - break; - } - } - - const item: DirectiveQuickPickItem = { - label: label, - description: options.description, - detail: options.detail, - alwaysShow: true, - picked: picked, - directive: directive, - }; - - return item; - } - - export function is(item: QuickPickItem): item is DirectiveQuickPickItem { - return item != null && 'directive' in item; - } -} - -export class CommandQuickPickItem implements QuickPickItem { - static fromCommand(label: string, command: Commands, args?: T): CommandQuickPickItem; - static fromCommand(item: QuickPickItem, command: Commands, args?: T): CommandQuickPickItem; - static fromCommand(labelOrItem: string | QuickPickItem, command: Commands, args?: T): CommandQuickPickItem { - return new CommandQuickPickItem( - typeof labelOrItem === 'string' ? { label: labelOrItem } : labelOrItem, - command, - args == null ? [] : [args], - ); - } - - static is(item: QuickPickItem): item is CommandQuickPickItem { - return item instanceof CommandQuickPickItem; - } - - label!: string; - description?: string; - detail?: string | undefined; - - constructor( - label: string, - command?: Commands, - args?: Arguments, - options?: { - onDidPressKey?: (key: Keys, result: Thenable) => void; - suppressKeyPress?: boolean; - }, - ); - constructor( - item: QuickPickItem, - command?: Commands, - args?: Arguments, - options?: { - onDidPressKey?: (key: Keys, result: Thenable) => void; - suppressKeyPress?: boolean; - }, - ); - constructor( - labelOrItem: string | QuickPickItem, - command?: Commands, - args?: Arguments, - options?: { - onDidPressKey?: (key: Keys, result: Thenable) => void; - suppressKeyPress?: boolean; - }, - ); - constructor( - labelOrItem: string | QuickPickItem, - protected readonly command?: Commands, - protected readonly args?: Arguments, - protected readonly options?: { - // onDidExecute?: ( - // options: { preserveFocus?: boolean; preview?: boolean } | undefined, - // result: Thenable, - // ) => void; - onDidPressKey?: (key: Keys, result: Thenable) => void; - suppressKeyPress?: boolean; - }, - ) { - this.command = command; - this.args = args; - - if (typeof labelOrItem === 'string') { - this.label = labelOrItem; - } else { - Object.assign(this, labelOrItem); - } - } - - execute(_options?: { preserveFocus?: boolean; preview?: boolean }): Promise { - if (this.command === undefined) return Promise.resolve(undefined); - - const result = commands.executeCommand(this.command, ...(this.args ?? [])) as Promise; - // this.options?.onDidExecute?.(options, result); - return result; - } - - async onDidPressKey(key: Keys): Promise { - if (this.options?.suppressKeyPress) return; - - const result = this.execute({ preserveFocus: true, preview: false }); - this.options?.onDidPressKey?.(key, result); - void (await result); - } -} - -export class ActionQuickPickItem extends CommandQuickPickItem { - constructor( - labelOrItem: string | QuickPickItem, - private readonly action: (options?: { preserveFocus?: boolean; preview?: boolean }) => void | Promise, - ) { - super(labelOrItem, undefined, undefined); - } - - override async execute(options?: { preserveFocus?: boolean; preview?: boolean }): Promise { - return this.action(options); - } -} - -export type FlagsQuickPickItem = QuickPickItemOfT; -export namespace FlagsQuickPickItem { - export function create(flags: T[], item: T[], options: QuickPickItem) { - return { ...options, item: item, picked: hasFlags(flags, item) }; - } -} - -function hasFlags(flags: T[], has?: T | T[]): boolean { - if (has === undefined) return flags.length === 0; - if (!Array.isArray(has)) return flags.includes(has); - - return has.length === 0 ? flags.length === 0 : has.every(f => flags.includes(f)); -} diff --git a/src/quickpicks/referencePicker.ts b/src/quickpicks/referencePicker.ts index 80fd33a..ea2eb38 100644 --- a/src/quickpicks/referencePicker.ts +++ b/src/quickpicks/referencePicker.ts @@ -5,7 +5,8 @@ import { GlyphChars } from '../constants'; import { Container } from '../container'; import { BranchSortOptions, GitBranch, GitReference, GitTag, TagSortOptions } from '../git/models'; import { KeyboardScope, Keys } from '../keyboard'; -import { BranchQuickPickItem, getQuickPickIgnoreFocusOut, RefQuickPickItem, TagQuickPickItem } from '../quickpicks'; +import { getQuickPickIgnoreFocusOut } from '../system/utils'; +import { BranchQuickPickItem, RefQuickPickItem, TagQuickPickItem } from './items/gitCommands'; export type ReferencesQuickPickItem = BranchQuickPickItem | TagQuickPickItem | RefQuickPickItem; diff --git a/src/quickpicks/remoteProviderPicker.ts b/src/quickpicks/remoteProviderPicker.ts index 49f1407..d20a19e 100644 --- a/src/quickpicks/remoteProviderPicker.ts +++ b/src/quickpicks/remoteProviderPicker.ts @@ -5,7 +5,8 @@ import { Container } from '../container'; import { GitBranch, GitRemote } from '../git/models'; import { getNameFromRemoteResource, RemoteProvider, RemoteResource, RemoteResourceType } from '../git/remotes/provider'; import { Keys } from '../keyboard'; -import { CommandQuickPickItem, getQuickPickIgnoreFocusOut } from '../quickpicks'; +import { CommandQuickPickItem } from '../quickpicks/items/common'; +import { getQuickPickIgnoreFocusOut } from '../system/utils'; export class ConfigureCustomRemoteProviderCommandQuickPickItem extends CommandQuickPickItem { constructor() { diff --git a/src/quickpicks/repositoryPicker.ts b/src/quickpicks/repositoryPicker.ts index b520413..b261a98 100644 --- a/src/quickpicks/repositoryPicker.ts +++ b/src/quickpicks/repositoryPicker.ts @@ -1,8 +1,10 @@ import { Disposable, TextEditor, Uri, window } from 'vscode'; import { Container } from '../container'; import { Repository } from '../git/models'; -import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, RepositoryQuickPickItem } from '../quickpicks'; -import { Iterables } from '../system'; +import { map } from '../system/iterable'; +import { getQuickPickIgnoreFocusOut } from '../system/utils'; +import { CommandQuickPickItem } from './items/common'; +import { RepositoryQuickPickItem } from './items/gitCommands'; export namespace RepositoryPicker { export async function getBestRepositoryOrShow( @@ -43,7 +45,7 @@ export namespace RepositoryPicker { repositories?: Repository[], ): Promise { const items: RepositoryQuickPickItem[] = await Promise.all([ - ...Iterables.map(repositories ?? Container.instance.git.openRepositories, r => + ...map(repositories ?? Container.instance.git.openRepositories, r => RepositoryQuickPickItem.create(r, undefined, { branch: true, status: true }), ), ]); diff --git a/src/storage.ts b/src/storage.ts index 524df65..1459bdf 100644 --- a/src/storage.ts +++ b/src/storage.ts @@ -1,6 +1,6 @@ import { ExtensionContext } from 'vscode'; -import { ViewShowBranchComparison } from './config'; -import { SearchPattern } from './git/search'; +import type { ViewShowBranchComparison } from './config'; +import type { SearchPattern } from './git/search'; export class Storage { constructor(private readonly context: ExtensionContext) {} diff --git a/src/system.ts b/src/system.ts deleted file mode 100644 index b953a68..0000000 --- a/src/system.ts +++ /dev/null @@ -1,26 +0,0 @@ -declare global { - export type PartialDeep = T extends Record ? { [K in keyof T]?: PartialDeep } : T; - export type PickPartialDeep = Omit, K> & { [P in K]?: Partial }; - - export type Mutable = { -readonly [P in keyof T]: T[P] }; - export type PickMutable = Omit & { -readonly [P in K]: T[P] }; - - export type ExcludeSome = Omit & { [P in K]-?: Exclude }; - - export type ExtractAll = { [K in keyof T]: T[K] extends U ? T[K] : never }; - export type ExtractSome = Omit & { [P in K]-?: Extract }; - - export type RequireSome = Omit & { [P in K]-?: T[P] }; - - export type AllNonNullable = { [P in keyof T]-?: NonNullable }; - export type SomeNonNullable = Omit & { [P in K]-?: NonNullable }; - - export type NarrowRepo = ExcludeSome; - export type NarrowRepos = ExcludeSome; -} - -export * as Arrays from './system/array'; -export * from './system/decorators/gate'; -export * from './system/decorators/log'; -export * as Iterables from './system/iterable'; -export * as Strings from './system/string'; diff --git a/src/system/command.ts b/src/system/command.ts index 3a321eb..3e093ee 100644 --- a/src/system/command.ts +++ b/src/system/command.ts @@ -1,7 +1,23 @@ -import { commands, Command as CoreCommand, Uri } from 'vscode'; +import { commands, Command as CoreCommand, Disposable, Uri } from 'vscode'; import { Action, ActionContext } from '../api/gitlens'; -import { Command } from '../commands/base'; +import type { Command } from '../commands/base'; import { Commands, CoreCommands, CoreGitCommands } from '../constants'; +import type { Container } from '../container'; + +interface CommandConstructor { + new (container: Container): Command; +} +const registrableCommands: CommandConstructor[] = []; + +export function command(): ClassDecorator { + return (target: any) => { + registrableCommands.push(target); + }; +} + +export function registerCommands(container: Container): Disposable[] { + return registrableCommands.map(c => new c(container)); +} export function asCommand( command: Omit & { arguments: [...T] }, @@ -13,12 +29,6 @@ export function executeActionCommand(action: Action, return commands.executeCommand(`${Commands.ActionPrefix}${action}`, { ...args, type: action }); } -export function getMarkdownActionCommand(action: Action, args: Omit): string { - return Command.getMarkdownCommandArgsCore(`${Commands.ActionPrefix}${action}`, { - ...args, - type: action, - }); -} type SupportedCommands = Commands | `gitlens.views.${string}.focus` | `gitlens.views.${string}.resetViewLocation`; export function executeCommand(command: SupportedCommands): Thenable; diff --git a/src/system/stopwatch.ts b/src/system/stopwatch.ts index 557aa1e..22f53be 100644 --- a/src/system/stopwatch.ts +++ b/src/system/stopwatch.ts @@ -1,7 +1,7 @@ import { hrtime } from '@env/hrtime'; import { GlyphChars } from '../constants'; import { LogCorrelationContext, Logger, LogLevel } from '../logger'; -import { getNextCorrelationId } from '../system'; +import { getNextCorrelationId } from '../system/decorators/log'; type StopwatchLogOptions = { message?: string; suffix?: string }; type StopwatchOptions = { diff --git a/src/system/utils.ts b/src/system/utils.ts index 5e6c69f..57502f1 100644 --- a/src/system/utils.ts +++ b/src/system/utils.ts @@ -1,4 +1,5 @@ import { TextDocument, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace } from 'vscode'; +import { configuration } from '../configuration'; import { CoreCommands, ImageMimetypes, Schemes } from '../constants'; import { GitUri } from '../git/gitUri'; import { Logger } from '../logger'; @@ -55,6 +56,10 @@ export function getEditorIfActive(document: TextDocument): TextEditor | undefine return editor != null && editor.document === document ? editor : undefined; } +export function getQuickPickIgnoreFocusOut() { + return !configuration.get('advanced.quickPick.closeOnFocusOut'); +} + export function hasVisibleTextEditor(): boolean { if (window.visibleTextEditors.length === 0) return false; diff --git a/src/trackers/gitLineTracker.ts b/src/trackers/gitLineTracker.ts index 525d76d..9c949c9 100644 --- a/src/trackers/gitLineTracker.ts +++ b/src/trackers/gitLineTracker.ts @@ -3,7 +3,7 @@ import { GlyphChars } from '../constants'; import { Container } from '../container'; import { GitCommit } from '../git/models'; import { Logger } from '../logger'; -import { debug } from '../system'; +import { debug } from '../system/decorators/log'; import { DocumentBlameStateChangeEvent, DocumentContentChangeEvent, diff --git a/src/views/nodes/autolinkedItemsNode.ts b/src/views/nodes/autolinkedItemsNode.ts index dfb9009..3d3115e 100644 --- a/src/views/nodes/autolinkedItemsNode.ts +++ b/src/views/nodes/autolinkedItemsNode.ts @@ -2,7 +2,8 @@ import { TreeItem, TreeItemCollapsibleState } from 'vscode'; import { GitUri } from '../../git/gitUri'; import { GitFile, GitLog, GitRemote, IssueOrPullRequest, PullRequest } from '../../git/models'; import { RichRemoteProvider } from '../../git/remotes/provider'; -import { debug, gate } from '../../system'; +import { gate } from '../../system/decorators/gate'; +import { debug } from '../../system/decorators/log'; import { PromiseCancelledErrorWithId } from '../../system/promise'; import { ViewsWithCommits } from '../viewBase'; import { AutolinkedItemNode } from './autolinkedItemNode'; diff --git a/src/views/nodes/branchOrTagFolderNode.ts b/src/views/nodes/branchOrTagFolderNode.ts index 3b3e8c3..bc02605 100644 --- a/src/views/nodes/branchOrTagFolderNode.ts +++ b/src/views/nodes/branchOrTagFolderNode.ts @@ -1,6 +1,6 @@ import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from 'vscode'; import { GitUri } from '../../git/gitUri'; -import { Arrays } from '../../system'; +import { HierarchicalItem } from '../../system/array'; import { View } from '../viewBase'; import { BranchNode } from './branchNode'; import { RepositoryNode } from './repositoryNode'; @@ -21,7 +21,7 @@ export class BranchOrTagFolderNode extends ViewNode { public readonly repoPath: string, public readonly folderName: string, public readonly relativePath: string | undefined, - public readonly root: Arrays.HierarchicalItem, + public readonly root: HierarchicalItem, private readonly _key?: string, private readonly _expanded: boolean = false, ) { diff --git a/src/views/nodes/branchTrackingStatusFilesNode.ts b/src/views/nodes/branchTrackingStatusFilesNode.ts index eec796c..5cb4555 100644 --- a/src/views/nodes/branchTrackingStatusFilesNode.ts +++ b/src/views/nodes/branchTrackingStatusFilesNode.ts @@ -2,11 +2,10 @@ import { TreeItem, TreeItemCollapsibleState } from 'vscode'; import { ViewFilesLayout } from '../../configuration'; import { GitUri } from '../../git/gitUri'; import { GitBranch, GitFileWithCommit, GitRevision } from '../../git/models'; -import { Strings } from '../../system'; import { groupBy, makeHierarchical } from '../../system/array'; import { filter, flatMap, map } from '../../system/iterable'; import { joinPaths, normalizePath } from '../../system/path'; -import { sortCompare } from '../../system/string'; +import { pluralize, sortCompare } from '../../system/string'; import { ViewsWithCommits } from '../viewBase'; import { BranchNode } from './branchNode'; import { BranchTrackingStatus } from './branchTrackingStatusNode'; @@ -114,7 +113,7 @@ export class BranchTrackingStatusFilesNode extends ViewNode { ); const files = stats?.changedFiles ?? 0; - const label = `${Strings.pluralize('file', files)} changed`; + const label = `${pluralize('file', files)} changed`; const item = new TreeItem(label, TreeItemCollapsibleState.Collapsed); item.id = this.id; item.contextValue = ContextValues.BranchStatusFiles; diff --git a/src/views/nodes/branchesNode.ts b/src/views/nodes/branchesNode.ts index 147cd85..efc9d79 100644 --- a/src/views/nodes/branchesNode.ts +++ b/src/views/nodes/branchesNode.ts @@ -2,7 +2,9 @@ import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from 'vscode'; import { ViewBranchesLayout } from '../../configuration'; import { GitUri } from '../../git/gitUri'; import { Repository } from '../../git/models'; -import { Arrays, debug, gate } from '../../system'; +import { makeHierarchical } from '../../system/array'; +import { gate } from '../../system/decorators/gate'; +import { debug } from '../../system/decorators/log'; import { BranchesView } from '../branchesView'; import { RepositoriesView } from '../repositoriesView'; import { BranchNode } from './branchNode'; @@ -57,7 +59,7 @@ export class BranchesNode extends ViewNode { ); if (this.view.config.branches.layout === ViewBranchesLayout.List) return branchNodes; - const hierarchy = Arrays.makeHierarchical( + const hierarchy = makeHierarchical( branchNodes, n => n.treeHierarchy, (...paths) => paths.join('/'), diff --git a/src/views/nodes/commitNode.ts b/src/views/nodes/commitNode.ts index 4a9152c..c5c6aab 100644 --- a/src/views/nodes/commitNode.ts +++ b/src/views/nodes/commitNode.ts @@ -4,8 +4,9 @@ import { ViewFilesLayout } from '../../configuration'; import { Colors, Commands } from '../../constants'; import { CommitFormatter } from '../../git/formatters'; import { GitBranch, GitCommit, GitRevisionReference } from '../../git/models'; -import { Arrays, Strings } from '../../system'; +import { makeHierarchical } from '../../system/array'; import { joinPaths, normalizePath } from '../../system/path'; +import { sortCompare } from '../../system/string'; import { FileHistoryView } from '../fileHistoryView'; import { TagsView } from '../tagsView'; import { ViewsWithCommits } from '../viewBase'; @@ -48,7 +49,7 @@ export class CommitNode extends ViewRefNode n.uri.relativePath.split('/'), (...parts: string[]) => normalizePath(joinPaths(...parts)), @@ -58,7 +59,7 @@ export class CommitNode extends ViewRefNode Strings.sortCompare(a.label!, b.label!)); + (children as FileNode[]).sort((a, b) => sortCompare(a.label!, b.label!)); } if (!(this.view instanceof TagsView) && !(this.view instanceof FileHistoryView)) { diff --git a/src/views/nodes/compareBranchNode.ts b/src/views/nodes/compareBranchNode.ts index ecff5bf..e572b2e 100644 --- a/src/views/nodes/compareBranchNode.ts +++ b/src/views/nodes/compareBranchNode.ts @@ -3,9 +3,12 @@ import { ViewShowBranchComparison } from '../../configuration'; import { GlyphChars } from '../../constants'; import { GitUri } from '../../git/gitUri'; import { GitBranch, GitRevision } from '../../git/models'; -import { CommandQuickPickItem, ReferencePicker } from '../../quickpicks'; +import { CommandQuickPickItem } from '../../quickpicks/items/common'; +import { ReferencePicker } from '../../quickpicks/referencePicker'; import { BranchComparison, BranchComparisons, WorkspaceState } from '../../storage'; -import { debug, gate, log, Strings } from '../../system'; +import { gate } from '../../system/decorators/gate'; +import { debug, log } from '../../system/decorators/log'; +import { pluralize } from '../../system/string'; import { BranchesView } from '../branchesView'; import { CommitsView } from '../commitsView'; import { RepositoriesView } from '../repositoriesView'; @@ -92,7 +95,7 @@ export class CompareBranchNode extends ViewNode { } static getPinnableId(repoPath: string, ref1: string, ref2: string) { - return Strings.md5(`${repoPath}|${ref1}|${ref2}`); + return md5(`${repoPath}|${ref1}|${ref2}`); } private _children: ViewNode[] | undefined; @@ -98,7 +100,7 @@ export class CompareResultsNode extends ViewNode { }, { id: 'behind', - description: Strings.pluralize('commit', aheadBehindCounts?.behind ?? 0), + description: pluralize('commit', aheadBehindCounts?.behind ?? 0), expand: false, }, ), @@ -119,7 +121,7 @@ export class CompareResultsNode extends ViewNode { }, { id: 'ahead', - description: Strings.pluralize('commit', aheadBehindCounts?.ahead ?? 0), + description: pluralize('commit', aheadBehindCounts?.ahead ?? 0), expand: false, }, ), @@ -249,7 +251,7 @@ export class CompareResultsNode extends ViewNode { } return { - label: `${Strings.pluralize('file', files?.length ?? 0, { zero: 'No' })} changed`, + label: `${pluralize('file', files?.length ?? 0, { zero: 'No' })} changed`, files: files, }; } @@ -279,7 +281,7 @@ export class CompareResultsNode extends ViewNode { } return { - label: `${Strings.pluralize('file', files?.length ?? 0, { zero: 'No' })} changed`, + label: `${pluralize('file', files?.length ?? 0, { zero: 'No' })} changed`, files: files, }; } @@ -320,7 +322,7 @@ export class CompareResultsNode extends ViewNode { const files = await this.view.container.git.getDiffStatus(this.uri.repoPath!, comparison); return { - label: `${Strings.pluralize('file', files?.length ?? 0, { zero: 'No' })} changed`, + label: `${pluralize('file', files?.length ?? 0, { zero: 'No' })} changed`, files: files, }; } diff --git a/src/views/nodes/contributorNode.ts b/src/views/nodes/contributorNode.ts index 93529dc..119c0f6 100644 --- a/src/views/nodes/contributorNode.ts +++ b/src/views/nodes/contributorNode.ts @@ -3,7 +3,9 @@ import { getPresenceDataUri } from '../../avatars'; import { GlyphChars } from '../../constants'; import { GitUri } from '../../git/gitUri'; import { GitContributor, GitLog } from '../../git/models'; -import { debug, gate, Iterables, Strings } from '../../system'; +import { gate } from '../../system/decorators/gate'; +import { debug } from '../../system/decorators/log'; +import { map } from '../../system/iterable'; import { pluralize } from '../../system/string'; import { ContactPresence } from '../../vsls/vsls'; import { ContributorsView } from '../contributorsView'; @@ -59,7 +61,7 @@ export class ContributorNode extends ViewNode new CommitNode(this.view, this, c, undefined, undefined, getBranchAndTagTips), ), @@ -123,11 +125,11 @@ export class ContributorNode extends ViewNode; + root?: HierarchicalItem; } export class FolderNode extends ViewNode { @@ -23,7 +24,7 @@ export class FolderNode extends ViewNode, + public readonly root: HierarchicalItem, private readonly containsWorkingFiles?: boolean, public readonly relativePath?: string, ) { @@ -76,7 +77,7 @@ export class FolderNode extends ViewNode { ); if (this.view.config.files.layout !== ViewFilesLayout.List) { - const hierarchy = Arrays.makeHierarchical( + const hierarchy = makeHierarchical( children, n => n.uri.relativePath.split('/'), (...parts: string[]) => normalizePath(joinPaths(...parts)), @@ -54,7 +55,7 @@ export class MergeStatusNode extends ViewNode { const root = new FolderNode(this.view, this, this.repoPath, '', hierarchy); children = root.getChildren() as FileNode[]; } else { - children.sort((a, b) => Strings.sortCompare(a.label!, b.label!)); + children.sort((a, b) => sortCompare(a.label!, b.label!)); } return children; @@ -71,9 +72,7 @@ export class MergeStatusNode extends ViewNode { ); item.id = this.id; item.contextValue = ContextValues.Merge; - item.description = this.status?.hasConflicts - ? Strings.pluralize('conflict', this.status.conflicts.length) - : undefined; + item.description = this.status?.hasConflicts ? pluralize('conflict', this.status.conflicts.length) : undefined; item.iconPath = this.status?.hasConflicts ? new ThemeIcon('warning', new ThemeColor('list.warningForeground')) : new ThemeIcon('debug-pause', new ThemeColor('list.foreground')); @@ -82,9 +81,7 @@ export class MergeStatusNode extends ViewNode { `${`Merging ${ this.mergeStatus.incoming != null ? GitReference.toString(this.mergeStatus.incoming) : '' }into ${GitReference.toString(this.mergeStatus.current)}`}${ - this.status?.hasConflicts - ? `\n\n${Strings.pluralize('conflicted file', this.status.conflicts.length)}` - : '' + this.status?.hasConflicts ? `\n\n${pluralize('conflicted file', this.status.conflicts.length)}` : '' }`, true, ); diff --git a/src/views/nodes/rebaseStatusNode.ts b/src/views/nodes/rebaseStatusNode.ts index 565901e..899c816 100644 --- a/src/views/nodes/rebaseStatusNode.ts +++ b/src/views/nodes/rebaseStatusNode.ts @@ -5,9 +5,10 @@ import { Commands, CoreCommands } from '../../constants'; import { CommitFormatter } from '../../git/formatters'; import { GitUri } from '../../git/gitUri'; import { GitBranch, GitCommit, GitRebaseStatus, GitReference, GitRevisionReference, GitStatus } from '../../git/models'; -import { Arrays, Strings } from '../../system'; +import { makeHierarchical } from '../../system/array'; import { executeCoreCommand } from '../../system/command'; import { joinPaths, normalizePath } from '../../system/path'; +import { pluralize, sortCompare } from '../../system/string'; import { ViewsWithCommits } from '../viewBase'; import { BranchNode } from './branchNode'; import { CommitFileNode } from './commitFileNode'; @@ -46,7 +47,7 @@ export class RebaseStatusNode extends ViewNode { this.status?.conflicts.map(f => new MergeConflictFileNode(this.view, this, this.rebaseStatus, f)) ?? []; if (this.view.config.files.layout !== ViewFilesLayout.List) { - const hierarchy = Arrays.makeHierarchical( + const hierarchy = makeHierarchical( children, n => n.uri.relativePath.split('/'), (...parts: string[]) => normalizePath(joinPaths(...parts)), @@ -56,7 +57,7 @@ export class RebaseStatusNode extends ViewNode { const root = new FolderNode(this.view, this, this.repoPath, '', hierarchy); children = root.getChildren() as FileNode[]; } else { - children.sort((a, b) => Strings.sortCompare(a.label!, b.label!)); + children.sort((a, b) => sortCompare(a.label!, b.label!)); } const commit = await this.view.container.git.getCommit( @@ -81,9 +82,7 @@ export class RebaseStatusNode extends ViewNode { ); item.id = this.id; item.contextValue = ContextValues.Rebase; - item.description = this.status?.hasConflicts - ? Strings.pluralize('conflict', this.status.conflicts.length) - : undefined; + item.description = this.status?.hasConflicts ? pluralize('conflict', this.status.conflicts.length) : undefined; item.iconPath = this.status?.hasConflicts ? new ThemeIcon('warning', new ThemeColor('list.warningForeground')) : new ThemeIcon('debug-pause', new ThemeColor('list.foreground')); @@ -96,11 +95,7 @@ export class RebaseStatusNode extends ViewNode { } of ${this.rebaseStatus.steps.total}\\\nPaused at ${GitReference.toString( this.rebaseStatus.steps.current.commit, { icon: true }, - )}${ - this.status?.hasConflicts - ? `\n\n${Strings.pluralize('conflicted file', this.status.conflicts.length)}` - : '' - }`, + )}${this.status?.hasConflicts ? `\n\n${pluralize('conflicted file', this.status.conflicts.length)}` : ''}`, true, ); markdown.supportHtml = true; @@ -139,7 +134,7 @@ export class RebaseCommitNode extends ViewRefNode new CommitFileNode(this.view, this, c.file!, c)); if (this.view.config.files.layout !== ViewFilesLayout.List) { - const hierarchy = Arrays.makeHierarchical( + const hierarchy = makeHierarchical( children, n => n.uri.relativePath.split('/'), (...parts: string[]) => normalizePath(joinPaths(...parts)), @@ -149,7 +144,7 @@ export class RebaseCommitNode extends ViewRefNode Strings.sortCompare(a.label!, b.label!)); + children.sort((a, b) => sortCompare(a.label!, b.label!)); } return children; diff --git a/src/views/nodes/reflogNode.ts b/src/views/nodes/reflogNode.ts index cf73180..cbee16a 100644 --- a/src/views/nodes/reflogNode.ts +++ b/src/views/nodes/reflogNode.ts @@ -1,7 +1,8 @@ import { TreeItem, TreeItemCollapsibleState } from 'vscode'; import { GitUri } from '../../git/gitUri'; import { GitReflog, Repository } from '../../git/models'; -import { debug, gate } from '../../system'; +import { gate } from '../../system/decorators/gate'; +import { debug } from '../../system/decorators/log'; import { RepositoriesView } from '../repositoriesView'; import { LoadMoreNode, MessageNode } from './common'; import { ReflogRecordNode } from './reflogRecordNode'; diff --git a/src/views/nodes/reflogRecordNode.ts b/src/views/nodes/reflogRecordNode.ts index 946a6a9..27cfa24 100644 --- a/src/views/nodes/reflogRecordNode.ts +++ b/src/views/nodes/reflogRecordNode.ts @@ -2,7 +2,9 @@ import { TreeItem, TreeItemCollapsibleState, window } from 'vscode'; import { GlyphChars } from '../../constants'; import { GitUri } from '../../git/gitUri'; import { GitLog, GitReflogRecord } from '../../git/models'; -import { debug, gate, Iterables } from '../../system'; +import { gate } from '../../system/decorators/gate'; +import { debug } from '../../system/decorators/log'; +import { map } from '../../system/iterable'; import { ViewsWithCommits } from '../viewBase'; import { CommitNode } from './commitNode'; import { LoadMoreNode, MessageNode } from './common'; @@ -44,7 +46,7 @@ export class ReflogRecordNode extends ViewNode implements Page if (log === undefined) return [new MessageNode(this.view, this, 'No commits could be found.')]; const children: (CommitNode | LoadMoreNode)[] = [ - ...Iterables.map(log.commits.values(), c => new CommitNode(this.view, this, c)), + ...map(log.commits.values(), c => new CommitNode(this.view, this, c)), ]; if (log.hasMore) { diff --git a/src/views/nodes/remoteNode.ts b/src/views/nodes/remoteNode.ts index 5cfa853..2adb8f3 100644 --- a/src/views/nodes/remoteNode.ts +++ b/src/views/nodes/remoteNode.ts @@ -3,7 +3,8 @@ import { ViewBranchesLayout } from '../../configuration'; import { GlyphChars } from '../../constants'; import { GitUri } from '../../git/gitUri'; import { GitRemote, GitRemoteType, Repository } from '../../git/models'; -import { Arrays, log } from '../../system'; +import { makeHierarchical } from '../../system/array'; +import { log } from '../../system/decorators/log'; import { RemotesView } from '../remotesView'; import { RepositoriesView } from '../repositoriesView'; import { BranchNode } from './branchNode'; @@ -54,7 +55,7 @@ export class RemoteNode extends ViewNode { ); if (this.view.config.branches.layout === ViewBranchesLayout.List) return branchNodes; - const hierarchy = Arrays.makeHierarchical( + const hierarchy = makeHierarchical( branchNodes, n => n.treeHierarchy, (...paths) => paths.join('/'), diff --git a/src/views/nodes/remotesNode.ts b/src/views/nodes/remotesNode.ts index ba014df..1fef2ad 100644 --- a/src/views/nodes/remotesNode.ts +++ b/src/views/nodes/remotesNode.ts @@ -1,7 +1,8 @@ import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from 'vscode'; import { GitUri } from '../../git/gitUri'; import { Repository } from '../../git/models'; -import { debug, gate } from '../../system'; +import { gate } from '../../system/decorators/gate'; +import { debug } from '../../system/decorators/log'; import { RemotesView } from '../remotesView'; import { RepositoriesView } from '../repositoriesView'; import { MessageNode } from './common'; diff --git a/src/views/nodes/repositoryNode.ts b/src/views/nodes/repositoryNode.ts index 6d98624..e4e2f7b 100644 --- a/src/views/nodes/repositoryNode.ts +++ b/src/views/nodes/repositoryNode.ts @@ -11,11 +11,11 @@ import { RepositoryChangeEvent, RepositoryFileSystemChangeEvent, } from '../../git/models'; -import { Strings } from '../../system'; import { findLastIndex } from '../../system/array'; import { gate } from '../../system/decorators/gate'; import { debug, log } from '../../system/decorators/log'; import { disposableInterval } from '../../system/function'; +import { pad } from '../../system/string'; import { RepositoriesView } from '../repositoriesView'; import { BranchesNode } from './branchesNode'; import { BranchNode } from './branchNode'; @@ -180,10 +180,7 @@ export class RepositoryNode extends SubscribeableViewNode { let description; let tooltip = `${this.repo.formattedName ?? this.uri.repoPath ?? ''}${ lastFetched - ? `${Strings.pad(GlyphChars.Dash, 2, 2)}Last fetched ${Repository.formatLastFetched( - lastFetched, - false, - )}` + ? `${pad(GlyphChars.Dash, 2, 2)}Last fetched ${Repository.formatLastFetched(lastFetched, false)}` : '' }${this.repo.formattedName ? `\n${this.uri.repoPath}` : ''}`; let iconSuffix = ''; @@ -201,12 +198,12 @@ export class RepositoryNode extends SubscribeableViewNode { if (this.view.config.includeWorkingTree && status.files.length !== 0) { workingStatus = status.getFormattedDiffStatus({ compact: true, - prefix: Strings.pad(GlyphChars.Dot, 1, 1), + prefix: pad(GlyphChars.Dot, 1, 1), }); } const upstreamStatus = status.getUpstreamStatus({ - suffix: Strings.pad(GlyphChars.Dot, 1, 1), + suffix: pad(GlyphChars.Dot, 1, 1), }); description = `${upstreamStatus}${status.branch}${status.rebasing ? ' (Rebasing)' : ''}${workingStatus}`; @@ -257,9 +254,7 @@ export class RepositoryNode extends SubscribeableViewNode { item.id = this.id; item.contextValue = contextValue; item.description = `${description ?? ''}${ - lastFetched - ? `${Strings.pad(GlyphChars.Dot, 1, 1)}Last fetched ${Repository.formatLastFetched(lastFetched)}` - : '' + lastFetched ? `${pad(GlyphChars.Dot, 1, 1)}Last fetched ${Repository.formatLastFetched(lastFetched)}` : '' }`; item.iconPath = { dark: this.view.container.context.asAbsolutePath(`images/dark/icon-repo${iconSuffix}.svg`), diff --git a/src/views/nodes/resultsCommitsNode.ts b/src/views/nodes/resultsCommitsNode.ts index 27e4f48..317001b 100644 --- a/src/views/nodes/resultsCommitsNode.ts +++ b/src/views/nodes/resultsCommitsNode.ts @@ -1,7 +1,9 @@ import { TreeItem, TreeItemCollapsibleState } from 'vscode'; import { GitUri } from '../../git/gitUri'; import { GitLog } from '../../git/models'; -import { debug, gate, Iterables } from '../../system'; +import { gate } from '../../system/decorators/gate'; +import { debug } from '../../system/decorators/log'; +import { map } from '../../system/iterable'; import { cancellable, PromiseCancelledError } from '../../system/promise'; import { ViewsWithCommits } from '../viewBase'; import { AutolinkedItemsNode } from './autolinkedItemsNode'; @@ -99,7 +101,7 @@ export class ResultsCommitsNode new CommitNode(this.view, this, c, undefined, undefined, getBranchAndTagTips, options), ), diff --git a/src/views/nodes/resultsFilesNode.ts b/src/views/nodes/resultsFilesNode.ts index 401ec89..c3e169e 100644 --- a/src/views/nodes/resultsFilesNode.ts +++ b/src/views/nodes/resultsFilesNode.ts @@ -2,9 +2,13 @@ import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from 'vscode'; import { ViewFilesLayout } from '../../configuration'; import { GitUri } from '../../git/gitUri'; import { GitFile } from '../../git/models'; -import { Arrays, debug, gate, Iterables, Strings } from '../../system'; +import { makeHierarchical } from '../../system/array'; +import { gate } from '../../system/decorators/gate'; +import { debug } from '../../system/decorators/log'; +import { map } from '../../system/iterable'; import { joinPaths, normalizePath } from '../../system/path'; import { cancellable, PromiseCancelledError } from '../../system/promise'; +import { sortCompare } from '../../system/string'; import { ViewsWithCommits } from '../viewBase'; import { FileNode, FolderNode } from './folderNode'; import { ResultsFileNode } from './resultsFileNode'; @@ -68,14 +72,14 @@ export class ResultsFilesNode extends ViewNode { if (files == null) return []; let children: FileNode[] = [ - ...Iterables.map( + ...map( files, s => new ResultsFileNode(this.view, this, this.repoPath, s, this.ref1, this.ref2, this.direction), ), ]; if (this.view.config.files.layout !== ViewFilesLayout.List) { - const hierarchy = Arrays.makeHierarchical( + const hierarchy = makeHierarchical( children, n => n.uri.relativePath.split('/'), (...parts: string[]) => normalizePath(joinPaths(...parts)), @@ -85,7 +89,7 @@ export class ResultsFilesNode extends ViewNode { const root = new FolderNode(this.view, this, this.repoPath, '', hierarchy); children = root.getChildren() as FileNode[]; } else { - children.sort((a, b) => a.priority - b.priority || Strings.sortCompare(a.label!, b.label!)); + children.sort((a, b) => a.priority - b.priority || sortCompare(a.label!, b.label!)); } return children; diff --git a/src/views/nodes/searchResultsNode.ts b/src/views/nodes/searchResultsNode.ts index e90272c..c05afbf 100644 --- a/src/views/nodes/searchResultsNode.ts +++ b/src/views/nodes/searchResultsNode.ts @@ -3,7 +3,9 @@ import { executeGitCommand } from '../../commands/gitCommands.actions'; import { GitUri } from '../../git/gitUri'; import { GitLog } from '../../git/models'; import { SearchPattern } from '../../git/search'; -import { debug, gate, log, Strings } from '../../system'; +import { gate } from '../../system/decorators/gate'; +import { debug, log } from '../../system/decorators/log'; +import { md5, pluralize } from '../../system/string'; import { SearchAndCompareView } from '../searchAndCompareView'; import { RepositoryNode } from './repositoryNode'; import { CommitsQueryResults, ResultsCommitsNode } from './resultsCommitsNode'; @@ -27,7 +29,7 @@ export class SearchResultsNode extends ViewNode implements } static getPinnableId(repoPath: string, search: SearchPattern) { - return Strings.md5(`${repoPath}|${SearchPattern.toKey(search)}`); + return md5(`${repoPath}|${SearchPattern.toKey(search)}`); } static override is(node: any): node is SearchResultsNode { @@ -242,7 +244,7 @@ export class SearchResultsNode extends ViewNode implements const resultsType = label.resultsType === undefined ? { singular: 'result', plural: 'results' } : label.resultsType; - return `${Strings.pluralize(resultsType.singular, count, { + return `${pluralize(resultsType.singular, count, { format: c => (log?.hasMore ? `${c}+` : undefined), plural: resultsType.plural, zero: 'No', diff --git a/src/views/nodes/stashNode.ts b/src/views/nodes/stashNode.ts index 20ffe4f..bae90b6 100644 --- a/src/views/nodes/stashNode.ts +++ b/src/views/nodes/stashNode.ts @@ -2,8 +2,9 @@ import { TreeItem, TreeItemCollapsibleState } from 'vscode'; import { ViewFilesLayout } from '../../config'; import { CommitFormatter } from '../../git/formatters'; import { GitStashCommit, GitStashReference } from '../../git/models'; -import { Arrays, Strings } from '../../system'; +import { makeHierarchical } from '../../system/array'; import { joinPaths, normalizePath } from '../../system/path'; +import { sortCompare } from '../../system/string'; import { ContextValues, FileNode, FolderNode, RepositoryNode, StashFileNode, ViewNode, ViewRefNode } from '../nodes'; import { RepositoriesView } from '../repositoriesView'; import { StashesView } from '../stashesView'; @@ -36,7 +37,7 @@ export class StashNode extends ViewRefNode new StashFileNode(this.view, this, c.file!, c as GitStashCommit)); if (this.view.config.files.layout !== ViewFilesLayout.List) { - const hierarchy = Arrays.makeHierarchical( + const hierarchy = makeHierarchical( children, n => n.uri.relativePath.split('/'), (...parts: string[]) => normalizePath(joinPaths(...parts)), @@ -46,7 +47,7 @@ export class StashNode extends ViewRefNode Strings.sortCompare(a.label!, b.label!)); + children.sort((a, b) => sortCompare(a.label!, b.label!)); } return children; } diff --git a/src/views/nodes/stashesNode.ts b/src/views/nodes/stashesNode.ts index 123238f..b32f642 100644 --- a/src/views/nodes/stashesNode.ts +++ b/src/views/nodes/stashesNode.ts @@ -1,7 +1,9 @@ import { TreeItem, TreeItemCollapsibleState } from 'vscode'; import { GitUri } from '../../git/gitUri'; import { Repository } from '../../git/models'; -import { debug, gate, Iterables } from '../../system'; +import { gate } from '../../system/decorators/gate'; +import { debug } from '../../system/decorators/log'; +import { map } from '../../system/iterable'; import { RepositoriesView } from '../repositoriesView'; import { StashesView } from '../stashesView'; import { MessageNode } from './common'; @@ -30,7 +32,7 @@ export class StashesNode extends ViewNode { const stash = await this.repo.getStash(); if (stash == null) return [new MessageNode(this.view, this, 'No stashes could be found.')]; - this._children = [...Iterables.map(stash.commits.values(), c => new StashNode(this.view, this, c))]; + this._children = [...map(stash.commits.values(), c => new StashNode(this.view, this, c))]; } return this._children; diff --git a/src/views/nodes/statusFileNode.ts b/src/views/nodes/statusFileNode.ts index 2beae81..9b952a2 100644 --- a/src/views/nodes/statusFileNode.ts +++ b/src/views/nodes/statusFileNode.ts @@ -5,8 +5,8 @@ import { Commands } from '../../constants'; import { StatusFileFormatter } from '../../git/formatters/statusFormatter'; import { GitUri } from '../../git/gitUri'; import { GitCommit, GitFile } from '../../git/models'; -import { Strings } from '../../system'; import { joinPaths, relativeDir } from '../../system/path'; +import { pluralize } from '../../system/string'; import { ViewsWithCommits } from '../viewBase'; import { FileRevisionAsCommitNode } from './fileRevisionAsCommitNode'; import { FileNode } from './folderNode'; @@ -215,7 +215,7 @@ export class StatusFileNode extends ViewNode implements FileNo } if (commits > 0) { - changedIn.push(Strings.pluralize('commit', commits)); + changedIn.push(pluralize('commit', commits)); } if (changedIn.length > 2) { diff --git a/src/views/nodes/tagNode.ts b/src/views/nodes/tagNode.ts index 238a21b..bc83315 100644 --- a/src/views/nodes/tagNode.ts +++ b/src/views/nodes/tagNode.ts @@ -5,7 +5,10 @@ import { Container } from '../../container'; import { emojify } from '../../emojis'; import { GitUri } from '../../git/gitUri'; import { GitLog, GitRevision, GitTag, GitTagReference } from '../../git/models'; -import { debug, gate, Iterables, Strings } from '../../system'; +import { gate } from '../../system/decorators/gate'; +import { debug } from '../../system/decorators/log'; +import { map } from '../../system/iterable'; +import { pad } from '../../system/string'; import { RepositoriesView } from '../repositoriesView'; import { TagsView } from '../tagsView'; import { CommitNode } from './commitNode'; @@ -50,7 +53,7 @@ export class TagNode extends ViewRefNode new CommitNode(this.view, this, c, undefined, undefined, getBranchAndTagTips), ), @@ -73,7 +76,7 @@ export class TagNode extends ViewRefNode { ); if (this.view.config.branches.layout === ViewBranchesLayout.List) return tagNodes; - const hierarchy = Arrays.makeHierarchical( + const hierarchy = makeHierarchical( tagNodes, n => n.tag.name.split('/'), (...paths) => paths.join('/'), diff --git a/src/views/repositoriesView.ts b/src/views/repositoriesView.ts index 3237c7c..bb1073d 100644 --- a/src/views/repositoriesView.ts +++ b/src/views/repositoriesView.ts @@ -30,8 +30,8 @@ import { GitTagReference, } from '../git/models'; import { WorkspaceState } from '../storage'; -import { gate } from '../system'; import { executeCommand } from '../system/command'; +import { gate } from '../system/decorators/gate'; import { BranchesNode, BranchNode, diff --git a/src/views/searchAndCompareView.ts b/src/views/searchAndCompareView.ts index 9651e39..2cb0e85 100644 --- a/src/views/searchAndCompareView.ts +++ b/src/views/searchAndCompareView.ts @@ -6,7 +6,8 @@ import { setContext } from '../context'; import { GitUri } from '../git/gitUri'; import { GitLog, GitRevision } from '../git/models'; import { SearchPattern } from '../git/search'; -import { ReferencePicker, ReferencesQuickPickIncludes, RepositoryPicker } from '../quickpicks'; +import { ReferencePicker, ReferencesQuickPickIncludes } from '../quickpicks/referencePicker'; +import { RepositoryPicker } from '../quickpicks/repositoryPicker'; import { NamedRef, PinnedItem, PinnedItems, WorkspaceState } from '../storage'; import { filterMap } from '../system/array'; import { executeCommand } from '../system/command'; diff --git a/src/views/viewCommands.ts b/src/views/viewCommands.ts index 35df987..05bfd5c 100644 --- a/src/views/viewCommands.ts +++ b/src/views/viewCommands.ts @@ -13,7 +13,6 @@ import { Container } from '../container'; import { setContext } from '../context'; import { GitUri } from '../git/gitUri'; import { GitReference, GitRevision } from '../git/models'; -import { debug } from '../system'; import { executeActionCommand, executeCommand, @@ -21,6 +20,7 @@ import { executeCoreGitCommand, executeEditorCommand, } from '../system/command'; +import { debug } from '../system/decorators/log'; import { runGitCommandInTerminal } from '../terminal'; import { BranchesNode, diff --git a/src/vsls/guest.ts b/src/vsls/guest.ts index 0de47a5..d8c9401 100644 --- a/src/vsls/guest.ts +++ b/src/vsls/guest.ts @@ -3,7 +3,7 @@ import type { LiveShare, SharedServiceProxy } from '../@types/vsls'; import { Container } from '../container'; import { GitCommandOptions } from '../git/commandOptions'; import { Logger } from '../logger'; -import { debug, log } from '../system'; +import { debug, log } from '../system/decorators/log'; import { VslsHostService } from './host'; import { GetRepositoriesForUriRequestType, GitCommandRequestType, RepositoryProxy, RequestType } from './protocol'; diff --git a/src/webviews/rebaseEditor.ts b/src/webviews/rebaseEditor.ts index c0fb26b..e6959bd 100644 --- a/src/webviews/rebaseEditor.ts +++ b/src/webviews/rebaseEditor.ts @@ -20,8 +20,10 @@ import { Container } from '../container'; import { RepositoryChange, RepositoryChangeComparisonMode } from '../git/models'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { debug, gate, Iterables } from '../system'; import { executeCoreCommand } from '../system/command'; +import { gate } from '../system/decorators/gate'; +import { debug } from '../system/decorators/log'; +import { join, map } from '../system/iterable'; import { normalizePath } from '../system/path'; import { Author, @@ -528,8 +530,8 @@ async function parseRebaseTodo( const commits: Commit[] = []; const log = await container.git.getLogForSearch(repoPath, { - pattern: `${onto ? `#:${onto} ` : ''}${Iterables.join( - Iterables.map(entries, e => `#:${e.ref}`), + pattern: `${onto ? `#:${onto} ` : ''}${join( + map(entries, e => `#:${e.ref}`), ' ', )}`, });