From 32183eca735be0b076db3b91033ad4ac1f55e5ad Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Fri, 16 Oct 2020 03:54:35 -0400 Subject: [PATCH] Combines search & compare views --- images/dark/icon-pin-small-selected.svg | 4 - images/dark/icon-pin-small.svg | 3 - images/dark/icon-pin.svg | 3 - images/dark/icon-unpin.svg | 3 - images/light/icon-pin-small-selected.svg | 4 - images/light/icon-pin-small.svg | 3 - images/light/icon-pin.svg | 3 - images/light/icon-unpin.svg | 3 - package.json | 540 +++++++-------------- src/commands/common.ts | 8 +- src/commands/diffBranchWith.ts | 2 +- src/commands/git/search.ts | 29 +- src/commands/quickCommand.buttons.ts | 2 +- src/commands/quickCommand.steps.ts | 16 +- src/commands/searchCommits.ts | 4 +- src/commands/showView.ts | 9 +- src/config.ts | 17 +- src/constants.ts | 36 +- src/container.ts | 25 +- src/quickpicks/commitQuickPickItems.ts | 4 +- src/quickpicks/quickPicksItems.ts | 2 +- src/views/compareView.ts | 184 ------- src/views/nodes.ts | 4 +- src/views/nodes/compareBranchNode.ts | 38 +- src/views/nodes/compareNode.ts | 231 --------- src/views/nodes/comparePickerNode.ts | 28 +- src/views/nodes/compareResultsNode.ts | 130 +++-- src/views/nodes/pullRequestNode.ts | 7 +- src/views/nodes/resultsCommitsNode.ts | 94 ++-- src/views/nodes/searchNode.ts | 147 ------ src/views/nodes/searchResultsCommitsNode.ts | 64 --- src/views/nodes/searchResultsNode.ts | 297 ++++++++++++ src/views/nodes/viewNode.ts | 6 +- src/views/searchAndCompareView.ts | 502 +++++++++++++++++++ src/views/searchView.ts | 279 ----------- src/views/viewBase.ts | 15 +- src/views/viewCommands.ts | 12 +- .../apps/settings/partials/views.compare.html | 140 ------ .../apps/settings/partials/views.search.html | 135 ------ .../settings/partials/views.searchAndCompare.html | 168 +++++++ src/webviews/apps/settings/settings.html | 19 +- src/webviews/settingsWebview.ts | 3 +- 42 files changed, 1381 insertions(+), 1842 deletions(-) delete mode 100644 images/dark/icon-pin-small-selected.svg delete mode 100644 images/dark/icon-pin-small.svg delete mode 100644 images/dark/icon-pin.svg delete mode 100644 images/dark/icon-unpin.svg delete mode 100644 images/light/icon-pin-small-selected.svg delete mode 100644 images/light/icon-pin-small.svg delete mode 100644 images/light/icon-pin.svg delete mode 100644 images/light/icon-unpin.svg delete mode 100644 src/views/compareView.ts delete mode 100644 src/views/nodes/compareNode.ts delete mode 100644 src/views/nodes/searchNode.ts delete mode 100644 src/views/nodes/searchResultsCommitsNode.ts create mode 100644 src/views/nodes/searchResultsNode.ts create mode 100644 src/views/searchAndCompareView.ts delete mode 100644 src/views/searchView.ts delete mode 100644 src/webviews/apps/settings/partials/views.compare.html delete mode 100644 src/webviews/apps/settings/partials/views.search.html create mode 100644 src/webviews/apps/settings/partials/views.searchAndCompare.html diff --git a/images/dark/icon-pin-small-selected.svg b/images/dark/icon-pin-small-selected.svg deleted file mode 100644 index 38189d1..0000000 --- a/images/dark/icon-pin-small-selected.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/images/dark/icon-pin-small.svg b/images/dark/icon-pin-small.svg deleted file mode 100644 index dd7f23f..0000000 --- a/images/dark/icon-pin-small.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/images/dark/icon-pin.svg b/images/dark/icon-pin.svg deleted file mode 100644 index 36c0c46..0000000 --- a/images/dark/icon-pin.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/images/dark/icon-unpin.svg b/images/dark/icon-unpin.svg deleted file mode 100644 index f89718c..0000000 --- a/images/dark/icon-unpin.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/images/light/icon-pin-small-selected.svg b/images/light/icon-pin-small-selected.svg deleted file mode 100644 index 6297c87..0000000 --- a/images/light/icon-pin-small-selected.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/images/light/icon-pin-small.svg b/images/light/icon-pin-small.svg deleted file mode 100644 index e0306dc..0000000 --- a/images/light/icon-pin-small.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/images/light/icon-pin.svg b/images/light/icon-pin.svg deleted file mode 100644 index 4ba1a75..0000000 --- a/images/light/icon-pin.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/images/light/icon-unpin.svg b/images/light/icon-unpin.svg deleted file mode 100644 index 623ba8f..0000000 --- a/images/light/icon-unpin.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/package.json b/package.json index 0d29332..056534a 100644 --- a/package.json +++ b/package.json @@ -595,10 +595,6 @@ "scope": "window" }, "gitlens.gitCommands.search.showResultsInView": { - "type": "boolean", - "default": true, - "markdownDescription": "Specifies whether to show the commit search results in the _Search Commits_ view or directly within the quick pick menu", - "scope": "window", "deprecationMessage": "Depreciated: This setting has been renamed to gitlens.gitCommands.search.showResultsInSideBar", "markdownDeprecationMessage": "Depreciated: This setting has been renamed to `#gitlens.gitCommands.search.showResultsInSideBar#`" }, @@ -1696,52 +1692,6 @@ "markdownDescription": "Specifies whether to show a comparison of the current branch or the working tree with a user-selected reference (branch, tag. etc) in the _Commits_ view", "scope": "window" }, - "gitlens.views.compare.avatars": { - "type": "boolean", - "default": true, - "markdownDescription": "Specifies whether to show avatar images instead of commit (or status) icons in the _Compare Commits_ view", - "scope": "window" - }, - "gitlens.views.compare.files.compact": { - "type": "boolean", - "default": true, - "markdownDescription": "Specifies whether to compact (flatten) unnecessary file nesting in the _Compare Commits_ view. Only applies when `#gitlens.views.compare.files.layout#` is set to `tree` or `auto`", - "scope": "window" - }, - "gitlens.views.compare.files.layout": { - "type": "string", - "default": "auto", - "enum": [ - "auto", - "list", - "tree" - ], - "enumDescriptions": [ - "Automatically switches between displaying files as a `tree` or `list` based on the `#gitlens.views.compare.files.threshold#` value and the number of files at each nesting level", - "Displays files as a list", - "Displays files as a tree" - ], - "markdownDescription": "Specifies how the _Compare Commits_ view will display files", - "scope": "window" - }, - "gitlens.views.compare.files.threshold": { - "type": "number", - "default": 5, - "markdownDescription": "Specifies when to switch between displaying files as a `tree` or `list` based on the number of files in a nesting level in the _Compare Commits_ view. Only applies when `#gitlens.views.compare.files.layout#` is set to `auto`", - "scope": "window" - }, - "gitlens.views.compare.pullRequests.enabled": { - "type": "boolean", - "default": true, - "markdownDescription": "Specifies whether to query for pull requests associated with branches and commits in the _Compare Commits_ view. Requires a connection to a supported remote service (e.g. GitHub)", - "scope": "window" - }, - "gitlens.views.compare.pullRequests.showForCommits": { - "type": "boolean", - "default": true, - "markdownDescription": "Specifies whether to show pull requests (if any) associated with commits in the _Compare Commits_ view. Requires a connection to a supported remote service (e.g. GitHub)", - "scope": "window" - }, "gitlens.views.contributors.avatars": { "type": "boolean", "default": true, @@ -2003,19 +1953,19 @@ "markdownDescription": "Specifies whether to show a comparison of the current branch or the working tree with a user-selected reference (branch, tag. etc) in the _Repositories_ view", "scope": "window" }, - "gitlens.views.search.avatars": { + "gitlens.views.searchAndCompare.avatars": { "type": "boolean", "default": true, - "markdownDescription": "Specifies whether to show avatar images instead of commit (or status) icons in the _Search Commits_ view", + "markdownDescription": "Specifies whether to show avatar images instead of commit (or status) icons in the _Search & Compare_ view", "scope": "window" }, - "gitlens.views.search.files.compact": { + "gitlens.views.searchAndCompare.files.compact": { "type": "boolean", "default": true, - "markdownDescription": "Specifies whether to compact (flatten) unnecessary file nesting in the _Search Commits_ view. Only applies when `#gitlens.views.search.files.layout#` is set to `tree` or `auto`", + "markdownDescription": "Specifies whether to compact (flatten) unnecessary file nesting in the _Search & Compare_ view. Only applies when `#gitlens.views.searchAndCompare.files.layout#` is set to `tree` or `auto`", "scope": "window" }, - "gitlens.views.search.files.layout": { + "gitlens.views.searchAndCompare.files.layout": { "type": "string", "default": "auto", "enum": [ @@ -2024,29 +1974,29 @@ "tree" ], "enumDescriptions": [ - "Automatically switches between displaying files as a `tree` or `list` based on the `#gitlens.views.search.files.threshold#` value and the number of files at each nesting level", + "Automatically switches between displaying files as a `tree` or `list` based on the `#gitlens.views.searchAndCompare.files.threshold#` value and the number of files at each nesting level", "Displays files as a list", "Displays files as a tree" ], - "markdownDescription": "Specifies how the _Search Commits_ view will display files", + "markdownDescription": "Specifies how the _Search & Compare_ view will display files", "scope": "window" }, - "gitlens.views.search.files.threshold": { + "gitlens.views.searchAndCompare.files.threshold": { "type": "number", "default": 5, - "markdownDescription": "Specifies when to switch between displaying files as a `tree` or `list` based on the number of files in a nesting level in the _Search Commits_ view. Only applies when `#gitlens.views.search.files.layout#` is set to `auto`", + "markdownDescription": "Specifies when to switch between displaying files as a `tree` or `list` based on the number of files in a nesting level in the _Search & Compare_ view. Only applies when `#gitlens.views.searchAndCompare.files.layout#` is set to `auto`", "scope": "window" }, - "gitlens.views.search.pullRequests.enabled": { + "gitlens.views.searchAndCompare.pullRequests.enabled": { "type": "boolean", "default": true, - "markdownDescription": "Specifies whether to query for pull requests associated with commits in the _Search Commits_ view. Requires a connection to a supported remote service (e.g. GitHub)", + "markdownDescription": "Specifies whether to query for pull requests associated with commits in the _Search & Compare_ view. Requires a connection to a supported remote service (e.g. GitHub)", "scope": "window" }, - "gitlens.views.search.pullRequests.showForCommits": { + "gitlens.views.searchAndCompare.pullRequests.showForCommits": { "type": "boolean", "default": true, - "markdownDescription": "Specifies whether to show pull requests (if any) associated with commits in the _Search Commits_ view. Requires a connection to a supported remote service (e.g. GitHub)", + "markdownDescription": "Specifies whether to show pull requests (if any) associated with commits in the _Search & Compare_ view. Requires a connection to a supported remote service (e.g. GitHub)", "scope": "window" }, "gitlens.views.showRelativeDateMarkers": { @@ -2464,12 +2414,6 @@ "icon": "$(gear)" }, { - "command": "gitlens.showSettingsPage#compare-view", - "title": "Open Settings", - "category": "GitLens", - "icon": "$(gear)" - }, - { "command": "gitlens.showSettingsPage#contributors-view", "title": "Open Settings", "category": "GitLens", @@ -2500,7 +2444,7 @@ "icon": "$(gear)" }, { - "command": "gitlens.showSettingsPage#search-commits-view", + "command": "gitlens.showSettingsPage#search-compare-view", "title": "Open Settings", "category": "GitLens", "icon": "$(gear)" @@ -2523,11 +2467,6 @@ "category": "GitLens" }, { - "command": "gitlens.showCompareView", - "title": "Show Compare View", - "category": "GitLens" - }, - { "command": "gitlens.showFileHistoryView", "title": "Show File History View", "category": "GitLens" @@ -2543,8 +2482,8 @@ "category": "GitLens" }, { - "command": "gitlens.showSearchView", - "title": "Show Search Commits View", + "command": "gitlens.showSearchAndCompareView", + "title": "Show Search And Compare Commits View", "category": "GitLens" }, { @@ -3767,96 +3706,6 @@ "category": "GitLens" }, { - "command": "gitlens.views.compare.clear", - "title": "Clear Results", - "category": "GitLens", - "icon": "$(clear-all)" - }, - { - "command": "gitlens.views.compare.copy", - "title": "Copy", - "category": "GitLens" - }, - { - "command": "gitlens.views.compare.pinComparison", - "title": "Pin Comparison", - "category": "GitLens", - "icon": { - "dark": "images/dark/icon-pin.svg", - "light": "images/light/icon-pin.svg" - } - }, - { - "command": "gitlens.views.compare.unpinComparison", - "title": "Unpin Comparison", - "category": "GitLens", - "icon": "$(pin)" - }, - { - "command": "gitlens.views.compare.refresh", - "title": "Refresh", - "category": "GitLens", - "icon": "$(refresh)" - }, - { - "command": "gitlens.views.compare.selectForCompare", - "title": "Compare References...", - "category": "GitLens", - "icon": "$(compare-changes)" - }, - { - "command": "gitlens.views.compare.setFilesLayoutToAuto", - "title": "Automatic Layout", - "category": "GitLens", - "icon": "$(list-tree)" - }, - { - "command": "gitlens.views.compare.setFilesLayoutToList", - "title": "List Layout", - "category": "GitLens", - "icon": { - "dark": "images/dark/icon-view-auto.svg", - "light": "images/light/icon-view-auto.svg" - } - }, - { - "command": "gitlens.views.compare.setFilesLayoutToTree", - "title": "Tree Layout", - "category": "GitLens", - "icon": "$(list-flat)" - }, - { - "command": "gitlens.views.compare.setKeepResultsToOn", - "title": "Keep Results", - "category": "GitLens", - "icon": "$(unlock)" - }, - { - "command": "gitlens.views.compare.setKeepResultsToOff", - "title": "Keep Results", - "category": "GitLens", - "icon": "$(lock)" - }, - { - "command": "gitlens.views.compare.setShowAvatarsOn", - "title": "Show Avatars", - "category": "GitLens" - }, - { - "command": "gitlens.views.compare.setShowAvatarsOff", - "title": "Hide Avatars", - "category": "GitLens" - }, - { - "command": "gitlens.views.compare.swapComparison", - "title": "Swap Comparison", - "category": "GitLens", - "icon": { - "dark": "images/dark/icon-swap.svg", - "light": "images/light/icon-swap.svg" - } - }, - { "command": "gitlens.views.contributors.copy", "title": "Copy", "category": "GitLens" @@ -4129,36 +3978,60 @@ "category": "GitLens" }, { - "command": "gitlens.views.search.clear", + "command": "gitlens.views.searchAndCompare.clear", "title": "Clear Results", "category": "GitLens", "icon": "$(clear-all)" }, { - "command": "gitlens.views.search.copy", + "command": "gitlens.views.searchAndCompare.copy", "title": "Copy", "category": "GitLens" }, { - "command": "gitlens.views.search.refresh", + "command": "gitlens.views.searchAndCompare.pin", + "title": "Pin", + "category": "GitLens", + "icon": "$(pin)" + }, + { + "command": "gitlens.views.searchAndCompare.unpin", + "title": "Unpin", + "category": "GitLens", + "icon": "$(pinned)" + }, + { + "command": "gitlens.views.searchAndCompare.refresh", "title": "Refresh", "category": "GitLens", "icon": "$(refresh)" }, { - "command": "gitlens.views.search.searchCommits", + "command": "gitlens.views.searchAndCompare.searchCommits", "title": "Search Commits", "category": "GitLens", "icon": "$(search)" }, { - "command": "gitlens.views.search.setFilesLayoutToAuto", + "command": "gitlens.views.searchAndCompare.edit", + "title": "Edit", + "category": "GitLens", + "icon": "$(edit)" + }, + { + "command": "gitlens.views.searchAndCompare.selectForCompare", + "title": "Compare References...", + "category": "GitLens", + "icon": "$(compare-changes)" + }, + { + "command": "gitlens.views.searchAndCompare.setFilesLayoutToAuto", "title": "Automatic Layout", "category": "GitLens", "icon": "$(list-tree)" }, { - "command": "gitlens.views.search.setFilesLayoutToList", + "command": "gitlens.views.searchAndCompare.setFilesLayoutToList", "title": "List Layout", "category": "GitLens", "icon": { @@ -4167,34 +4040,43 @@ } }, { - "command": "gitlens.views.search.setFilesLayoutToTree", + "command": "gitlens.views.searchAndCompare.setFilesLayoutToTree", "title": "Tree Layout", "category": "GitLens", "icon": "$(list-flat)" }, { - "command": "gitlens.views.search.setKeepResultsToOn", + "command": "gitlens.views.searchAndCompare.setKeepResultsToOn", "title": "Keep Results", "category": "GitLens", "icon": "$(unlock)" }, { - "command": "gitlens.views.search.setKeepResultsToOff", + "command": "gitlens.views.searchAndCompare.setKeepResultsToOff", "title": "Keep Results", "category": "GitLens", "icon": "$(lock)" }, { - "command": "gitlens.views.search.setShowAvatarsOn", + "command": "gitlens.views.searchAndCompare.setShowAvatarsOn", "title": "Show Avatars", "category": "GitLens" }, { - "command": "gitlens.views.search.setShowAvatarsOff", + "command": "gitlens.views.searchAndCompare.setShowAvatarsOff", "title": "Hide Avatars", "category": "GitLens" }, { + "command": "gitlens.views.searchAndCompare.swapComparison", + "title": "Swap Comparison", + "category": "GitLens", + "icon": { + "dark": "images/dark/icon-swap.svg", + "light": "images/light/icon-swap.svg" + } + }, + { "command": "gitlens.views.stashes.copy", "title": "Copy", "category": "GitLens" @@ -4292,10 +4174,6 @@ "when": "false" }, { - "command": "gitlens.showSettingsPage#compare-view", - "when": "false" - }, - { "command": "gitlens.showSettingsPage#contributors-view", "when": "false" }, @@ -4316,7 +4194,7 @@ "when": "false" }, { - "command": "gitlens.showSettingsPage#search-commits-view", + "command": "gitlens.showSettingsPage#search-compare-view", "when": "false" }, { @@ -4328,10 +4206,6 @@ "when": "false" }, { - "command": "gitlens.showCompareView", - "when": "gitlens:enabled" - }, - { "command": "gitlens.showFileHistoryView", "when": "gitlens:enabled" }, @@ -4344,7 +4218,7 @@ "when": "gitlens:enabled" }, { - "command": "gitlens.showSearchView", + "command": "gitlens.showSearchAndCompareView", "when": "gitlens:enabled" }, { @@ -5084,62 +4958,6 @@ "when": "false" }, { - "command": "gitlens.views.compare.clear", - "when": "false" - }, - { - "command": "gitlens.views.compare.copy", - "when": "false" - }, - { - "command": "gitlens.views.compare.pinComparison", - "when": "false" - }, - { - "command": "gitlens.views.compare.unpinComparison", - "when": "false" - }, - { - "command": "gitlens.views.compare.refresh", - "when": "false" - }, - { - "command": "gitlens.views.compare.selectForCompare", - "when": "gitlens:enabled && config.gitlens.views.compare.enabled" - }, - { - "command": "gitlens.views.compare.setFilesLayoutToAuto", - "when": "false" - }, - { - "command": "gitlens.views.compare.setFilesLayoutToList", - "when": "false" - }, - { - "command": "gitlens.views.compare.setFilesLayoutToTree", - "when": "false" - }, - { - "command": "gitlens.views.compare.setKeepResultsToOn", - "when": "false" - }, - { - "command": "gitlens.views.compare.setKeepResultsToOff", - "when": "false" - }, - { - "command": "gitlens.views.compare.setShowAvatarsOn", - "when": "false" - }, - { - "command": "gitlens.views.compare.setShowAvatarsOff", - "when": "false" - }, - { - "command": "gitlens.views.compare.swapComparison", - "when": "false" - }, - { "command": "gitlens.views.contributors.copy", "when": "false" }, @@ -5328,47 +5146,67 @@ "when": "false" }, { - "command": "gitlens.views.search.clear", + "command": "gitlens.views.searchAndCompare.clear", + "when": "false" + }, + { + "command": "gitlens.views.searchAndCompare.copy", + "when": "false" + }, + { + "command": "gitlens.views.searchAndCompare.pin", + "when": "false" + }, + { + "command": "gitlens.views.searchAndCompare.unpin", "when": "false" }, { - "command": "gitlens.views.search.copy", + "command": "gitlens.views.searchAndCompare.refresh", "when": "false" }, { - "command": "gitlens.views.search.refresh", + "command": "gitlens.views.searchAndCompare.searchCommits", "when": "false" }, { - "command": "gitlens.views.search.searchCommits", + "command": "gitlens.views.searchAndCompare.edit", "when": "false" }, { - "command": "gitlens.views.search.setFilesLayoutToAuto", + "command": "gitlens.views.searchAndCompare.selectForCompare", "when": "false" }, { - "command": "gitlens.views.search.setFilesLayoutToList", + "command": "gitlens.views.searchAndCompare.setFilesLayoutToAuto", "when": "false" }, { - "command": "gitlens.views.search.setFilesLayoutToTree", + "command": "gitlens.views.searchAndCompare.setFilesLayoutToList", "when": "false" }, { - "command": "gitlens.views.search.setKeepResultsToOn", + "command": "gitlens.views.searchAndCompare.setFilesLayoutToTree", "when": "false" }, { - "command": "gitlens.views.search.setKeepResultsToOff", + "command": "gitlens.views.searchAndCompare.setKeepResultsToOn", "when": "false" }, { - "command": "gitlens.views.search.setShowAvatarsOn", + "command": "gitlens.views.searchAndCompare.setKeepResultsToOff", "when": "false" }, { - "command": "gitlens.views.search.setShowAvatarsOff", + "command": "gitlens.views.searchAndCompare.setShowAvatarsOn", + "when": "false" + }, + { + "command": "gitlens.views.searchAndCompare.setShowAvatarsOff", + "when": "false" + }, + { + "command": "gitlens.views.searchAndCompare.swapComparison", "when": "false" }, { @@ -5964,56 +5802,6 @@ "group": "1_gitlens@1" }, { - "command": "gitlens.views.compare.selectForCompare", - "when": "view =~ /^gitlens\\.views\\.compare/", - "group": "navigation@10" - }, - { - "command": "gitlens.views.compare.setKeepResultsToOn", - "when": "view =~ /^gitlens\\.views\\.compare/ && !gitlens:views:compare:keepResults", - "group": "navigation@11" - }, - { - "command": "gitlens.views.compare.setKeepResultsToOff", - "when": "view =~ /^gitlens\\.views\\.compare/ && gitlens:views:compare:keepResults", - "group": "navigation@11" - }, - { - "command": "gitlens.views.compare.setFilesLayoutToList", - "when": "view =~ /^gitlens\\.views\\.compare/ && config.gitlens.views.compare.files.layout == auto", - "group": "navigation@50" - }, - { - "command": "gitlens.views.compare.setFilesLayoutToTree", - "when": "view =~ /^gitlens\\.views\\.compare/ && config.gitlens.views.compare.files.layout == list", - "group": "navigation@50" - }, - { - "command": "gitlens.views.compare.setFilesLayoutToAuto", - "when": "view =~ /^gitlens\\.views\\.compare/ && config.gitlens.views.compare.files.layout == tree", - "group": "navigation@50" - }, - { - "command": "gitlens.views.compare.clear", - "when": "view =~ /^gitlens\\.views\\.compare/", - "group": "navigation@98" - }, - { - "command": "gitlens.views.compare.refresh", - "when": "view =~ /^gitlens\\.views\\.compare/", - "group": "navigation@99" - }, - { - "command": "gitlens.views.compare.setShowAvatarsOn", - "when": "view =~ /^gitlens\\.views\\.compare/ && !config.gitlens.views.compare.avatars", - "group": "1_gitlens@0" - }, - { - "command": "gitlens.views.compare.setShowAvatarsOff", - "when": "view =~ /^gitlens\\.views\\.compare/ && config.gitlens.views.compare.avatars", - "group": "1_gitlens@0" - }, - { "command": "gitlens.views.addAuthors", "when": "view =~ /^gitlens\\.views\\.contributors/", "group": "navigation@10" @@ -6224,56 +6012,77 @@ "group": "2_gitlens@0" }, { - "command": "gitlens.views.search.searchCommits", - "when": "view =~ /^gitlens\\.views\\.search/", + "command": "gitlens.views.searchAndCompare.searchCommits", + "when": "view =~ /^gitlens\\.views\\.searchAndCompare\\b/", "group": "navigation@10" }, { - "command": "gitlens.views.search.setKeepResultsToOn", - "when": "view =~ /^gitlens\\.views\\.search/ && !gitlens:views:search:keepResults", + "command": "gitlens.views.searchAndCompare.selectForCompare", + "when": "view =~ /^gitlens\\.views\\.searchAndCompare\\b/", "group": "navigation@11" }, { - "command": "gitlens.views.search.setKeepResultsToOff", - "when": "view =~ /^gitlens\\.views\\.search/ && gitlens:views:search:keepResults", - "group": "navigation@11" + "command": "gitlens.views.searchAndCompare.setKeepResultsToOn", + "when": "view =~ /^gitlens\\.views\\.searchAndCompare\\b/ && !gitlens:views:searchAndCompare:keepResults", + "group": "navigation@12" }, { - "command": "gitlens.views.search.setFilesLayoutToList", - "when": "view =~ /^gitlens\\.views\\.search/ && config.gitlens.views.search.files.layout == auto", + "command": "gitlens.views.searchAndCompare.setKeepResultsToOff", + "when": "view =~ /^gitlens\\.views\\.searchAndCompare\\b/ && gitlens:views:searchAndCompare:keepResults", + "group": "navigation@13" + }, + { + "command": "gitlens.views.searchAndCompare.setFilesLayoutToAuto", + "when": "view =~ /^gitlens\\.views\\.searchAndCompare\\b/ && config.gitlens.views.searchAndCompare.files.layout == tree", "group": "navigation@50" }, { - "command": "gitlens.views.search.setFilesLayoutToTree", - "when": "view =~ /^gitlens\\.views\\.search/ && config.gitlens.views.search.files.layout == list", + "command": "gitlens.views.searchAndCompare.setFilesLayoutToList", + "when": "view =~ /^gitlens\\.views\\.searchAndCompare\\b/ && config.gitlens.views.searchAndCompare.files.layout == auto", "group": "navigation@50" }, { - "command": "gitlens.views.search.setFilesLayoutToAuto", - "when": "view =~ /^gitlens\\.views\\.search/ && config.gitlens.views.search.files.layout == tree", + "command": "gitlens.views.searchAndCompare.setFilesLayoutToTree", + "when": "view =~ /^gitlens\\.views\\.searchAndCompare\\b/ && config.gitlens.views.searchAndCompare.files.layout == list", "group": "navigation@50" }, { - "command": "gitlens.views.search.clear", - "when": "view =~ /^gitlens\\.views\\.search/", + "command": "gitlens.views.searchAndCompare.clear", + "when": "view =~ /^gitlens\\.views\\.searchAndCompare\\b/", "group": "navigation@98" }, { - "command": "gitlens.views.search.refresh", - "when": "view =~ /^gitlens\\.views\\.search/", + "command": "gitlens.views.searchAndCompare.refresh", + "when": "view =~ /^gitlens\\.views\\.searchAndCompare\\b/", "group": "navigation@99" }, { - "command": "gitlens.views.search.setShowAvatarsOn", - "when": "view =~ /^gitlens\\.views\\.search/ && !config.gitlens.views.search.avatars", + "command": "gitlens.views.searchAndCompare.setShowAvatarsOn", + "when": "view =~ /^gitlens\\.views\\.searchAndCompare\\b/ && !config.gitlens.views.searchAndCompare.avatars", "group": "1_gitlens@0" }, { - "command": "gitlens.views.search.setShowAvatarsOff", - "when": "view =~ /^gitlens\\.views\\.search/ && config.gitlens.views.search.avatars", + "command": "gitlens.views.searchAndCompare.setShowAvatarsOff", + "when": "view =~ /^gitlens\\.views\\.searchAndCompare\\b/ && config.gitlens.views.searchAndCompare.avatars", "group": "1_gitlens@0" }, { + "command": "gitlens.views.searchAndCompare.copy", + "when": "false" + }, + { + "command": "gitlens.views.searchAndCompare.pin", + "when": "false" + }, + { + "command": "gitlens.views.searchAndCompare.unpin", + "when": "false" + }, + { + "command": "gitlens.views.searchAndCompare.swapComparison", + "when": "false" + }, + { "command": "gitlens.stashSave", "when": "!gitlens:readonly && view =~ /^gitlens\\.views\\.stashes/", "group": "navigation@10" @@ -6369,11 +6178,6 @@ "group": "9_gitlens@1" }, { - "command": "gitlens.showSettingsPage#compare-view", - "when": "view =~ /^gitlens\\.views\\.compare/", - "group": "9_gitlens@1" - }, - { "command": "gitlens.showSettingsPage#contributors-view", "when": "view =~ /^gitlens\\.views\\.contributors/", "group": "9_gitlens@1" @@ -6399,8 +6203,8 @@ "group": "9_gitlens@1" }, { - "command": "gitlens.showSettingsPage#search-commits-view", - "when": "view =~ /^gitlens\\.views\\.search/", + "command": "gitlens.showSettingsPage#search-compare-view", + "when": "view =~ /^gitlens\\.views\\.searchAndCompare\\b/", "group": "9_gitlens@1" }, { @@ -7149,7 +6953,7 @@ }, { "command": "gitlens.views.dismissNode", - "when": "viewItem =~ /gitlens:(compare:picker:ref|compare:results\\b(?!.*?\\b\\+pinned\\b)|search)\\b(?!:(commits|files))/", + "when": "viewItem =~ /gitlens:(compare:picker|(compare|search):results\\b(?!.*?\\b\\+pinned\\b))\\b(?!:(commits|files))/", "group": "inline@99" }, { @@ -7193,57 +6997,57 @@ "group": "9_gitlens@1" }, { - "command": "gitlens.views.compare.swapComparison", + "command": "gitlens.views.searchAndCompare.swapComparison", "when": "viewItem =~ /gitlens:compare:results\\b/", - "group": "inline@3" + "group": "inline@1" }, { "command": "gitlens.views.refreshNode", "when": "viewItem =~ /gitlens:compare:(branch(?=.*?\\b\\+comparing\\b)|results)\\b/", - "group": "inline@4" + "group": "inline@97" + }, + { + "command": "gitlens.views.refreshNode", + "when": "viewItem =~ /gitlens:search:results\\b/", + "group": "inline@97" }, { - "command": "gitlens.views.compare.pinComparison", - "when": "viewItem =~ /gitlens:compare:results\\b(?!.*?\\b\\+pinned\\b)/", + "command": "gitlens.views.searchAndCompare.pin", + "when": "viewItem =~ /gitlens:(compare|search):results\\b(?!.*?\\b\\+pinned\\b)/", "group": "inline@98" }, { - "command": "gitlens.views.compare.unpinComparison", - "when": "viewItem =~ /gitlens:compare:results\\b(?=.*?\\b\\+pinned\\b)/", + "command": "gitlens.views.searchAndCompare.unpin", + "when": "viewItem =~ /gitlens:(compare|search):results\\b(?=.*?\\b\\+pinned\\b)/", "group": "inline@98" }, { - "command": "gitlens.views.openDirectoryDiff", + "command": "gitlens.views.searchAndCompare.swapComparison", "when": "viewItem =~ /gitlens:compare:results\\b/", - "group": "2_gitlens_quickopen@1" + "group": "1_gitlens_actions@2" }, { - "command": "gitlens.views.compare.swapComparison", + "command": "gitlens.views.openDirectoryDiff", "when": "viewItem =~ /gitlens:compare:results\\b/", - "group": "1_gitlens_actions@2" + "group": "2_gitlens_quickopen@1" }, { - "command": "gitlens.views.compare.pinComparison", - "when": "viewItem =~ /gitlens:compare:results\\b(?!.*?\\b\\+pinned\\b)/", + "command": "gitlens.views.searchAndCompare.pin", + "when": "viewItem =~ /gitlens:(compare|search):results\\b(?!.*?\\b\\+pinned\\b)/", "group": "8_gitlens_actions@1" }, { - "command": "gitlens.views.compare.unpinComparison", - "when": "viewItem =~ /gitlens:compare:results\\b(?=.*?\\b\\+pinned\\b)/", + "command": "gitlens.views.searchAndCompare.unpin", + "when": "viewItem =~ /gitlens:(compare|search):results\\b(?=.*?\\b\\+pinned\\b)/", "group": "8_gitlens_actions@1" }, { - "command": "gitlens.views.search.searchCommits", + "command": "gitlens.views.searchAndCompare.edit", "when": "viewItem == gitlens:search:results", "group": "inline@1" }, { - "command": "gitlens.views.refreshNode", - "when": "viewItem == gitlens:search:results", - "group": "inline@2" - }, - { - "command": "gitlens.views.search.searchCommits", + "command": "gitlens.views.searchAndCompare.edit", "when": "viewItem == gitlens:search:results", "group": "1_gitlens_actions@1" }, @@ -7339,7 +7143,7 @@ }, { "command": "gitlens.views.dismissNode", - "when": "viewItem =~ /gitlens:(compare:picker:ref|compare:results\\b(?!.*?\\b\\+pinned\\b)|search)\\b(?!:(commits|files))/", + "when": "viewItem =~ /gitlens:(compare:picker:ref|(compare|search):results\\b(?!.*?\\b\\+pinned\\b))\\b(?!:(commits|files))/", "group": "8_gitlens_actions@98" }, { @@ -7623,12 +7427,6 @@ "when": "gitlens:enabled && focusedView =~ /^gitlens\\.views\\.commits/" }, { - "command": "gitlens.views.compare.copy", - "key": "ctrl+c", - "mac": "cmd+c", - "when": "gitlens:enabled && focusedView =~ /^gitlens\\.views\\.compare/" - }, - { "command": "gitlens.views.contributors.copy", "key": "ctrl+c", "mac": "cmd+c", @@ -7665,10 +7463,10 @@ "when": "gitlens:enabled && focusedView =~ /^gitlens\\.views\\.stashes/" }, { - "command": "gitlens.views.search.copy", + "command": "gitlens.views.searchAndCompare.copy", "key": "ctrl+c", "mac": "cmd+c", - "when": "gitlens:enabled && focusedView =~ /^gitlens\\.views\\.search/" + "when": "gitlens:enabled && focusedView =~ /^gitlens\\.views\\.searchAndCompare\\b/" }, { "command": "gitlens.views.tags.copy", @@ -7805,20 +7603,12 @@ "visibility": "collapsed" }, { - "id": "gitlens.views.search", - "name": "Search Commits", + "id": "gitlens.views.searchAndCompare", + "name": "Search & Compare", "when": "!gitlens:disabled", "contextualTitle": "GitLens", "icon": "images/views/search.svg", "visibility": "collapsed" - }, - { - "id": "gitlens.views.compare", - "name": "Compare Commits", - "when": "!gitlens:disabled", - "contextualTitle": "GitLens", - "icon": "images/views/compare.svg", - "visibility": "collapsed" } ] } diff --git a/src/commands/common.ts b/src/commands/common.ts index 66ff7fb..4590c7d 100644 --- a/src/commands/common.ts +++ b/src/commands/common.ts @@ -87,11 +87,10 @@ export enum Commands { ResetSuppressedWarnings = 'gitlens.resetSuppressedWarnings', RevealCommitInView = 'gitlens.revealCommitInView', SearchCommits = 'gitlens.showCommitSearch', - SearchCommitsInView = 'gitlens.views.search.searchCommits', + SearchCommitsInView = 'gitlens.views.searchAndCompare.searchCommits', SetViewsLayout = 'gitlens.setViewsLayout', ShowCommitInView = 'gitlens.showCommitInView', ShowCommitsInView = 'gitlens.showCommitsInView', - ShowCompareView = 'gitlens.showCompareView', ShowFileHistoryView = 'gitlens.showFileHistoryView', ShowFileHistoryInView = 'gitlens.showFileHistoryInView', ShowLineHistoryView = 'gitlens.showLineHistoryView', @@ -107,18 +106,17 @@ export enum Commands { ShowQuickCommitRevisionInDiffRight = 'gitlens.showQuickRevisionDetailsInDiffRight', ShowQuickStashList = 'gitlens.showQuickStashList', ShowRepositoriesView = 'gitlens.showRepositoriesView', - ShowSearchView = 'gitlens.showSearchView', + ShowSearchAndCompareView = 'gitlens.showSearchAndCompareView', ShowHistoryPage = 'gitlens.showHistoryPage', ShowSettingsPage = 'gitlens.showSettingsPage', ShowSettingsPageAndJumpToBranchesView = 'gitlens.showSettingsPage#branches-view', ShowSettingsPageAndJumpToCommitsView = 'gitlens.showSettingsPage#commits-view', - ShowSettingsPageAndJumpToCompareView = 'gitlens.showSettingsPage#compare-view', ShowSettingsPageAndJumpToContributorsView = 'gitlens.showSettingsPage#contributors-view', ShowSettingsPageAndJumpToFileHistoryView = 'gitlens.showSettingsPage#file-history-view', ShowSettingsPageAndJumpToLineHistoryView = 'gitlens.showSettingsPage#line-history-view', ShowSettingsPageAndJumpToRemotesView = 'gitlens.showSettingsPage#remotes-view', ShowSettingsPageAndJumpToRepositoriesView = 'gitlens.showSettingsPage#repositories-view', - ShowSettingsPageAndJumpToSearchCommitsView = 'gitlens.showSettingsPage#search-commits-view', + ShowSettingsPageAndJumpToSearchAndCompareView = 'gitlens.showSettingsPage#search-compare-view', ShowSettingsPageAndJumpToStashesView = 'gitlens.showSettingsPage#stashes-view', ShowSettingsPageAndJumpToTagsView = 'gitlens.showSettingsPage#tags-view', ShowWelcomePage = 'gitlens.showWelcomePage', diff --git a/src/commands/diffBranchWith.ts b/src/commands/diffBranchWith.ts index 0fdf53d..d08475d 100644 --- a/src/commands/diffBranchWith.ts +++ b/src/commands/diffBranchWith.ts @@ -83,7 +83,7 @@ export class DiffBranchWithCommand extends ActiveEditorCommand { if (args.ref2 == null) return; } - void (await Container.compareView.compare(repoPath, args.ref1, args.ref2)); + void (await Container.searchAndCompareView.compare(repoPath, args.ref1, args.ref2)); } catch (ex) { Logger.error(ex, 'DiffBranchWithCommand'); void Messages.showGenericErrorMessage('Unable to open branch compare'); diff --git a/src/commands/git/search.ts b/src/commands/git/search.ts index f235fd6..540fade 100644 --- a/src/commands/git/search.ts +++ b/src/commands/git/search.ts @@ -3,6 +3,7 @@ import { GlyphChars } from '../../constants'; import { Container } from '../../container'; import { GitLog, GitLogCommit, Repository, SearchOperators, searchOperators, SearchPattern } from '../../git/git'; import { GitCommandsCommand } from '../gitCommands'; +import { SearchResultsNode } from '../../views/nodes'; import { appendReposToTitle, PartialStepState, @@ -29,7 +30,7 @@ interface Context { interface State extends Required { repo: string | Repository; - showResultsInSideBar: boolean; + showResultsInSideBar: boolean | SearchResultsNode; } export interface SearchGitCommandArgs { @@ -159,14 +160,16 @@ export class SearchGitCommand extends QuickCommand { context.resultsKey = searchKey; } + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions if (state.showResultsInSideBar) { - void Container.searchView.search( + void Container.searchAndCompareView.search( state.repo.path, search, { label: { label: `for ${state.pattern}` }, }, context.resultsPromise, + state.showResultsInSideBar instanceof SearchResultsNode ? state.showResultsInSideBar : undefined, ); break; @@ -188,7 +191,7 @@ export class SearchGitCommand extends QuickCommand { showInSideBarButton: { button: QuickCommandButtons.ShowResultsInSideBar, onDidClick: () => - void Container.searchView.search( + void Container.searchAndCompareView.search( repoPath, search, { @@ -262,17 +265,27 @@ export class SearchGitCommand extends QuickCommand { const matchCaseButton = new QuickCommandButtons.MatchCaseToggle(state.matchCase); const matchAllButton = new QuickCommandButtons.MatchAllToggle(state.matchAll); const matchRegexButton = new QuickCommandButtons.MatchRegexToggle(state.matchRegex); - const showResultsToggleButton = new QuickCommandButtons.ShowResultsToggle(state.showResultsInSideBar, () => { - state.showResultsInSideBar = !state.showResultsInSideBar; - showResultsToggleButton.on = state.showResultsInSideBar; - }); + + const additionalButtons = [matchCaseButton, matchAllButton, matchRegexButton]; + if (!SearchResultsNode.is(state.showResultsInSideBar)) { + const showResultsToggleButton = new QuickCommandButtons.ShowResultsToggle( + state.showResultsInSideBar, + () => { + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions + state.showResultsInSideBar = !state.showResultsInSideBar; + showResultsToggleButton.on = state.showResultsInSideBar; + }, + ); + + additionalButtons.push(showResultsToggleButton); + } const step = QuickCommand.createPickStep>({ title: appendReposToTitle(context.title, state, context), placeholder: 'e.g. "Updates dependencies" author:eamodio', matchOnDescription: true, matchOnDetail: true, - additionalButtons: [matchCaseButton, matchAllButton, matchRegexButton, showResultsToggleButton], + additionalButtons: additionalButtons, items: items, value: state.pattern, onDidAccept: (quickpick): boolean => { diff --git a/src/commands/quickCommand.buttons.ts b/src/commands/quickCommand.buttons.ts index 7ef8e2e..828cd92 100644 --- a/src/commands/quickCommand.buttons.ts +++ b/src/commands/quickCommand.buttons.ts @@ -108,7 +108,7 @@ export namespace QuickCommandButtons { }; export const ShowResultsInSideBar: QuickInputButton = { - iconPath: new ThemeIcon('search'), + iconPath: new ThemeIcon('link-external'), tooltip: 'Show Results in Side Bar', }; diff --git a/src/commands/quickCommand.steps.ts b/src/commands/quickCommand.steps.ts index 24daa15..c594b1a 100644 --- a/src/commands/quickCommand.steps.ts +++ b/src/commands/quickCommand.steps.ts @@ -814,7 +814,7 @@ export function* pickCommitStep< } if (button === QuickCommandButtons.SearchInSideBar) { - void Container.searchView.search( + void Container.searchAndCompareView.search( state.repo.path, { pattern: SearchPattern.fromCommit(quickpick.activeItems[0].item.ref) }, { @@ -842,7 +842,7 @@ export function* pickCommitStep< }); } else { const commit = quickpick.activeItems[0].item; - await Container.searchView.search( + await Container.searchAndCompareView.search( commit.repoPath, { pattern: SearchPattern.fromCommit(commit) }, { @@ -933,7 +933,7 @@ export function* pickCommitsStep< } if (button === QuickCommandButtons.SearchInSideBar) { - void Container.searchView.search( + void Container.searchAndCompareView.search( state.repo.path, { pattern: SearchPattern.fromCommit(quickpick.activeItems[0].item.ref) }, { @@ -961,7 +961,7 @@ export function* pickCommitsStep< }); } else { const commit = quickpick.activeItems[0].item; - await Container.searchView.search( + await Container.searchAndCompareView.search( commit.repoPath, { pattern: SearchPattern.fromCommit(commit) }, { @@ -1183,7 +1183,7 @@ export function* pickStashStep< if (button === QuickCommandButtons.SearchInSideBar) { if (quickpick.activeItems.length === 0) return; - void Container.searchView.search( + void Container.searchAndCompareView.search( state.repo.path, { pattern: SearchPattern.fromCommit(quickpick.activeItems[0].item.stashName) }, { @@ -1305,7 +1305,7 @@ export async function* showCommitOrStashStep< : [QuickCommandButtons.RevealInSideBar, QuickCommandButtons.SearchInSideBar], onDidClickButton: (quickpick, button) => { if (button === QuickCommandButtons.SearchInSideBar) { - void Container.searchView.search( + void Container.searchAndCompareView.search( state.repo.path, { pattern: SearchPattern.fromCommit(state.reference.ref) }, { @@ -1559,7 +1559,7 @@ export function* showCommitOrStashFilesStep< additionalButtons: [QuickCommandButtons.RevealInSideBar, QuickCommandButtons.SearchInSideBar], onDidClickButton: (quickpick, button) => { if (button === QuickCommandButtons.SearchInSideBar) { - void Container.searchView.search( + void Container.searchAndCompareView.search( state.repo.path, { pattern: SearchPattern.fromCommit(state.reference.ref) }, { @@ -1631,7 +1631,7 @@ export async function* showCommitOrStashFileStep< additionalButtons: [QuickCommandButtons.RevealInSideBar, QuickCommandButtons.SearchInSideBar], onDidClickButton: (quickpick, button) => { if (button === QuickCommandButtons.SearchInSideBar) { - void Container.searchView.search( + void Container.searchAndCompareView.search( state.repo.path, { pattern: SearchPattern.fromCommit(state.reference.ref) }, { diff --git a/src/commands/searchCommits.ts b/src/commands/searchCommits.ts index d289e00..f90f8d6 100644 --- a/src/commands/searchCommits.ts +++ b/src/commands/searchCommits.ts @@ -2,7 +2,7 @@ import { executeGitCommand } from '../commands'; import { Command, command, CommandContext, Commands, isCommandViewContextWithRepo } from './common'; import { SearchPattern } from '../git/git'; -import { SearchResultsCommitsNode } from '../views/nodes'; +import { SearchResultsNode } from '../views/nodes'; export interface SearchCommitsCommandArgs { search?: Partial; @@ -24,7 +24,7 @@ export class SearchCommitsCommand extends Command { args = { ...args }; args.showResultsInSideBar = true; - if (context.node instanceof SearchResultsCommitsNode) { + if (context.node instanceof SearchResultsNode) { args.repoPath = context.node.repoPath; args.search = context.node.search; args.prefillOnly = true; diff --git a/src/commands/showView.ts b/src/commands/showView.ts index f71e62c..66e0ce7 100644 --- a/src/commands/showView.ts +++ b/src/commands/showView.ts @@ -6,11 +6,10 @@ import { command, Command, CommandContext, Commands } from './common'; export class ShowViewCommand extends Command { constructor() { super([ - Commands.ShowCompareView, Commands.ShowFileHistoryView, Commands.ShowLineHistoryView, Commands.ShowRepositoriesView, - Commands.ShowSearchView, + Commands.ShowSearchAndCompareView, ]); } @@ -20,16 +19,14 @@ export class ShowViewCommand extends Command { execute(command: Commands) { switch (command) { - case Commands.ShowCompareView: - return Container.compareView.show(); case Commands.ShowFileHistoryView: return Container.fileHistoryView.show(); case Commands.ShowLineHistoryView: return Container.lineHistoryView.show(); case Commands.ShowRepositoriesView: return Container.repositoriesView.show(); - case Commands.ShowSearchView: - return Container.searchView.show(); + case Commands.ShowSearchAndCompareView: + return Container.searchAndCompareView.show(); } return Promise.resolve(undefined); diff --git a/src/config.ts b/src/config.ts index e6bcfb8..6705bfd 100644 --- a/src/config.ts +++ b/src/config.ts @@ -431,13 +431,12 @@ export const viewsCommonConfigKeys: (keyof ViewsCommonConfig)[] = [ interface ViewsConfigs { branches: BranchesViewConfig; commits: CommitsViewConfig; - compare: CompareViewConfig; contributors: ContributorsViewConfig; fileHistory: FileHistoryViewConfig; lineHistory: LineHistoryViewConfig; remotes: RemotesViewConfig; repositories: RepositoriesViewConfig; - search: SearchViewConfig; + searchAndCompare: SearchAndCompareViewConfig; stashes: StashesViewConfig; tags: TagsViewConfig; welcome: WelcomeViewConfig; @@ -454,8 +453,7 @@ export const viewsConfigKeys: ViewsConfigKeys[] = [ 'stashes', 'tags', 'contributors', - 'search', - 'compare', + 'searchAndCompare', ]; export type ViewsConfig = ViewsCommonConfig & ViewsConfigs; @@ -486,15 +484,6 @@ export interface CommitsViewConfig { showBranchComparison: false | ViewShowBranchComparison; } -export interface CompareViewConfig { - avatars: boolean; - files: ViewsFilesConfig; - pullRequests: { - enabled: boolean; - showForCommits: boolean; - }; -} - export interface ContributorsViewConfig { avatars: boolean; files: ViewsFilesConfig; @@ -545,7 +534,7 @@ export interface RepositoriesViewConfig { showBranchComparison: false | ViewShowBranchComparison; } -export interface SearchViewConfig { +export interface SearchAndCompareViewConfig { avatars: boolean; files: ViewsFilesConfig; pullRequests: { diff --git a/src/constants.ts b/src/constants.ts index 1a521e1..cc94c70 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,6 +1,7 @@ 'use strict'; import { commands, TextDocument, TextEditor, window } from 'vscode'; import { ViewShowBranchComparison } from './config'; +import { SearchPattern } from './git/git'; export const applicationInsightsKey = 'a9c302f8-6483-4d01-b92c-c159c799c679'; export const extensionId = 'gitlens'; @@ -43,12 +44,11 @@ export enum CommandContext { ViewsCanCompare = 'gitlens:views:canCompare', ViewsCanCompareFile = 'gitlens:views:canCompare:file', ViewsCommitsMyCommitsOnly = 'gitlens:views:commits:myCommitsOnly', - ViewsCompareKeepResults = 'gitlens:views:compare:keepResults', ViewsFileHistoryCursorFollowing = 'gitlens:views:fileHistory:cursorFollowing', ViewsFileHistoryEditorFollowing = 'gitlens:views:fileHistory:editorFollowing', ViewsLineHistoryEditorFollowing = 'gitlens:views:lineHistory:editorFollowing', ViewsRepositoriesAutoRefresh = 'gitlens:views:repositories:autoRefresh', - ViewsSearchKeepResults = 'gitlens:views:search:keepResults', + ViewsSearchAndCompareKeepResults = 'gitlens:views:searchAndCompare:keepResults', Vsls = 'gitlens:vsls', } @@ -165,14 +165,34 @@ export interface NamedRef { } export interface PinnedComparison { + type: 'comparison'; + timestamp: number; path: string; ref1: NamedRef; ref2: NamedRef; - notation: '..' | '...' | undefined; + notation?: '..' | '...'; } -export interface PinnedComparisons { - [id: string]: PinnedComparison; +export interface PinnedSearch { + type: 'search'; + timestamp: number; + path: string; + labels: { + label: string; + queryLabel: + | string + | { + label: string; + resultsType?: { singular: string; plural: string }; + }; + }; + search: SearchPattern; +} + +export type PinnedItem = PinnedComparison | PinnedSearch; + +export interface PinnedItems { + [id: string]: PinnedItem; } export interface StarredBranches { @@ -186,10 +206,10 @@ export interface StarredRepositories { export enum WorkspaceState { BranchComparisons = 'gitlens:branch:comparisons', DefaultRemote = 'gitlens:remote:default', - PinnedComparisons = 'gitlens:pinned:comparisons', + DeprecatedPinnedComparisons = 'gitlens:pinned:comparisons', StarredBranches = 'gitlens:starred:branches', StarredRepositories = 'gitlens:starred:repositories', - ViewsCompareKeepResults = 'gitlens:views:compare:keepResults', ViewsRepositoriesAutoRefresh = 'gitlens:views:repositories:autoRefresh', - ViewsSearchKeepResults = 'gitlens:views:search:keepResults', + ViewsSearchAndCompareKeepResults = 'gitlens:views:searchAndCompare:keepResults', + ViewsSearchAndComparePinnedItems = 'gitlens:views:searchAndCompare:pinned', } diff --git a/src/container.ts b/src/container.ts index 54d9f5a..76fb1e9 100644 --- a/src/container.ts +++ b/src/container.ts @@ -18,13 +18,12 @@ import { GitDocumentTracker } from './trackers/gitDocumentTracker'; import { GitLineTracker } from './trackers/gitLineTracker'; import { BranchesView } from './views/branchesView'; import { CommitsView } from './views/commitsView'; -import { CompareView } from './views/compareView'; import { ContributorsView } from './views/contributorsView'; import { FileHistoryView } from './views/fileHistoryView'; import { LineHistoryView } from './views/lineHistoryView'; import { RemotesView } from './views/remotesView'; import { RepositoriesView } from './views/repositoriesView'; -import { SearchView } from './views/searchView'; +import { SearchAndCompareView } from './views/searchAndCompareView'; import { StashesView } from './views/stashesView'; import { TagsView } from './views/tagsView'; import { ViewCommands } from './views/viewCommands'; @@ -68,8 +67,7 @@ export class Container { context.subscriptions.push((this._stashesView = new StashesView())); context.subscriptions.push((this._tagsView = new TagsView())); context.subscriptions.push((this._contributorsView = new ContributorsView())); - context.subscriptions.push((this._searchView = new SearchView())); - context.subscriptions.push((this._compareView = new CompareView())); + context.subscriptions.push((this._searchAndCompareView = new SearchAndCompareView())); if (config.views.lineHistory.enabled) { context.subscriptions.push((this._lineHistoryView = new LineHistoryView())); @@ -151,15 +149,6 @@ export class Container { return this._commitsView; } - private static _compareView: CompareView | undefined; - static get compareView() { - if (this._compareView === undefined) { - this._context.subscriptions.push((this._compareView = new CompareView())); - } - - return this._compareView; - } - private static _config: Config | undefined; static get config() { if (this._config === undefined) { @@ -266,13 +255,13 @@ export class Container { return this._repositoriesView; } - private static _searchView: SearchView | undefined; - static get searchView() { - if (this._searchView === undefined) { - this._context.subscriptions.push((this._searchView = new SearchView())); + private static _searchAndCompareView: SearchAndCompareView | undefined; + static get searchAndCompareView() { + if (this._searchAndCompareView === undefined) { + this._context.subscriptions.push((this._searchAndCompareView = new SearchAndCompareView())); } - return this._searchView; + return this._searchAndCompareView; } private static _settingsWebview: SettingsWebview; diff --git a/src/quickpicks/commitQuickPickItems.ts b/src/quickpicks/commitQuickPickItems.ts index ac71b0e..46918dc 100644 --- a/src/quickpicks/commitQuickPickItems.ts +++ b/src/quickpicks/commitQuickPickItems.ts @@ -86,7 +86,7 @@ export class CommitCompareWithHEADCommandQuickPickItem extends CommandQuickPickI } execute(_options: { preserveFocus?: boolean; preview?: boolean }): Promise { - return Container.compareView.compare(this.commit.repoPath, this.commit.ref, 'HEAD'); + return Container.searchAndCompareView.compare(this.commit.repoPath, this.commit.ref, 'HEAD'); } } @@ -96,7 +96,7 @@ export class CommitCompareWithWorkingCommandQuickPickItem extends CommandQuickPi } execute(_options: { preserveFocus?: boolean; preview?: boolean }): Promise { - return Container.compareView.compare(this.commit.repoPath, this.commit.ref, ''); + return Container.searchAndCompareView.compare(this.commit.repoPath, this.commit.ref, ''); } } diff --git a/src/quickpicks/quickPicksItems.ts b/src/quickpicks/quickPicksItems.ts index 5e2e8db..b35993f 100644 --- a/src/quickpicks/quickPicksItems.ts +++ b/src/quickpicks/quickPicksItems.ts @@ -213,7 +213,7 @@ export class SearchForCommitQuickPickItem extends CommandQuickPickItem { } async execute(options?: { preserveFocus?: boolean; preview?: boolean }): Promise { - void (await Container.searchView.search( + void (await Container.searchAndCompareView.search( this.reference.repoPath, { pattern: SearchPattern.fromCommit(this.reference), diff --git a/src/views/compareView.ts b/src/views/compareView.ts deleted file mode 100644 index bc2c558..0000000 --- a/src/views/compareView.ts +++ /dev/null @@ -1,184 +0,0 @@ -'use strict'; -import { commands, ConfigurationChangeEvent } from 'vscode'; -import { CompareViewConfig, configuration, ViewFilesLayout } from '../configuration'; -import { - CommandContext, - NamedRef, - PinnedComparison, - PinnedComparisons, - setCommandContext, - WorkspaceState, -} from '../constants'; -import { Container } from '../container'; -import { CompareNode, CompareResultsNode, nodeSupportsConditionalDismissal, ViewNode } from './nodes'; -import { ViewBase } from './viewBase'; - -export class CompareView extends ViewBase { - protected readonly configKey = 'compare'; - - constructor() { - super('gitlens.views.compare', 'Compare'); - - void setCommandContext(CommandContext.ViewsCompareKeepResults, this.keepResults); - } - - getRoot() { - return new CompareNode(this); - } - - protected registerCommands() { - void Container.viewCommands; - - commands.registerCommand(this.getQualifiedCommand('clear'), () => this.clear(), this); - commands.registerCommand( - this.getQualifiedCommand('copy'), - () => commands.executeCommand('gitlens.views.copy', this.selection), - this, - ); - commands.registerCommand(this.getQualifiedCommand('refresh'), () => this.refresh(true), this); - commands.registerCommand( - this.getQualifiedCommand('setFilesLayoutToAuto'), - () => this.setFilesLayout(ViewFilesLayout.Auto), - this, - ); - commands.registerCommand( - this.getQualifiedCommand('setFilesLayoutToList'), - () => this.setFilesLayout(ViewFilesLayout.List), - this, - ); - commands.registerCommand( - this.getQualifiedCommand('setFilesLayoutToTree'), - () => this.setFilesLayout(ViewFilesLayout.Tree), - this, - ); - commands.registerCommand(this.getQualifiedCommand('setKeepResultsToOn'), () => this.setKeepResults(true), this); - commands.registerCommand( - this.getQualifiedCommand('setKeepResultsToOff'), - () => this.setKeepResults(false), - this, - ); - commands.registerCommand(this.getQualifiedCommand('setShowAvatarsOn'), () => this.setShowAvatars(true), this); - commands.registerCommand(this.getQualifiedCommand('setShowAvatarsOff'), () => this.setShowAvatars(false), this); - commands.registerCommand(this.getQualifiedCommand('pinComparison'), this.pinComparison, this); - commands.registerCommand(this.getQualifiedCommand('unpinComparison'), this.unpinComparison, this); - commands.registerCommand(this.getQualifiedCommand('swapComparison'), this.swapComparison, this); - commands.registerCommand(this.getQualifiedCommand('selectForCompare'), this.selectForCompare, this); - commands.registerCommand(this.getQualifiedCommand('compareWithSelected'), this.compareWithSelected, this); - } - - protected filterConfigurationChanged(e: ConfigurationChangeEvent) { - const changed = super.filterConfigurationChanged(e); - if ( - !changed && - !configuration.changed(e, 'defaultDateFormat') && - !configuration.changed(e, 'defaultDateSource') && - !configuration.changed(e, 'defaultDateStyle') && - !configuration.changed(e, 'defaultGravatarsStyle') - ) { - return false; - } - - return true; - } - - get keepResults(): boolean { - return Container.context.workspaceState.get(WorkspaceState.ViewsCompareKeepResults, false); - } - - clear() { - this.root?.clear(); - } - - dismissNode(node: ViewNode) { - if (this.root == null) return; - if (nodeSupportsConditionalDismissal(node) && node.canDismiss() === false) return; - - this.root.dismiss(node); - } - - compare(repoPath: string, ref1: string | NamedRef, ref2: string | NamedRef) { - return this.addResults( - new CompareResultsNode( - this, - repoPath, - typeof ref1 === 'string' ? { ref: ref1 } : ref1, - typeof ref2 === 'string' ? { ref: ref2 } : ref2, - ), - ); - } - - compareWithSelected(repoPath?: string, ref?: string | NamedRef) { - const root = this.ensureRoot(); - void root.compareWithSelected(repoPath, ref); - } - - selectForCompare(repoPath?: string, ref?: string | NamedRef) { - const root = this.ensureRoot(); - void root.selectForCompare(repoPath, ref); - } - - getPinnedComparisons() { - const pinned = Container.context.workspaceState.get(WorkspaceState.PinnedComparisons); - if (pinned == null) return []; - - return Object.values(pinned).map(p => new CompareResultsNode(this, p.path, p.ref1, p.ref2, true)); - } - - async updatePinnedComparison(id: string, pin?: PinnedComparison) { - let pinned = Container.context.workspaceState.get(WorkspaceState.PinnedComparisons); - if (pinned == null) { - pinned = Object.create(null) as PinnedComparisons; - } - - if (pin !== undefined) { - pinned[id] = { ...pin }; - } else { - const { [id]: _, ...rest } = pinned; - pinned = rest; - } - - await Container.context.workspaceState.update(WorkspaceState.PinnedComparisons, pinned); - } - - private async addResults(results: ViewNode) { - if (!this.visible) { - await this.show(); - } - - const root = this.ensureRoot(); - root.addOrReplace(results, !this.keepResults); - - setImmediate(() => this.reveal(results, { expand: true, focus: true, select: true })); - } - - private setFilesLayout(layout: ViewFilesLayout) { - return configuration.updateEffective('views', this.configKey, 'files', 'layout', layout); - } - - private setKeepResults(enabled: boolean) { - void Container.context.workspaceState.update(WorkspaceState.ViewsCompareKeepResults, enabled); - void setCommandContext(CommandContext.ViewsCompareKeepResults, enabled); - } - - private setShowAvatars(enabled: boolean) { - return configuration.updateEffective('views', this.configKey, 'avatars', enabled); - } - - private pinComparison(node: ViewNode) { - if (!(node instanceof CompareResultsNode)) return undefined; - - return node.pin(); - } - - private swapComparison(node: ViewNode) { - if (!(node instanceof CompareResultsNode)) return undefined; - - return node.swap(); - } - - private unpinComparison(node: ViewNode) { - if (!(node instanceof CompareResultsNode)) return undefined; - - return node.unpin(); - } -} diff --git a/src/views/nodes.ts b/src/views/nodes.ts index 0882bbc..d90a692 100644 --- a/src/views/nodes.ts +++ b/src/views/nodes.ts @@ -10,7 +10,6 @@ export * from './nodes/branchTrackingStatusNode'; export * from './nodes/commitFileNode'; export * from './nodes/commitNode'; export * from './nodes/compareBranchNode'; -export * from './nodes/compareNode'; export * from './nodes/compareResultsNode'; export * from './nodes/contributorNode'; export * from './nodes/contributorsNode'; @@ -28,8 +27,7 @@ export * from './nodes/repositoryNode'; export * from './nodes/resultsCommitsNode'; export * from './nodes/resultsFileNode'; export * from './nodes/resultsFilesNode'; -export * from './nodes/searchNode'; -export * from './nodes/searchResultsCommitsNode'; +export * from './nodes/searchResultsNode'; export * from './nodes/stashesNode'; export * from './nodes/stashFileNode'; export * from './nodes/stashNode'; diff --git a/src/views/nodes/compareBranchNode.ts b/src/views/nodes/compareBranchNode.ts index 5df169a..283b54d 100644 --- a/src/views/nodes/compareBranchNode.ts +++ b/src/views/nodes/compareBranchNode.ts @@ -64,44 +64,46 @@ export class CompareBranchNode extends ViewNode { - private _children: (ViewNode | MessageNode)[] = []; - private _comparePickerNode: ComparePickerNode | undefined; - - constructor(view: CompareView) { - super(unknownGitUri, view); - } - - private _selectedRef: RepoRef | undefined; - get selectedRef(): RepoRef | undefined { - return this._selectedRef; - } - - getChildren(): ViewNode[] { - if (this._children.length === 0) { - // Not really sure why I can't reuse this node -- but if I do the Tree errors out with an id already exists error - this._comparePickerNode = new ComparePickerNode(this.view, this); - this._children = [this._comparePickerNode]; - - const pinned = this.view.getPinnedComparisons(); - if (pinned.length !== 0) { - this._children.push(...pinned); - } - } else if (this._comparePickerNode === undefined || !this._children.includes(this._comparePickerNode)) { - // Not really sure why I can't reuse this node -- but if I do the Tree errors out with an id already exists error - this._comparePickerNode = new ComparePickerNode(this.view, this); - this._children.splice(0, 0, this._comparePickerNode); - - if (this._selectedRef !== undefined) { - const node = this._comparePickerNode; - setImmediate(() => this.view.reveal(node, { focus: false, select: true })); - } - } - - return this._children; - } - - getTreeItem(): TreeItem { - const item = new TreeItem('Compare', TreeItemCollapsibleState.Expanded); - item.contextValue = ContextValues.Compare; - return item; - } - - addOrReplace(results: ViewNode, replace: boolean) { - if (this._children.includes(results)) return; - - if (this._children.length !== 0 && replace) { - this._children.length = 0; - this._children.push(results); - - // Re-add the pinned comparisons - const pinned = this.view.getPinnedComparisons(); - if (pinned.length !== 0) { - this._children.push(...pinned); - } - } else { - if (this._comparePickerNode !== undefined) { - const index = this._children.indexOf(this._comparePickerNode); - if (index !== -1) { - this._children.splice(index, 1); - } - } - - this._children.splice(0, 0, results); - } - - this.view.triggerNodeChange(); - } - - @log() - clear() { - this._selectedRef = undefined; - void setCommandContext(CommandContext.ViewsCanCompare, false); - - this._children.length = 0; - this.view.triggerNodeChange(); - } - - @log({ - args: { 0: (n: ViewNode) => n.toString() }, - }) - dismiss(node: ViewNode) { - this._selectedRef = undefined; - void setCommandContext(CommandContext.ViewsCanCompare, false); - - if (this._children.length !== 0) { - const index = this._children.indexOf(node); - if (index === -1) return; - - this._children.splice(index, 1); - } - this.view.triggerNodeChange(); - } - - @gate() - @debug() - async refresh() { - if (this._children.length === 0) return; - - const promises: Promise[] = [ - ...Iterables.filterMap(this._children, c => { - const result = c.refresh === undefined ? false : c.refresh(); - return Promises.is(result) ? result : undefined; - }), - ]; - await Promise.all(promises); - } - - async compareWithSelected(repoPath?: string, ref?: string | NamedRef) { - if (this._selectedRef === undefined) return; - - if (repoPath === undefined) { - repoPath = this._selectedRef.repoPath; - } else if (repoPath !== this._selectedRef.repoPath) { - // If we don't have a matching repoPath, then start over - void this.selectForCompare(repoPath, ref); - - return; - } - - if (ref === undefined) { - const pick = await ReferencePicker.show( - repoPath, - `Compare ${this.getRefName(this._selectedRef.ref)} with`, - 'Choose a reference to compare with', - { - allowEnteringRefs: true, - picked: - typeof this._selectedRef.ref === 'string' ? this._selectedRef.ref : this._selectedRef.ref.ref, - // checkmarks: true, - include: - ReferencesQuickPickIncludes.BranchesAndTags | - ReferencesQuickPickIncludes.HEAD | - ReferencesQuickPickIncludes.WorkingTree, - sort: { - branches: { current: true, orderBy: BranchSorting.DateDesc }, - tags: { orderBy: TagSorting.DateDesc }, - }, - }, - ); - if (pick === undefined) { - await this.view.show(); - await this.view.reveal(this._comparePickerNode!, { focus: true, select: true }); - - return; - } - - ref = pick.ref; - } - - const ref1 = this._selectedRef; - - this._selectedRef = undefined; - void setCommandContext(CommandContext.ViewsCanCompare, false); - - void (await this.view.compare(repoPath, ref1.ref, ref)); - } - - async selectForCompare(repoPath?: string, ref?: string | NamedRef) { - if (repoPath === undefined) { - repoPath = await getRepoPathOrPrompt('Compare'); - } - if (repoPath === undefined) { - await this.view.show(); - await this.view.reveal(this._comparePickerNode!, { focus: true, select: true }); - - return; - } - - let autoCompare = false; - if (ref === undefined) { - const pick = await ReferencePicker.show(repoPath, 'Compare', 'Choose a reference to compare', { - allowEnteringRefs: true, - // checkmarks: false, - include: - ReferencesQuickPickIncludes.BranchesAndTags | - ReferencesQuickPickIncludes.HEAD | - ReferencesQuickPickIncludes.WorkingTree, - sort: { - branches: { current: true, orderBy: BranchSorting.DateDesc }, - tags: { orderBy: TagSorting.DateDesc }, - }, - }); - if (pick == null) { - await this.view.show(); - await this.view.reveal(this._comparePickerNode!, { focus: true, select: true }); - - return; - } - - ref = pick.ref; - - autoCompare = true; - } - - this._selectedRef = { label: this.getRefName(ref), repoPath: repoPath, ref: ref }; - void setCommandContext(CommandContext.ViewsCanCompare, true); - - void (await this.triggerChange()); - await this.view.reveal(this._comparePickerNode!, { focus: true, select: true }); - - if (autoCompare) { - void (await this.compareWithSelected()); - } - } - - private getRefName(ref: string | NamedRef) { - return typeof ref === 'string' - ? GitRevision.shorten(ref, { strings: { working: 'Working Tree' } })! - : ref.label ?? GitRevision.shorten(ref.ref)!; - } -} diff --git a/src/views/nodes/comparePickerNode.ts b/src/views/nodes/comparePickerNode.ts index eb5045c..02d70d0 100644 --- a/src/views/nodes/comparePickerNode.ts +++ b/src/views/nodes/comparePickerNode.ts @@ -1,23 +1,35 @@ 'use strict'; import { TreeItem, TreeItemCollapsibleState } from 'vscode'; -import { GlyphChars } from '../../constants'; +import { GlyphChars, NamedRef } from '../../constants'; import { Container } from '../../container'; -import { CompareView } from '../compareView'; -import { CompareNode } from './compareNode'; +import { SearchAndCompareView, SearchAndCompareViewNode } from '../searchAndCompareView'; import { ContextValues, unknownGitUri, ViewNode } from './viewNode'; -export class ComparePickerNode extends ViewNode { - constructor(view: CompareView, protected readonly parent: CompareNode) { +interface RepoRef { + label: string; + repoPath: string; + ref: string | NamedRef; +} + +export class ComparePickerNode extends ViewNode { + readonly order: number = Date.now(); + readonly pinned: boolean = false; + + constructor(view: SearchAndCompareView, parent: SearchAndCompareViewNode, public readonly selectedRef: RepoRef) { super(unknownGitUri, view, parent); } + get canDismiss(): boolean { + return true; + } + getChildren(): ViewNode[] { return []; } async getTreeItem(): Promise { - const selectedRef = this.parent.selectedRef; - const repoPath = selectedRef !== undefined ? selectedRef.repoPath : undefined; + const selectedRef = this.selectedRef; + const repoPath = selectedRef?.repoPath; let description; if (repoPath !== undefined) { @@ -28,7 +40,7 @@ export class ComparePickerNode extends ViewNode { } let item; - if (selectedRef === undefined) { + if (selectedRef == null) { item = new TreeItem( 'Compare with ', TreeItemCollapsibleState.None, diff --git a/src/views/nodes/compareResultsNode.ts b/src/views/nodes/compareResultsNode.ts index 95b591e..410f406 100644 --- a/src/views/nodes/compareResultsNode.ts +++ b/src/views/nodes/compareResultsNode.ts @@ -1,59 +1,58 @@ 'use strict'; -import { Disposable, TreeItem, TreeItemCollapsibleState } from 'vscode'; +import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from 'vscode'; import { NamedRef } from '../../constants'; import { Container } from '../../container'; import { GitRevision } from '../../git/git'; import { GitUri } from '../../git/gitUri'; import { debug, gate, log, Strings } from '../../system'; -import { CompareView } from '../compareView'; import { CommitsQueryResults, ResultsCommitsNode } from './resultsCommitsNode'; import { FilesQueryResults } from './resultsFilesNode'; import { ContextValues, ViewNode } from './viewNode'; import { RepositoryNode } from './repositoryNode'; -import { TreeViewNodeCollapsibleStateChangeEvent } from '../viewBase'; +import { SearchAndCompareView } from '../searchAndCompareView'; let instanceId = 0; -export class CompareResultsNode extends ViewNode implements Disposable { +export class CompareResultsNode extends ViewNode { static key = ':compare-results'; static getId(repoPath: string, ref1: string, ref2: string, instanceId: number): string { return `${RepositoryNode.getId(repoPath)}${this.key}(${ref1}|${ref2}):${instanceId}`; } + static getPinnableId(repoPath: string, ref1: string, ref2: string) { + return Strings.sha1(`${repoPath}|${ref1}|${ref2}`); + } + private _children: ViewNode[] | undefined; - private _disposable: Disposable; private _instanceId: number; constructor( - view: CompareView, + view: SearchAndCompareView, + parent: ViewNode, public readonly repoPath: string, private _ref: NamedRef, private _compareWith: NamedRef, - private _pinned: boolean = false, + private _pinned: number = 0, ) { - super(GitUri.fromRepoPath(repoPath), view); + super(GitUri.fromRepoPath(repoPath), view, parent); this._instanceId = instanceId++; - - this._disposable = this.view.onDidChangeNodeCollapsibleState(this.onCollapsibleStateChanged, this); } - dispose() { - this._disposable.dispose(); + get id(): string { + return CompareResultsNode.getId(this.repoPath, this._ref.ref, this._compareWith.ref, this._instanceId); } - private _collapsibleState: TreeItemCollapsibleState | undefined; - private onCollapsibleStateChanged(e: TreeViewNodeCollapsibleStateChangeEvent) { - if (e.element !== this) return; - - this._collapsibleState = e.state; + get canDismiss(): boolean { + return !this.pinned; } - get id(): string { - return CompareResultsNode.getId(this.repoPath, this._ref.ref, this._compareWith.ref, this._instanceId); + private readonly _order: number = Date.now(); + get order(): number { + return this._pinned || this._order; } get pinned(): boolean { - return this._pinned; + return this._pinned !== 0; } async getChildren(): Promise { @@ -68,40 +67,42 @@ export class CompareResultsNode extends ViewNode implements Disposa this, this.uri.repoPath!, 'Behind', - this.getCommitsQuery( - GitRevision.createRange(this._ref.ref, this._compareWith?.ref ?? 'HEAD', '..'), - ), { - id: 'behind', - description: Strings.pluralize('commit', aheadBehind?.behind ?? 0), - expand: false, - includeRepoName: true, + query: this.getCommitsQuery( + GitRevision.createRange(this._ref.ref, this._compareWith?.ref ?? 'HEAD', '..'), + ), files: { ref1: this._ref.ref, ref2: this._compareWith.ref || 'HEAD', query: this.getBehindFilesQuery.bind(this), }, }, + { + id: 'behind', + description: Strings.pluralize('commit', aheadBehind?.behind ?? 0), + expand: false, + }, ), new ResultsCommitsNode( this.view, this, this.uri.repoPath!, 'Ahead', - this.getCommitsQuery( - GitRevision.createRange(this._compareWith?.ref ?? 'HEAD', this._ref.ref, '..'), - ), { - id: 'ahead', - description: Strings.pluralize('commit', aheadBehind?.ahead ?? 0), - expand: false, - includeRepoName: true, + query: this.getCommitsQuery( + GitRevision.createRange(this._compareWith?.ref ?? 'HEAD', this._ref.ref, '..'), + ), files: { ref1: this._compareWith.ref || 'HEAD', ref2: this._ref.ref, query: this.getAheadFilesQuery.bind(this), }, }, + { + id: 'ahead', + description: Strings.pluralize('commit', aheadBehind?.ahead ?? 0), + expand: false, + }, ), ]; } @@ -122,52 +123,36 @@ export class CompareResultsNode extends ViewNode implements Disposa this._compareWith.label ?? GitRevision.shorten(this._compareWith.ref, { strings: { working: 'Working Tree' } }) }`, - this._collapsibleState ?? TreeItemCollapsibleState.Collapsed, + TreeItemCollapsibleState.Collapsed, ); item.contextValue = `${ContextValues.CompareResults}${this._pinned ? '+pinned' : ''}`; item.description = description; if (this._pinned) { - item.iconPath = { - dark: Container.context.asAbsolutePath('images/dark/icon-pin-small.svg'), - light: Container.context.asAbsolutePath('images/light/icon-pin-small.svg'), - }; + item.iconPath = new ThemeIcon('pinned'); } return item; } - canDismiss(): boolean { - return !this._pinned; - } - - // eslint-disable-next-line @typescript-eslint/require-await @gate() @debug() async getDiffRefs(): Promise<[string, string]> { - // if (this.comparisonNotation === '..') { - // return [ - // (await Container.git.getMergeBase(this.repoPath, this._compareWith.ref, this._ref.ref)) ?? - // this._compareWith.ref, - // this._ref.ref, - // ]; - // } - - return [this._compareWith.ref, this._ref.ref]; + return Promise.resolve([this._compareWith.ref, this._ref.ref]); } @log() async pin() { - if (this._pinned) return; + if (this.pinned) return; - await this.view.updatePinnedComparison(this.getPinnableId(), { + this._pinned = Date.now(); + await this.view.updatePinned(this.getPinnableId(), { + type: 'comparison', + timestamp: this._pinned, path: this.repoPath, ref1: this._ref, ref2: this._compareWith, - notation: undefined, }); - - this._pinned = true; - void this.triggerChange(); + setImmediate(() => this.view.reveal(this, { focus: true, select: true })); } @gate() @@ -188,28 +173,33 @@ export class CompareResultsNode extends ViewNode implements Disposa this._compareWith = ref1; // If we were pinned, remove the existing pin and save a new one - if (this._pinned) { - await this.view.updatePinnedComparison(currentId); - await this.view.updatePinnedComparison(this.getPinnableId(), { + if (this.pinned) { + await this.view.updatePinned(currentId); + await this.view.updatePinned(this.getPinnableId(), { + type: 'comparison', + timestamp: this._pinned, path: this.repoPath, ref1: this._ref, ref2: this._compareWith, - notation: undefined, }); } this._children = undefined; - this.view.triggerNodeChange(this); + this.view.triggerNodeChange(this.parent); + setImmediate(() => this.view.reveal(this, { expand: true, focus: true, select: true })); } @log() async unpin() { - if (!this._pinned) return; + if (!this.pinned) return; - await this.view.updatePinnedComparison(this.getPinnableId()); + this._pinned = 0; + await this.view.updatePinned(this.getPinnableId()); + setImmediate(() => this.view.reveal(this, { focus: true, select: true })); + } - this._pinned = false; - void this.triggerChange(); + private getPinnableId() { + return CompareResultsNode.getPinnableId(this.repoPath, this._ref.ref, this._compareWith.ref); } private async getAheadFilesQuery(): Promise { @@ -294,8 +284,4 @@ export class CompareResultsNode extends ViewNode implements Disposa return results as CommitsQueryResults; }; } - - private getPinnableId() { - return Strings.sha1(`${this.repoPath}|${this._ref.ref}|${this._compareWith.ref}`); - } } diff --git a/src/views/nodes/pullRequestNode.ts b/src/views/nodes/pullRequestNode.ts index 77c0101..e957f10 100644 --- a/src/views/nodes/pullRequestNode.ts +++ b/src/views/nodes/pullRequestNode.ts @@ -2,18 +2,17 @@ import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from 'vscode'; import { BranchesView } from '../branchesView'; import { CommitsView } from '../commitsView'; -import { CompareView } from '../compareView'; import { ContributorsView } from '../contributorsView'; import { GitBranch, GitCommit, PullRequest, PullRequestState } from '../../git/git'; import { GitUri } from '../../git/gitUri'; import { RemotesView } from '../remotesView'; import { RepositoriesView } from '../repositoriesView'; import { RepositoryNode } from './repositoryNode'; -import { SearchView } from '../searchView'; +import { SearchAndCompareView } from '../searchAndCompareView'; import { ContextValues, ViewNode } from './viewNode'; export class PullRequestNode extends ViewNode< - BranchesView | CommitsView | CompareView | ContributorsView | RemotesView | RepositoriesView | SearchView + BranchesView | CommitsView | ContributorsView | RemotesView | RepositoriesView | SearchAndCompareView > { static key = ':pullrequest'; static getId(repoPath: string, number: number, ref: string): string { @@ -21,7 +20,7 @@ export class PullRequestNode extends ViewNode< } constructor( - view: BranchesView | CommitsView | CompareView | ContributorsView | RemotesView | RepositoriesView | SearchView, + view: BranchesView | CommitsView | ContributorsView | RemotesView | RepositoriesView | SearchAndCompareView, parent: ViewNode, public readonly pullRequest: PullRequest, public readonly branchOrCommit: GitBranch | GitCommit, diff --git a/src/views/nodes/resultsCommitsNode.ts b/src/views/nodes/resultsCommitsNode.ts index b6eef9c..2cd0c07 100644 --- a/src/views/nodes/resultsCommitsNode.ts +++ b/src/views/nodes/resultsCommitsNode.ts @@ -2,7 +2,6 @@ import { TreeItem, TreeItemCollapsibleState } from 'vscode'; import { CommitNode } from './commitNode'; import { LoadMoreNode } from './common'; -import { GlyphChars } from '../../constants'; import { Container } from '../../container'; import { GitLog } from '../../git/git'; import { GitUri } from '../../git/gitUri'; @@ -19,48 +18,50 @@ export interface CommitsQueryResults { more?(limit: number | undefined): Promise; } -export class ResultsCommitsNode extends ViewNode implements PageableViewNode { +export class ResultsCommitsNode + extends ViewNode + implements PageableViewNode { constructor( - view: ViewsWithFiles, + view: View, parent: ViewNode, public readonly repoPath: string, private _label: string, - private readonly _commitsQuery: (limit: number | undefined) => Promise, - private readonly _options: { - id?: string; - description?: string; - expand?: boolean; - includeRepoName?: boolean; + private readonly _results: { + query: (limit: number | undefined) => Promise; + deferred?: boolean; files?: { ref1: string; ref2: string; query: () => Promise; }; + }, + private readonly _options: { + id?: string; + description?: string; + expand?: boolean; } = {}, + splatted?: boolean, ) { super(GitUri.fromRepoPath(repoPath), view, parent); - this._options = { expand: true, includeRepoName: true, ..._options }; + if (splatted != null) { + this.splatted = splatted; + } + this._options = { expand: true, ..._options }; } get id(): string { return `${this.parent!.id}:results:commits${this._options.id ? `:${this._options.id}` : ''}`; } - get type(): ContextValues { - return ContextValues.ResultsCommits; - } - async getChildren(): Promise { const { log } = await this.getCommitsQueryResults(); if (log == null) return []; - const options = { expand: this._options.expand && log.count === 1 }; - const getBranchAndTagTips = await Container.git.getBranchesAndTagsTipsFn(this.uri.repoPath); const children = []; - const { files } = this._options; + const { files } = this._results; if (files != null) { children.push( new ResultsFilesNode(this.view, this, this.uri.repoPath!, files.ref1, files.ref2, files.query, { @@ -69,6 +70,8 @@ export class ResultsCommitsNode extends ViewNode implements Page ); } + const options = { expand: this._options.expand && log.count === 1 }; + children.push( ...insertDateMarkers( Iterables.map( @@ -90,38 +93,35 @@ export class ResultsCommitsNode extends ViewNode implements Page async getTreeItem(): Promise { let label; - let log; let state; - try { - ({ label, log } = await Promises.cancellable(this.getCommitsQueryResults(), 100)); - state = - log == null || log.count === 0 - ? TreeItemCollapsibleState.None - : this._options.expand || log.count === 1 - ? TreeItemCollapsibleState.Expanded - : TreeItemCollapsibleState.Collapsed; - } catch (ex) { - if (ex instanceof Promises.CancellationError) { - ex.promise.then(() => this.triggerChange(false)); - } - - // Need to use Collapsed before we have results or the item won't show up in the view until the children are awaited - // https://github.com/microsoft/vscode/issues/54806 & https://github.com/microsoft/vscode/issues/62214 + if (this._results.deferred) { + label = this._label; state = TreeItemCollapsibleState.Collapsed; - } - - let description = this._options.description; - if (this._options.includeRepoName && (await Container.git.getRepositoryCount()) > 1) { - const repo = await Container.git.getRepository(this.repoPath); - description = `${description ? `${description} ${GlyphChars.Dot} ` : ''}${ - repo?.formattedName ?? this.repoPath - }`; + } else { + try { + let log; + ({ label, log } = await Promises.cancellable(this.getCommitsQueryResults(), 100)); + state = + log == null || log.count === 0 + ? TreeItemCollapsibleState.None + : this._options.expand || log.count === 1 + ? TreeItemCollapsibleState.Expanded + : TreeItemCollapsibleState.Collapsed; + } catch (ex) { + if (ex instanceof Promises.CancellationError) { + ex.promise.then(() => this.triggerChange(false)); + } + + // Need to use Collapsed before we have results or the item won't show up in the view until the children are awaited + // https://github.com/microsoft/vscode/issues/54806 & https://github.com/microsoft/vscode/issues/62214 + state = TreeItemCollapsibleState.Collapsed; + } } const item = new TreeItem(label ?? this._label, state); - item.contextValue = this.type; - item.description = description; + item.contextValue = ContextValues.ResultsCommits; + item.description = this._options.description; item.id = this.id; return item; @@ -139,9 +139,15 @@ export class ResultsCommitsNode extends ViewNode implements Page private _commitsQueryResults: Promise | undefined; private async getCommitsQueryResults() { if (this._commitsQueryResults == null) { - this._commitsQueryResults = this._commitsQuery(this.limit ?? Container.config.advanced.maxSearchItems); + this._commitsQueryResults = this._results.query(this.limit ?? Container.config.advanced.maxSearchItems); const results = await this._commitsQueryResults; this._hasMore = results.hasMore; + + if (this._results.deferred) { + this._results.deferred = false; + + void this.triggerChange(false); + } } return this._commitsQueryResults; diff --git a/src/views/nodes/searchNode.ts b/src/views/nodes/searchNode.ts deleted file mode 100644 index f44cb3c..0000000 --- a/src/views/nodes/searchNode.ts +++ /dev/null @@ -1,147 +0,0 @@ -'use strict'; -import { TreeItem, TreeItemCollapsibleState } from 'vscode'; -import { SearchCommitsCommandArgs } from '../../commands'; -import { GlyphChars } from '../../constants'; -import { debug, gate, Iterables, log, Promises } from '../../system'; -import { View } from '../viewBase'; -import { CommandMessageNode, MessageNode } from './common'; -import { ContextValues, unknownGitUri, ViewNode } from './viewNode'; -import { SearchOperators } from '../../git/git'; - -export class SearchNode extends ViewNode { - private _children: (ViewNode | MessageNode)[] = []; - - constructor(view: View) { - super(unknownGitUri, view); - } - - getChildren(): ViewNode[] { - if (this._children.length === 0) { - const command = { - title: ' ', - command: 'gitlens.showCommitSearch', - }; - - const getCommandArgs = (search: SearchOperators): SearchCommitsCommandArgs => { - return { - search: { pattern: search }, - prefillOnly: true, - }; - }; - - return [ - new CommandMessageNode( - this.view, - this, - { - ...command, - arguments: [this, getCommandArgs('message:')], - }, - 'Search by Message', - `pattern or message: pattern or =: pattern ${GlyphChars.Dash} use quotes to search for phrases`, - `Click to search for commits with matching messages ${GlyphChars.Dash} use quotes to search for phrases`, - ), - new CommandMessageNode( - this.view, - this, - { - ...command, - arguments: [this, getCommandArgs('author:')], - }, - `${GlyphChars.Space.repeat(4)} or, Author`, - 'author: pattern or @: pattern', - 'Click to search for commits with matching authors', - ), - new CommandMessageNode( - this.view, - this, - { - ...command, - arguments: [this, getCommandArgs('commit:')], - }, - `${GlyphChars.Space.repeat(4)} or, Commit ID`, - 'commit: sha or #: sha', - 'Click to search for commits with matching commit ids', - ), - new CommandMessageNode( - this.view, - this, - { - ...command, - arguments: [this, getCommandArgs('file:')], - }, - `${GlyphChars.Space.repeat(4)} or, Files`, - 'file: glob or ?: glob', - 'Click to search for commits with matching files', - ), - new CommandMessageNode( - this.view, - this, - { - ...command, - arguments: [this, getCommandArgs('change:')], - }, - `${GlyphChars.Space.repeat(4)} or, Changes`, - 'change: pattern or ~: pattern', - 'Click to search for commits with matching changes', - ), - ]; - } - - return this._children; - } - - getTreeItem(): TreeItem { - const item = new TreeItem('Search', TreeItemCollapsibleState.Expanded); - item.contextValue = ContextValues.Search; - return item; - } - - addOrReplace(results: ViewNode, replace: boolean) { - if (this._children.includes(results)) return; - - if (this._children.length !== 0 && replace) { - this._children.length = 0; - this._children.push(results); - } else { - this._children.splice(0, 0, results); - } - - this.view.triggerNodeChange(); - } - - @log() - clear() { - if (this._children.length === 0) return; - - this._children.length = 0; - this.view.triggerNodeChange(); - } - - @log({ - args: { 0: (n: ViewNode) => n.toString() }, - }) - dismiss(node: ViewNode) { - if (this._children.length === 0) return; - - const index = this._children.findIndex(n => n === node); - if (index === -1) return; - - this._children.splice(index, 1); - this.view.triggerNodeChange(); - } - - @gate() - @debug() - async refresh() { - if (this._children.length === 0) return; - - const promises: Promise[] = [ - ...Iterables.filterMap(this._children, c => { - const result = c.refresh === undefined ? false : c.refresh(); - return Promises.is(result) ? result : undefined; - }), - ]; - await Promise.all(promises); - } -} diff --git a/src/views/nodes/searchResultsCommitsNode.ts b/src/views/nodes/searchResultsCommitsNode.ts deleted file mode 100644 index 8ea7b39..0000000 --- a/src/views/nodes/searchResultsCommitsNode.ts +++ /dev/null @@ -1,64 +0,0 @@ -'use strict'; -import { TreeItem, TreeItemCollapsibleState } from 'vscode'; -import { Commands, SearchCommitsCommandArgs } from '../../commands'; -import { ViewsWithFiles } from '../viewBase'; -import { CommitsQueryResults, ResultsCommitsNode } from './resultsCommitsNode'; -import { ContextValues, ViewNode } from './viewNode'; -import { RepositoryNode } from './repositoryNode'; -import { SearchPattern } from '../../git/git'; - -let instanceId = 0; - -export class SearchResultsCommitsNode extends ResultsCommitsNode { - static key = ':search-results'; - static getId(repoPath: string, search: SearchPattern | undefined, instanceId: number): string { - return `${RepositoryNode.getId(repoPath)}${this.key}(${ - search === undefined ? '?' : SearchPattern.toKey(search) - }):${instanceId}`; - } - - private _instanceId: number; - - constructor( - view: ViewsWithFiles, - parent: ViewNode, - repoPath: string, - public readonly search: SearchPattern, - label: string, - commitsQuery: (limit: number | undefined) => Promise, - ) { - super(view, parent, repoPath, label, commitsQuery, { - expand: true, - includeRepoName: true, - }); - - this._instanceId = instanceId++; - } - - get id(): string { - return SearchResultsCommitsNode.getId(this.repoPath, this.search, this._instanceId); - } - - get type(): ContextValues { - return ContextValues.SearchResults; - } - - async getTreeItem(): Promise { - const item = await super.getTreeItem(); - - if (item.collapsibleState === TreeItemCollapsibleState.None) { - const args: SearchCommitsCommandArgs = { - search: this.search, - prefillOnly: true, - showResultsInSideBar: true, - }; - item.command = { - title: 'Search Commits', - command: Commands.SearchCommitsInView, - arguments: [args], - }; - } - - return item; - } -} diff --git a/src/views/nodes/searchResultsNode.ts b/src/views/nodes/searchResultsNode.ts new file mode 100644 index 0000000..b5d8958 --- /dev/null +++ b/src/views/nodes/searchResultsNode.ts @@ -0,0 +1,297 @@ +'use strict'; +import { ThemeIcon, TreeItem } from 'vscode'; +import { executeGitCommand } from '../../commands'; +import { Container } from '../../container'; +import { GitLog, SearchPattern } from '../../git/git'; +import { GitUri } from '../../git/gitUri'; +import { RepositoryNode } from './repositoryNode'; +import { CommitsQueryResults, ResultsCommitsNode } from './resultsCommitsNode'; +import { SearchAndCompareView } from '../searchAndCompareView'; +import { debug, gate, log, Strings } from '../../system'; +import { ContextValues, PageableViewNode, ViewNode } from './viewNode'; + +let instanceId = 0; + +interface SearchQueryResults { + readonly label: string; + readonly log: GitLog | undefined; + readonly hasMore: boolean; + more?(limit: number | undefined): Promise; +} + +export class SearchResultsNode extends ViewNode implements PageableViewNode { + static key = ':search-results'; + static getId(repoPath: string, search: SearchPattern | undefined, instanceId: number): string { + return `${RepositoryNode.getId(repoPath)}${this.key}(${ + search == null ? '?' : SearchPattern.toKey(search) + }):${instanceId}`; + } + + static getPinnableId(repoPath: string, search: SearchPattern) { + return Strings.sha1(`${repoPath}|${SearchPattern.toKey(search)}`); + } + + static is(node: any): node is SearchResultsNode { + return node instanceof SearchResultsNode; + } + + private _instanceId: number; + constructor( + view: SearchAndCompareView, + parent: ViewNode, + public readonly repoPath: string, + search: SearchPattern, + private _labels: { + label: string; + queryLabel: + | string + | { + label: string; + resultsType?: { singular: string; plural: string }; + }; + resultsType?: { singular: string; plural: string }; + }, + private _searchQueryOrLog?: + | ((limit: number | undefined) => Promise) + | Promise + | GitLog + | undefined, + private _pinned: number = 0, + ) { + super(GitUri.fromRepoPath(repoPath), view, parent); + + this._search = search; + this._instanceId = instanceId++; + this._order = Date.now(); + } + + get id(): string { + return SearchResultsNode.getId(this.repoPath, this.search, this._instanceId); + } + + get canDismiss(): boolean { + return !this.pinned; + } + + private readonly _order: number = Date.now(); + get order(): number { + return this._pinned || this._order; + } + + get pinned(): boolean { + return this._pinned !== 0; + } + + private _search: SearchPattern; + get search(): SearchPattern { + return this._search; + } + + private _resultsNode: ResultsCommitsNode | undefined; + private ensureResults() { + if (this._resultsNode == null) { + let deferred; + if (this._searchQueryOrLog == null) { + deferred = true; + this._searchQueryOrLog = this.getSearchQuery({ + label: this._labels.queryLabel, + }); + } else if (typeof this._searchQueryOrLog !== 'function') { + this._searchQueryOrLog = this.getSearchQuery( + { + label: this._labels.queryLabel, + }, + this._searchQueryOrLog, + ); + } + + this._resultsNode = new ResultsCommitsNode( + this.view, + this, + this.repoPath, + this._labels.label, + { + query: this._searchQueryOrLog, + deferred: deferred, + }, + { + expand: !this.pinned, + }, + true, + ); + } + + return this._resultsNode; + } + + async getChildren(): Promise { + return this.ensureResults().getChildren(); + } + + async getTreeItem(): Promise { + const item = await this.ensureResults().getTreeItem(); + item.contextValue = `${ContextValues.SearchResults}${this._pinned ? '+pinned' : ''}`; + if ((await Container.git.getRepositoryCount()) > 1) { + const repo = await Container.git.getRepository(this.repoPath); + item.description = repo?.formattedName ?? this.repoPath; + } + if (this._pinned) { + item.iconPath = new ThemeIcon('pinned'); + } + + // if (item.collapsibleState === TreeItemCollapsibleState.None) { + // const args: SearchCommitsCommandArgs = { + // search: this.search, + // prefillOnly: true, + // showResultsInSideBar: true, + // }; + // item.command = { + // title: 'Search Commits', + // command: Commands.SearchCommitsInView, + // arguments: [args], + // }; + // } + + return item; + } + + get hasMore() { + return this.ensureResults().hasMore; + } + + async loadMore(limit?: number) { + return this.ensureResults().loadMore(limit); + } + + async edit(search?: { + pattern: SearchPattern; + labels: { + label: string; + queryLabel: + | string + | { + label: string; + resultsType?: { singular: string; plural: string }; + }; + resultsType?: { singular: string; plural: string }; + }; + log: Promise | GitLog | undefined; + }) { + if (search == null) { + void (await executeGitCommand({ + command: 'search', + prefillOnly: true, + state: { + repo: this.repoPath, + ...this.search, + showResultsInSideBar: this, + }, + })); + + return; + } + + this._search = search.pattern; + this._labels = search.labels; + this._searchQueryOrLog = search.log; + this._resultsNode = undefined; + + void this.triggerChange(false); + } + + @gate() + @debug() + refresh(reset: boolean = false) { + this._resultsNode?.refresh(reset); + } + + @log() + async pin() { + if (this.pinned) return; + + this._pinned = Date.now(); + await this.view.updatePinned(this.getPinnableId(), { + type: 'search', + timestamp: this._pinned, + path: this.repoPath, + labels: this._labels, + search: this.search, + }); + setImmediate(() => this.view.reveal(this, { focus: true, select: true })); + } + + @log() + async unpin() { + if (!this.pinned) return; + + this._pinned = 0; + await this.view.updatePinned(this.getPinnableId()); + setImmediate(() => this.view.reveal(this, { focus: true, select: true })); + } + + private getPinnableId() { + return SearchResultsNode.getPinnableId(this.repoPath, this.search); + } + + private getSearchLabel( + label: + | string + | { + label: string; + resultsType?: { singular: string; plural: string }; + }, + log: GitLog | undefined, + ): string { + if (typeof label === 'string') return label; + + const count = log?.count ?? 0; + + const resultsType = + label.resultsType === undefined ? { singular: 'result', plural: 'results' } : label.resultsType; + + return `${Strings.pluralize(resultsType.singular, count, { + number: log?.hasMore ?? false ? `${count}+` : undefined, + plural: resultsType.plural, + zero: 'No', + })} ${label.label}`; + } + + private getSearchQuery( + options: { + label: + | string + | { + label: string; + resultsType?: { singular: string; plural: string }; + }; + }, + log?: Promise | GitLog, + ): (limit: number | undefined) => Promise { + let useCacheOnce = true; + + return async (limit: number | undefined) => { + log = await (log ?? Container.git.getLogForSearch(this.repoPath, this.search)); + + if (!useCacheOnce && log != null && log.query != null) { + log = await log.query(limit); + } + useCacheOnce = false; + + const results: Mutable = { + label: this.getSearchLabel(options.label, log), + log: log, + hasMore: log?.hasMore ?? false, + }; + if (results.hasMore) { + results.more = async (limit: number | undefined) => { + results.log = (await results.log?.more?.(limit)) ?? results.log; + + results.label = this.getSearchLabel(options.label, results.log); + results.hasMore = results.log?.hasMore ?? true; + }; + } + + return results; + }; + } +} diff --git a/src/views/nodes/viewNode.ts b/src/views/nodes/viewNode.ts index d2778e4..8d555c3 100644 --- a/src/views/nodes/viewNode.ts +++ b/src/views/nodes/viewNode.ts @@ -41,7 +41,7 @@ export enum ContextValues { ResultsCommits = 'gitlens:results:commits', ResultsFile = 'gitlens:file:results', ResultsFiles = 'gitlens:results:files', - Search = 'gitlens:search', + SearchAndCompare = 'gitlens:searchAndCompare', SearchResults = 'gitlens:search:results', Stash = 'gitlens:stash', StashFile = 'gitlens:file:stash', @@ -142,10 +142,6 @@ export function nodeSupportsClearing(node: ViewNode): node is ViewNode & { clear return typeof (node as ViewNode & { clear(): void | Promise }).clear === 'function'; } -export function nodeSupportsConditionalDismissal(node: ViewNode): node is ViewNode & { canDismiss(): boolean } { - return typeof (node as ViewNode & { canDismiss(): boolean }).canDismiss === 'function'; -} - export interface PageableViewNode { readonly id: string; limit?: number; diff --git a/src/views/searchAndCompareView.ts b/src/views/searchAndCompareView.ts new file mode 100644 index 0000000..d549586 --- /dev/null +++ b/src/views/searchAndCompareView.ts @@ -0,0 +1,502 @@ +'use strict'; +import { commands, ConfigurationChangeEvent, TreeItem, TreeItemCollapsibleState } from 'vscode'; +import { + BranchSorting, + configuration, + SearchAndCompareViewConfig, + TagSorting, + ViewFilesLayout, +} from '../configuration'; +import { CommandContext, NamedRef, PinnedItem, PinnedItems, setCommandContext, WorkspaceState } from '../constants'; +import { Container } from '../container'; +import { GitLog, GitRevision, SearchPattern } from '../git/git'; +import { CompareResultsNode, ContextValues, SearchResultsNode, unknownGitUri, ViewNode } from './nodes'; +import { debug, gate, Iterables, log, Promises } from '../system'; +import { ViewBase } from './viewBase'; +import { ComparePickerNode } from './nodes/comparePickerNode'; +import { ReferencePicker, ReferencesQuickPickIncludes } from '../quickpicks'; +import { getRepoPathOrPrompt } from '../commands'; + +interface DeprecatedPinnedComparison { + path: string; + ref1: NamedRef; + ref2: NamedRef; + notation?: '..' | '...'; +} + +interface DeprecatedPinnedComparisons { + [id: string]: DeprecatedPinnedComparison; +} + +export class SearchAndCompareViewNode extends ViewNode { + protected splatted = true; + private comparePicker: ComparePickerNode | undefined; + + constructor(view: SearchAndCompareView) { + super(unknownGitUri, view); + } + + private _children: (ComparePickerNode | CompareResultsNode | SearchResultsNode)[] | undefined; + private get children(): (ComparePickerNode | CompareResultsNode | SearchResultsNode)[] { + if (this._children == null) { + this._children = []; + + // Get pinned searches & comparisons + const pinned = this.view.getPinned(); + if (pinned.length !== 0) { + this._children.push(...pinned); + } + } + + return this._children; + } + + getChildren(): ViewNode[] { + return this.children.sort((a, b) => (a.pinned ? -1 : 1) - (b.pinned ? -1 : 1) || b.order - a.order); + } + + getTreeItem(): TreeItem { + this.splatted = false; + + const item = new TreeItem('SearchAndCompare', TreeItemCollapsibleState.Expanded); + item.contextValue = ContextValues.SearchAndCompare; + return item; + } + + addOrReplace(results: CompareResultsNode | SearchResultsNode, replace: boolean) { + if (this.children.includes(results)) return; + + if (replace) { + this.clear(); + } + + this.children.push(results); + + this.view.triggerNodeChange(); + } + + @log() + clear(silent: boolean = false) { + if (this.children.length === 0) return; + + this.removeComparePicker(true); + const index = this._children!.findIndex(c => !c.pinned); + if (index !== -1) { + this._children!.splice(index, this._children!.length); + } + + if (!silent) { + this.view.triggerNodeChange(); + } + } + + @log({ + args: { 0: (n: ViewNode) => n.toString() }, + }) + dismiss(node: ComparePickerNode | CompareResultsNode | SearchResultsNode) { + if (node === this.comparePicker) { + this.removeComparePicker(); + + return; + } + + if (this.children.length === 0) return; + + const index = this.children.indexOf(node); + if (index === -1) return; + + this.children.splice(index, 1); + + this.view.triggerNodeChange(); + } + + @gate() + @debug() + async refresh() { + if (this.children.length === 0) return; + + const promises: Promise[] = [ + ...Iterables.filterMap(this.children, c => { + const result = c.refresh === undefined ? false : c.refresh(); + return Promises.is(result) ? result : undefined; + }), + ]; + await Promise.all(promises); + } + + async compareWithSelected(repoPath?: string, ref?: string | NamedRef) { + const selectedRef = this.comparePicker?.selectedRef; + if (selectedRef == null) return; + + if (repoPath == null) { + repoPath = selectedRef.repoPath; + } else if (repoPath !== selectedRef.repoPath) { + // If we don't have a matching repoPath, then start over + void this.selectForCompare(repoPath, ref); + + return; + } + + if (ref == null) { + const pick = await ReferencePicker.show( + repoPath, + `Compare ${this.getRefName(selectedRef.ref)} with`, + 'Choose a reference to compare with', + { + allowEnteringRefs: true, + picked: typeof selectedRef.ref === 'string' ? selectedRef.ref : selectedRef.ref.ref, + // checkmarks: true, + include: + ReferencesQuickPickIncludes.BranchesAndTags | + ReferencesQuickPickIncludes.HEAD | + ReferencesQuickPickIncludes.WorkingTree, + sort: { + branches: { current: true, orderBy: BranchSorting.DateDesc }, + tags: { orderBy: TagSorting.DateDesc }, + }, + }, + ); + if (pick == null) { + if (this.comparePicker != null) { + await this.view.show(); + await this.view.reveal(this.comparePicker, { focus: true, select: true }); + } + + return; + } + + ref = pick.ref; + } + + this.removeComparePicker(); + void (await this.view.compare(repoPath, selectedRef.ref, ref)); + } + + async selectForCompare(repoPath?: string, ref?: string | NamedRef) { + if (repoPath == null) { + repoPath = await getRepoPathOrPrompt('Compare'); + } + if (repoPath == null) return; + + this.removeComparePicker(true); + + let autoCompare = false; + if (ref == null) { + const pick = await ReferencePicker.show(repoPath, 'Compare', 'Choose a reference to compare', { + allowEnteringRefs: true, + // checkmarks: false, + include: + ReferencesQuickPickIncludes.BranchesAndTags | + ReferencesQuickPickIncludes.HEAD | + ReferencesQuickPickIncludes.WorkingTree, + sort: { + branches: { current: true, orderBy: BranchSorting.DateDesc }, + tags: { orderBy: TagSorting.DateDesc }, + }, + }); + if (pick == null) { + await this.triggerChange(); + + return; + } + + ref = pick.ref; + + autoCompare = true; + } + + this.comparePicker = new ComparePickerNode(this.view, this, { + label: this.getRefName(ref), + repoPath: repoPath, + ref: ref, + }); + this.children.splice(0, 0, this.comparePicker); + void setCommandContext(CommandContext.ViewsCanCompare, true); + + await this.triggerChange(); + + await this.view.reveal(this.comparePicker, { focus: false, select: true }); + + if (autoCompare) { + await this.compareWithSelected(); + } + } + + private getRefName(ref: string | NamedRef) { + return typeof ref === 'string' + ? GitRevision.shorten(ref, { strings: { working: 'Working Tree' } })! + : ref.label ?? GitRevision.shorten(ref.ref)!; + } + + private removeComparePicker(silent: boolean = false) { + void setCommandContext(CommandContext.ViewsCanCompare, false); + if (this.comparePicker != null) { + const index = this.children.indexOf(this.comparePicker); + if (index !== -1) { + this.children.splice(index, 1); + if (!silent) { + void this.triggerChange(); + } + } + this.comparePicker = undefined; + } + } +} + +export class SearchAndCompareView extends ViewBase { + protected readonly configKey = 'searchAndCompare'; + + constructor() { + super('gitlens.views.searchAndCompare', 'Search & Compare'); + + void setCommandContext(CommandContext.ViewsSearchAndCompareKeepResults, this.keepResults); + } + + getRoot() { + return new SearchAndCompareViewNode(this); + } + + protected registerCommands() { + void Container.viewCommands; + + commands.registerCommand(this.getQualifiedCommand('clear'), () => this.clear(), this); + commands.registerCommand( + this.getQualifiedCommand('copy'), + () => commands.executeCommand('gitlens.views.copy', this.selection), + this, + ); + commands.registerCommand(this.getQualifiedCommand('refresh'), () => this.refresh(true), this); + commands.registerCommand( + this.getQualifiedCommand('setFilesLayoutToAuto'), + () => this.setFilesLayout(ViewFilesLayout.Auto), + this, + ); + commands.registerCommand( + this.getQualifiedCommand('setFilesLayoutToList'), + () => this.setFilesLayout(ViewFilesLayout.List), + this, + ); + commands.registerCommand( + this.getQualifiedCommand('setFilesLayoutToTree'), + () => this.setFilesLayout(ViewFilesLayout.Tree), + this, + ); + commands.registerCommand(this.getQualifiedCommand('setKeepResultsToOn'), () => this.setKeepResults(true), this); + commands.registerCommand( + this.getQualifiedCommand('setKeepResultsToOff'), + () => this.setKeepResults(false), + this, + ); + commands.registerCommand(this.getQualifiedCommand('setShowAvatarsOn'), () => this.setShowAvatars(true), this); + commands.registerCommand(this.getQualifiedCommand('setShowAvatarsOff'), () => this.setShowAvatars(false), this); + + commands.registerCommand(this.getQualifiedCommand('pin'), this.pin, this); + commands.registerCommand(this.getQualifiedCommand('unpin'), this.unpin, this); + commands.registerCommand(this.getQualifiedCommand('edit'), this.edit, this); + commands.registerCommand(this.getQualifiedCommand('swapComparison'), this.swapComparison, this); + commands.registerCommand(this.getQualifiedCommand('selectForCompare'), this.selectForCompare, this); + commands.registerCommand(this.getQualifiedCommand('compareWithSelected'), this.compareWithSelected, this); + } + + protected filterConfigurationChanged(e: ConfigurationChangeEvent) { + const changed = super.filterConfigurationChanged(e); + if ( + !changed && + !configuration.changed(e, 'defaultDateFormat') && + !configuration.changed(e, 'defaultDateSource') && + !configuration.changed(e, 'defaultDateStyle') && + !configuration.changed(e, 'defaultGravatarsStyle') + ) { + return false; + } + + return true; + } + + get keepResults(): boolean { + return Container.context.workspaceState.get(WorkspaceState.ViewsSearchAndCompareKeepResults, true); + } + + clear() { + this.root?.clear(); + } + + dismissNode(node: ViewNode) { + if ( + this.root == null || + (!(node instanceof ComparePickerNode) && + !(node instanceof CompareResultsNode) && + !(node instanceof SearchResultsNode)) || + !node.canDismiss + ) { + return; + } + + this.root.dismiss(node); + } + + compare(repoPath: string, ref1: string | NamedRef, ref2: string | NamedRef) { + return this.addResults( + new CompareResultsNode( + this, + this.ensureRoot(), + repoPath, + typeof ref1 === 'string' ? { ref: ref1 } : ref1, + typeof ref2 === 'string' ? { ref: ref2 } : ref2, + ), + ); + } + + compareWithSelected(repoPath?: string, ref?: string | NamedRef) { + void this.ensureRoot().compareWithSelected(repoPath, ref); + } + + selectForCompare(repoPath?: string, ref?: string | NamedRef) { + void this.ensureRoot().selectForCompare(repoPath, ref); + } + + async search( + repoPath: string, + search: SearchPattern, + { + label, + reveal, + }: { + label: + | string + | { + label: string; + resultsType?: { singular: string; plural: string }; + }; + reveal?: { + select?: boolean; + focus?: boolean; + expand?: boolean | number; + }; + }, + results?: Promise | GitLog, + updateNode?: SearchResultsNode, + ) { + if (!this.visible) { + await this.show(); + } + + const labels = { label: `Results ${typeof label === 'string' ? label : label.label}`, queryLabel: label }; + if (updateNode != null) { + await updateNode.edit({ pattern: search, labels: labels, log: results }); + + return; + } + + await this.addResults(new SearchResultsNode(this, this.root!, repoPath, search, labels, results), reveal); + } + + getPinned() { + let savedPins = Container.context.workspaceState.get( + WorkspaceState.ViewsSearchAndComparePinnedItems, + ); + if (savedPins == null) { + // Migrate any deprecated pinned items + const deprecatedPins = Container.context.workspaceState.get( + WorkspaceState.DeprecatedPinnedComparisons, + ); + if (deprecatedPins == null) return []; + + savedPins = Object.create(null) as PinnedItems; + for (const p of Object.values(deprecatedPins)) { + savedPins[CompareResultsNode.getPinnableId(p.path, p.ref1.ref, p.ref2.ref)] = { + type: 'comparison', + timestamp: Date.now(), + path: p.path, + ref1: p.ref1, + ref2: p.ref2, + }; + } + + void Container.context.workspaceState.update(WorkspaceState.ViewsSearchAndComparePinnedItems, savedPins); + void Container.context.workspaceState.update(WorkspaceState.DeprecatedPinnedComparisons, undefined); + } + + const root = this.ensureRoot(); + return Object.values(savedPins) + .sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0)) + .map(p => + p.type === 'comparison' + ? new CompareResultsNode(this, root, p.path, p.ref1, p.ref2, p.timestamp) + : new SearchResultsNode(this, root, p.path, p.search, p.labels, undefined, p.timestamp), + ); + } + + async updatePinned(id: string, pin?: PinnedItem) { + let pinned = Container.context.workspaceState.get(WorkspaceState.ViewsSearchAndComparePinnedItems); + if (pinned == null) { + pinned = Object.create(null) as PinnedItems; + } + + if (pin != null) { + pinned[id] = { ...pin }; + } else { + const { [id]: _, ...rest } = pinned; + pinned = rest; + } + + await Container.context.workspaceState.update(WorkspaceState.ViewsSearchAndComparePinnedItems, pinned); + + this.triggerNodeChange(this.ensureRoot()); + } + + private async addResults( + results: CompareResultsNode | SearchResultsNode, + options: { + expand?: boolean | number; + focus?: boolean; + select?: boolean; + } = { expand: true, focus: true, select: true }, + ) { + if (!this.visible) { + await this.show(); + } + + const root = this.ensureRoot(); + root.addOrReplace(results, !this.keepResults); + + setImmediate(() => this.reveal(results, options)); + } + + private edit(node: SearchResultsNode) { + if (!(node instanceof SearchResultsNode)) return undefined; + + return node.edit(); + } + + private setFilesLayout(layout: ViewFilesLayout) { + return configuration.updateEffective('views', this.configKey, 'files', 'layout', layout); + } + + private setKeepResults(enabled: boolean) { + void Container.context.workspaceState.update(WorkspaceState.ViewsSearchAndCompareKeepResults, enabled); + void setCommandContext(CommandContext.ViewsSearchAndCompareKeepResults, enabled); + } + + private setShowAvatars(enabled: boolean) { + return configuration.updateEffective('views', this.configKey, 'avatars', enabled); + } + + private pin(node: CompareResultsNode | SearchResultsNode) { + if (!(node instanceof CompareResultsNode) && !(node instanceof SearchResultsNode)) return undefined; + + return node.pin(); + } + + private swapComparison(node: CompareResultsNode) { + if (!(node instanceof CompareResultsNode)) return undefined; + + return node.swap(); + } + + private unpin(node: CompareResultsNode | SearchResultsNode) { + if (!(node instanceof CompareResultsNode) && !(node instanceof SearchResultsNode)) return undefined; + + return node.unpin(); + } +} diff --git a/src/views/searchView.ts b/src/views/searchView.ts deleted file mode 100644 index 46e9940..0000000 --- a/src/views/searchView.ts +++ /dev/null @@ -1,279 +0,0 @@ -'use strict'; -import { commands, ConfigurationChangeEvent } from 'vscode'; -import { configuration, SearchViewConfig, ViewFilesLayout } from '../configuration'; -import { CommandContext, setCommandContext, WorkspaceState } from '../constants'; -import { Container } from '../container'; -import { GitLog, SearchPattern } from '../git/git'; -import { Functions, Strings } from '../system'; -import { nodeSupportsConditionalDismissal, SearchNode, SearchResultsCommitsNode, ViewNode } from './nodes'; -import { ViewBase } from './viewBase'; - -interface SearchQueryResults { - readonly label: string; - readonly log: GitLog | undefined; - readonly hasMore: boolean; - more?(limit: number | undefined): Promise; -} - -export class SearchView extends ViewBase { - protected readonly configKey = 'search'; - - constructor() { - super('gitlens.views.search', 'Search Commits'); - - void setCommandContext(CommandContext.ViewsSearchKeepResults, this.keepResults); - } - - getRoot() { - return new SearchNode(this); - } - - protected registerCommands() { - void Container.viewCommands; - - commands.registerCommand(this.getQualifiedCommand('clear'), () => this.clear(), this); - commands.registerCommand( - this.getQualifiedCommand('copy'), - () => commands.executeCommand('gitlens.views.copy', this.selection), - this, - ); - commands.registerCommand(this.getQualifiedCommand('refresh'), () => this.refresh(true), this); - commands.registerCommand( - this.getQualifiedCommand('setFilesLayoutToAuto'), - () => this.setFilesLayout(ViewFilesLayout.Auto), - this, - ); - commands.registerCommand( - this.getQualifiedCommand('setFilesLayoutToList'), - () => this.setFilesLayout(ViewFilesLayout.List), - this, - ); - commands.registerCommand( - this.getQualifiedCommand('setFilesLayoutToTree'), - () => this.setFilesLayout(ViewFilesLayout.Tree), - this, - ); - commands.registerCommand(this.getQualifiedCommand('setKeepResultsToOn'), () => this.setKeepResults(true), this); - commands.registerCommand( - this.getQualifiedCommand('setKeepResultsToOff'), - () => this.setKeepResults(false), - this, - ); - commands.registerCommand(this.getQualifiedCommand('setShowAvatarsOn'), () => this.setShowAvatars(true), this); - commands.registerCommand(this.getQualifiedCommand('setShowAvatarsOff'), () => this.setShowAvatars(false), this); - } - - protected filterConfigurationChanged(e: ConfigurationChangeEvent) { - const changed = super.filterConfigurationChanged(e); - if ( - !changed && - !configuration.changed(e, 'defaultDateFormat') && - !configuration.changed(e, 'defaultDateSource') && - !configuration.changed(e, 'defaultDateStyle') && - !configuration.changed(e, 'defaultGravatarsStyle') - ) { - return false; - } - - return true; - } - - get keepResults(): boolean { - return Container.context.workspaceState.get(WorkspaceState.ViewsSearchKeepResults, false); - } - - clear() { - this.root?.clear(); - } - - dismissNode(node: ViewNode) { - if (this.root == null) return; - if (nodeSupportsConditionalDismissal(node) && node.canDismiss() === false) return; - - this.root.dismiss(node); - } - - async search( - repoPath: string, - search: SearchPattern, - { - label, - reveal, - ...options - }: { - label: - | string - | { - label: string; - resultsType?: { singular: string; plural: string }; - }; - limit?: number; - reveal?: { - select?: boolean; - focus?: boolean; - expand?: boolean | number; - }; - }, - results?: Promise | GitLog, - ) { - if (!this.visible) { - await this.show(); - } - - const searchQueryFn = this.getSearchQueryFn( - results ?? Container.git.getLogForSearch(repoPath, search, options), - { label: label }, - ); - - return this.addResults( - new SearchResultsCommitsNode( - this, - this.root!, - repoPath, - search, - `Results ${typeof label === 'string' ? label : label.label}`, - searchQueryFn, - ), - reveal, - ); - } - - showSearchResults( - repoPath: string, - search: SearchPattern, - log: GitLog, - { - label, - reveal, - - ...options - }: { - label: - | string - | { - label: string; - resultsType?: { singular: string; plural: string }; - }; - limit?: number; - reveal?: { - select?: boolean; - focus?: boolean; - expand?: boolean | number; - }; - }, - ) { - const labelString = this.getSearchLabel(label, log); - const results: Mutable> = { - label: labelString, - log: log, - hasMore: log.hasMore, - }; - if (results.hasMore) { - results.more = async (limit: number | undefined) => { - results.log = (await results.log?.more?.(limit)) ?? results.log; - - results.label = this.getSearchLabel(label, results.log); - results.hasMore = results.log?.hasMore ?? true; - }; - } - - const searchQueryFn = Functions.cachedOnce( - this.getSearchQueryFn(log, { label: label, ...options }), - results as SearchQueryResults, - ); - - return this.addResults( - new SearchResultsCommitsNode(this, this.root!, repoPath, search, labelString, searchQueryFn), - reveal, - ); - } - - private addResults( - results: ViewNode, - options: { - select?: boolean; - focus?: boolean; - expand?: boolean | number; - } = { select: true, expand: true }, - ) { - const root = this.ensureRoot(); - root.addOrReplace(results, !this.keepResults); - - setImmediate(() => void this.reveal(results, options)); - } - - private getSearchLabel( - label: - | string - | { - label: string; - resultsType?: { singular: string; plural: string }; - }, - log: GitLog | undefined, - ) { - if (typeof label === 'string') return label; - - const count = log?.count ?? 0; - - const resultsType = - label.resultsType === undefined ? { singular: 'result', plural: 'results' } : label.resultsType; - - return `${Strings.pluralize(resultsType.singular, count, { - number: log?.hasMore ?? false ? `${count}+` : undefined, - plural: resultsType.plural, - zero: 'No', - })} ${label.label}`; - } - - private getSearchQueryFn( - logOrPromise: Promise | GitLog | undefined, - options: { - label: - | string - | { - label: string; - resultsType?: { singular: string; plural: string }; - }; - }, - ): (limit: number | undefined) => Promise { - let useCacheOnce = true; - - return async (limit: number | undefined) => { - let log = await logOrPromise; - - if (!useCacheOnce && log !== undefined && log.query !== undefined) { - log = await log.query(limit); - } - useCacheOnce = false; - - const results: Mutable> = { - label: this.getSearchLabel(options.label, log), - log: log, - hasMore: log?.hasMore, - }; - if (results.hasMore) { - results.more = async (limit: number | undefined) => { - results.log = (await results.log?.more?.(limit)) ?? results.log; - - results.label = this.getSearchLabel(options.label, results.log); - results.hasMore = results.log?.hasMore ?? true; - }; - } - - return results as SearchQueryResults; - }; - } - - private setFilesLayout(layout: ViewFilesLayout) { - return configuration.updateEffective('views', this.configKey, 'files', 'layout', layout); - } - - private setKeepResults(enabled: boolean) { - void Container.context.workspaceState.update(WorkspaceState.ViewsSearchKeepResults, enabled); - void setCommandContext(CommandContext.ViewsSearchKeepResults, enabled); - } - - private setShowAvatars(enabled: boolean) { - return configuration.updateEffective('views', this.configKey, 'avatars', enabled); - } -} diff --git a/src/views/viewBase.ts b/src/views/viewBase.ts index 4186b01..5878f00 100644 --- a/src/views/viewBase.ts +++ b/src/views/viewBase.ts @@ -18,18 +18,16 @@ import { } from 'vscode'; import { BranchesView } from './branchesView'; import { CommitsView } from './commitsView'; -import { CompareView } from './compareView'; import { BranchesViewConfig, CommitsViewConfig, - CompareViewConfig, configuration, ContributorsViewConfig, FileHistoryViewConfig, LineHistoryViewConfig, RemotesViewConfig, RepositoriesViewConfig, - SearchViewConfig, + SearchAndCompareViewConfig, StashesViewConfig, TagsViewConfig, ViewsCommonConfig, @@ -45,31 +43,29 @@ import { Logger } from '../logger'; import { PageableViewNode, ViewNode } from './nodes'; import { RemotesView } from './remotesView'; import { RepositoriesView } from './repositoriesView'; -import { SearchView } from './searchView'; +import { SearchAndCompareView } from './searchAndCompareView'; import { StashesView } from './stashesView'; import { debug, Functions, log, Promises, Strings } from '../system'; import { TagsView } from './tagsView'; export type View = | BranchesView - | CompareView | ContributorsView | FileHistoryView | CommitsView | LineHistoryView | RemotesView | RepositoriesView - | SearchView + | SearchAndCompareView | StashesView | TagsView; export type ViewsWithFiles = | BranchesView - | CompareView | ContributorsView | CommitsView | RemotesView | RepositoriesView - | SearchView + | SearchAndCompareView | StashesView | TagsView; @@ -81,14 +77,13 @@ export abstract class ViewBase< RootNode extends ViewNode, ViewConfig extends | BranchesViewConfig - | CompareViewConfig | ContributorsViewConfig | FileHistoryViewConfig | CommitsViewConfig | LineHistoryViewConfig | RemotesViewConfig | RepositoriesViewConfig - | SearchViewConfig + | SearchAndCompareViewConfig | StashesViewConfig | TagsViewConfig > implements TreeDataProvider, Disposable { diff --git a/src/views/viewCommands.ts b/src/views/viewCommands.ts index 710ee4e..32b4d54 100644 --- a/src/views/viewCommands.ts +++ b/src/views/viewCommands.ts @@ -570,7 +570,7 @@ export class ViewCommands { private compareWithHead(node: ViewRefNode) { if (!(node instanceof ViewRefNode)) return Promise.resolve(); - return Container.compareView.compare(node.repoPath, node.ref, 'HEAD'); + return Container.searchAndCompareView.compare(node.repoPath, node.ref, 'HEAD'); } @debug() @@ -578,14 +578,14 @@ export class ViewCommands { if (!(node instanceof BranchNode)) return Promise.resolve(); if (!node.branch.tracking) return Promise.resolve(); - return Container.compareView.compare(node.repoPath, node.branch.tracking, node.ref); + return Container.searchAndCompareView.compare(node.repoPath, node.branch.tracking, node.ref); } @debug() private compareWithWorking(node: ViewRefNode) { if (!(node instanceof ViewRefNode)) return Promise.resolve(); - return Container.compareView.compare(node.repoPath, node.ref, ''); + return Container.searchAndCompareView.compare(node.repoPath, node.ref, ''); } @debug() @@ -598,7 +598,7 @@ export class ViewCommands { const commonAncestor = await Container.git.getMergeBase(node.repoPath, branch.ref, node.ref.ref); if (commonAncestor == null) return undefined; - return Container.compareView.compare( + return Container.searchAndCompareView.compare( node.repoPath, { ref: commonAncestor, label: `ancestry with ${node.ref.ref} (${GitRevision.shorten(commonAncestor)})` }, '', @@ -609,14 +609,14 @@ export class ViewCommands { private compareWithSelected(node: ViewRefNode) { if (!(node instanceof ViewRefNode)) return; - Container.compareView.compareWithSelected(node.repoPath, node.ref); + Container.searchAndCompareView.compareWithSelected(node.repoPath, node.ref); } @debug() private selectForCompare(node: ViewRefNode) { if (!(node instanceof ViewRefNode)) return; - Container.compareView.selectForCompare(node.repoPath, node.ref); + Container.searchAndCompareView.selectForCompare(node.repoPath, node.ref); } @debug() diff --git a/src/webviews/apps/settings/partials/views.compare.html b/src/webviews/apps/settings/partials/views.compare.html deleted file mode 100644 index 364fa8d..0000000 --- a/src/webviews/apps/settings/partials/views.compare.html +++ /dev/null @@ -1,140 +0,0 @@ -
-
-

- Compare view - - - -

- -

- Adds a Compare view to visualize comparisons between branches, tags, commits, and more -

-
- -
-
-
-
-
-
-
-
-
- - -
-

- Requires a connection to a supported remote service (e.g. GitHub) -

-
- -
-
-
- - -
-
-
-
-
-
- -
-
- -
- -
-
-
- -
-
- - -
-

Compacts (flattens) unnecessary nesting when using a tree layouts

-
- -
-
- - -
-
-
-
- -
- - - - -
-
- -
-

- For more options, open - Settings - and search for gitlens.views.compare or gitlens.views -

-
-
-
diff --git a/src/webviews/apps/settings/partials/views.search.html b/src/webviews/apps/settings/partials/views.search.html deleted file mode 100644 index d50d98a..0000000 --- a/src/webviews/apps/settings/partials/views.search.html +++ /dev/null @@ -1,135 +0,0 @@ -
-
-

- Search Commits view - - - -

- -

- Adds a Search Commits view to search and explore commit histories by message, author, files, id, etc -

-
- -
-
-
-
-
-
-
-
-
- - -
-

- Requires a connection to a supported remote service (e.g. GitHub) -

-
- -
-
-
- - -
-
-
-
-
-
- -
-
- -
- -
-
-
- -
-
- - -
-

Compacts (flattens) unnecessary nesting when using a tree layouts

-
- -
-
- - -
-
-
-
- -
- - - - -
-
- -
-

- For more options, open - Settings - and search for gitlens.views.search or gitlens.views -

-
-
-
diff --git a/src/webviews/apps/settings/partials/views.searchAndCompare.html b/src/webviews/apps/settings/partials/views.searchAndCompare.html new file mode 100644 index 0000000..54c98b4 --- /dev/null +++ b/src/webviews/apps/settings/partials/views.searchAndCompare.html @@ -0,0 +1,168 @@ +
+
+

+ Search & Compare view + + + +

+ +

+ Adds a Search & Compare view to search and explore commit histories by message, author, files, id, etc, + or visualize comparisons between branches, tags, commits, and more +

+
+ +
+
+
+
+
+
+
+
+
+ + +
+

+ Requires a connection to a supported remote service (e.g. GitHub) +

+
+ +
+
+
+ + +
+
+
+
+
+
+ +
+
+ +
+ +
+
+
+ +
+
+ + +
+

Compacts (flattens) unnecessary nesting when using a tree layouts

+
+ +
+
+ + +
+
+
+
+ +
+ + + + +
+ + +
+ +
+

+ For more options, open + Settings + and search for gitlens.views.searchAndCompare or gitlens.views +

+
+
+
diff --git a/src/webviews/apps/settings/settings.html b/src/webviews/apps/settings/settings.html index 51336b2..7e7c536 100644 --- a/src/webviews/apps/settings/settings.html +++ b/src/webviews/apps/settings/settings.html @@ -207,9 +207,7 @@ <%= require('html-loader!./partials/views.contributors.html') %> - <%= require('html-loader!./partials/views.search.html') %> - - <%= require('html-loader!./partials/views.compare.html') %> + <%= require('html-loader!./partials/views.searchAndCompare.html') %> <%= require('html-loader!./partials/blame.html') %> @@ -369,18 +367,9 @@ Search Commits view - -
  • - Compare viewSearch & Compare view
  • diff --git a/src/webviews/settingsWebview.ts b/src/webviews/settingsWebview.ts index e1dd7e0..e368ef0 100644 --- a/src/webviews/settingsWebview.ts +++ b/src/webviews/settingsWebview.ts @@ -24,13 +24,12 @@ export class SettingsWebview extends WebviewBase { ...[ Commands.ShowSettingsPageAndJumpToBranchesView, Commands.ShowSettingsPageAndJumpToCommitsView, - Commands.ShowSettingsPageAndJumpToCompareView, Commands.ShowSettingsPageAndJumpToContributorsView, Commands.ShowSettingsPageAndJumpToFileHistoryView, Commands.ShowSettingsPageAndJumpToLineHistoryView, Commands.ShowSettingsPageAndJumpToRemotesView, Commands.ShowSettingsPageAndJumpToRepositoriesView, - Commands.ShowSettingsPageAndJumpToSearchCommitsView, + Commands.ShowSettingsPageAndJumpToSearchAndCompareView, Commands.ShowSettingsPageAndJumpToStashesView, Commands.ShowSettingsPageAndJumpToTagsView, ].map(c => {