Skip to content

Commit 8facc8c

Browse files
committed
fix: multiple selection fix
1 parent de022e5 commit 8facc8c

File tree

6 files changed

+107
-36
lines changed

6 files changed

+107
-36
lines changed

src/decoration/FileNodeDecoration.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ export class FileNodeDecorationProvider implements FileDecorationProvider {
1717
try {
1818
const params = new URLSearchParams(decodeURIComponent(uri.query))
1919
const status = params.get('status')
20+
const type = params.get('type')
2021

22+
if (type === 'folder') {
23+
return undefined
24+
}
2125
if (!status) {
2226
logger.warn('No status in URI:', uri.toString())
2327
return undefined

src/storage.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export const useStorage = createSingletonComposable(() => {
1919
}
2020

2121
function getCommits(): CommitGraph {
22-
return extensionContext.value?.globalState.get<CommitGraph>(COMMITS_KEY) || ({
22+
return (extensionContext.value?.globalState.get<CommitGraph>(COMMITS_KEY)) || ({
2323
operations: [],
2424
branches: [],
2525
logResult: {
@@ -31,6 +31,7 @@ export const useStorage = createSingletonComposable(() => {
3131

3232
function getCommit(hash: string): Commit | undefined {
3333
const { logResult: { all: commits } } = getCommits()
34+
3435
if (!commits)
3536
return
3637
return commits.find(commit => commit.hash === hash)

src/views/diff/DiffTreeView.ts

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ export const useDiffTreeView = createSingletonComposable(() => {
2424
if (hashes.length === 0) {
2525
commitDetailsList.value = []
2626
files.value = []
27-
return
27+
return null
2828
}
2929

3030
const commitList: CommitDetails[] = []
31-
const allCommitFiles: Map<string, TreeViewNode[]> = new Map()
31+
const allCommitFiles: Map<string, { files: TreeViewNode[], total: number }> = new Map()
3232
let totalFiles = 0
3333

3434
for (const hash of hashes) {
@@ -51,15 +51,13 @@ export const useDiffTreeView = createSingletonComposable(() => {
5151

5252
const { files: commitFiles, total } = await fileTree.getChildren(hash)
5353

54-
allCommitFiles.set(hash, commitFiles)
54+
allCommitFiles.set(hash, { files: commitFiles, total })
5555
totalFiles += total
5656
}
5757

5858
commitDetailsList.value = commitList
5959

60-
// Aggregate file changes for all selected commits
6160
if (hashes.length === 1) {
62-
// Single commit view - show files directly
6361
files.value = [
6462
{
6563
treeItem: new CommitNode(
@@ -68,7 +66,7 @@ export const useDiffTreeView = createSingletonComposable(() => {
6866
TreeItemCollapsibleState.Expanded,
6967
'files',
7068
),
71-
children: allCommitFiles.get(hashes[0]) || [],
69+
children: allCommitFiles.get(hashes[0])?.files || [],
7270
},
7371
]
7472
}
@@ -81,12 +79,12 @@ export const useDiffTreeView = createSingletonComposable(() => {
8179
if (!commit)
8280
continue
8381

84-
const commitFiles = allCommitFiles.get(hash) || []
82+
const { files: commitFiles, total } = allCommitFiles.get(hash) || { files: [], total: 0 }
8583
const shortHash = hash.substring(0, 7)
8684

8785
commitNodes.push({
8886
treeItem: new CommitNode(
89-
`${commitFiles.length} Files Changed`,
87+
`${total} Files Changed`,
9088
`${shortHash}`,
9189
TreeItemCollapsibleState.Expanded,
9290
'git-commit',
@@ -98,8 +96,8 @@ export const useDiffTreeView = createSingletonComposable(() => {
9896
files.value = [
9997
{
10098
treeItem: new CommitNode(
101-
'Selected Commits',
102-
`${hashes.length} Commits Selected`,
99+
'Multiple Commits Selected',
100+
`${commitDetailsList.value.length} commits`,
103101
TreeItemCollapsibleState.Expanded,
104102
'git-commit',
105103
),
@@ -151,16 +149,7 @@ export const useDiffTreeView = createSingletonComposable(() => {
151149
]
152150
}
153151
else {
154-
// Multiple commits view - summary information
155152
return [
156-
{
157-
treeItem: new CommitNode(
158-
'Multiple Commits Selected',
159-
`${commitDetailsList.value.length} commits`,
160-
TreeItemCollapsibleState.None,
161-
'git-commit',
162-
),
163-
},
164153
...files.value,
165154
]
166155
}
@@ -174,7 +163,7 @@ export const useDiffTreeView = createSingletonComposable(() => {
174163
},
175164
)
176165

177-
async function refresh(hashes: string[]) {
166+
async function refresh(hashes: string[] = []) {
178167
const currentHashes = new Set(selectedCommitHashes.value)
179168
const newHashes = new Set(hashes)
180169

@@ -187,9 +176,16 @@ export const useDiffTreeView = createSingletonComposable(() => {
187176
await loadCommitDetails(hashes)
188177
}
189178

179+
function clearSelection() {
180+
selectedCommitHashes.value = []
181+
commitDetailsList.value = []
182+
refresh()
183+
}
184+
190185
return {
191186
tree,
192-
refresh,
193187
selectedCommitHashes,
188+
refresh,
189+
clearSelection,
194190
}
195191
})

src/views/history/App.vue

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ const error = ref<string>('')
2828
const vscode = acquireVsCodeApi<State>()
2929
window.vscode = vscode
3030
31+
const selectedCommitHashes = ref<string[]>([])
32+
3133
// Handle messages from extension
3234
window.addEventListener('message', (event: { data: any }) => {
3335
const message = event.data
@@ -36,6 +38,9 @@ window.addEventListener('message', (event: { data: any }) => {
3638
case CHANNEL.HISTORY:
3739
commits.value = message.commits as CommitGraph
3840
break
41+
case CHANNEL.CLEAR_SELECTED:
42+
selectedCommitHashes.value = []
43+
break
3944
case 'error':
4045
error.value = message.message
4146
break
@@ -57,7 +62,7 @@ const transformedCommits = computed(() => {
5762
<input v-model="filter" type="text" placeholder="Search commits..." class="search-input">
5863
</div> -->
5964

60-
<CommitTable :commits="transformedCommits" :graph-data="commits?.operations || []" class="git-graph-container" />
65+
<CommitTable v-model="selectedCommitHashes" :commits="transformedCommits" :graph-data="commits?.operations || []" class="git-graph-container" />
6166

6267
<div v-if="error" class="error">
6368
{{ error }}

src/views/history/components/CommitTable/index.vue

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ const props = defineProps<{
1414
}>()
1515
1616
// Selected commit hash state
17-
const selectedCommitHashes = ref<string[]>([])
17+
const selectedCommitHashes = defineModel<string[]>({ default: [] })
18+
1819
const isDragging = ref(false)
19-
const dragStartIndex = ref<number | null>(null)
20-
const dragEndIndex = ref<number | null>(null)
20+
const selectionStart = ref<number | null>(null)
21+
const dragEndIndex = ref<number>(0)
2122
2223
const ITEMS_PER_PAGE = 45
2324
const currentPage = ref(1)
@@ -46,8 +47,9 @@ const visibleCommits = computed(() => {
4647
})
4748
4849
function handleCommitSelected(hash: string, index: number, event: MouseEvent) {
50+
isDragging.value = true
51+
4952
if (event.shiftKey && selectedCommitHashes.value.length > 0) {
50-
// Range selection with shift key
5153
const lastSelectedIndex = visibleCommits.value.findIndex(
5254
commit => commit.hash === selectedCommitHashes.value[selectedCommitHashes.value.length - 1],
5355
)
@@ -62,21 +64,66 @@ function handleCommitSelected(hash: string, index: number, event: MouseEvent) {
6264
selectedCommitHashes.value = [...new Set([...selectedCommitHashes.value, ...hashesToSelect])]
6365
}
6466
}
67+
else if (event.ctrlKey || event.metaKey) {
68+
if (selectedCommitHashes.value.includes(hash)) {
69+
selectedCommitHashes.value = selectedCommitHashes.value.filter(h => h !== hash)
70+
}
71+
else {
72+
selectedCommitHashes.value.push(hash)
73+
}
74+
}
6575
else {
6676
selectedCommitHashes.value = [hash]
6777
}
6878
69-
try {
70-
if (window.vscode) {
79+
handleMouseUp()
80+
}
81+
82+
function handleMouseDown(index: number, event: MouseEvent) {
83+
const target = event.target as HTMLElement
84+
if (target.closest('.copy-button')) {
85+
return
86+
}
87+
88+
if (!event.ctrlKey && !event.metaKey && !event.shiftKey) {
89+
isDragging.value = true
90+
selectionStart.value = index
91+
dragEndIndex.value = index
92+
selectedCommitHashes.value = [visibleCommits.value[index].hash]
93+
}
94+
}
95+
96+
function handleMouseOver(index: number) {
97+
if (isDragging.value && selectionStart.value !== null) {
98+
const startIdx = Math.min(selectionStart.value, index)
99+
const endIdx = Math.max(selectionStart.value, index)
100+
const hashesToSelect = visibleCommits.value
101+
.slice(startIdx, endIdx + 1)
102+
.map(commit => commit.hash)
103+
104+
selectedCommitHashes.value = hashesToSelect
105+
}
106+
}
107+
108+
function handleMouseUp() {
109+
const wasDragging = isDragging.value
110+
111+
isDragging.value = false
112+
113+
if (wasDragging) {
114+
try {
71115
window.vscode.postMessage({
72116
command: WEBVIEW_CHANNEL.SHOW_COMMIT_DETAILS,
73117
commitHashes: JSON.stringify(toRaw(selectedCommitHashes.value)),
74118
})
75119
}
120+
catch (error) {
121+
console.error('Error sending commit details:', error)
122+
}
76123
}
77-
catch (error) {
78-
console.error('Error sending commit details:', error)
79-
}
124+
125+
// 最后重置选择起点
126+
selectionStart.value = null
80127
}
81128
82129
onMounted(() => {
@@ -100,15 +147,20 @@ onUnmounted(() => {
100147
</script>
101148

102149
<template>
103-
<div class="git-graph" @mouseleave="isDragging = false">
150+
<div class="git-graph" @mouseleave="handleMouseUp" @mouseup="handleMouseUp">
104151
<ul class="commit-list">
105152
<ColumnHeader v-model="columnWidths" />
106153
<ListItem
107154
v-for="(commit, index) in visibleCommits" :key="commit.hash" :graph-data="graphData[index]"
108155
:prev-graph-data="index > 0 ? graphData[index - 1] : null"
109156
:next-graph-data="index < graphData.length - 1 ? graphData[index + 1] : null" :commit="commit"
110-
:column-widths="columnWidths" :is-selected="selectedCommitHashes.includes(commit.hash)"
111-
@select="(event) => handleCommitSelected(commit.hash, index, event)"
157+
:column-widths="columnWidths" :is-selected="selectedCommitHashes.includes(commit.hash)" :class="{
158+
'being-dragged': isDragging && selectionStart !== null
159+
&& ((index >= selectionStart && index <= dragEndIndex)
160+
|| (index <= selectionStart && index >= dragEndIndex)),
161+
}" @select="(event) => handleCommitSelected(commit.hash, index, event)"
162+
@mousedown="(event: MouseEvent) => handleMouseDown(index, event)"
163+
@mouseover="() => isDragging && handleMouseOver(index)"
112164
/>
113165
<li ref="loadingTriggerRef" class="loading-trigger">
114166
<div v-if="visibleCommits.length < commitData.length" class="loading-text">
@@ -124,6 +176,8 @@ onUnmounted(() => {
124176
width: 100%;
125177
overflow: auto;
126178
font-family: var(--vscode-editor-font-family);
179+
user-select: none;
180+
/* Prevent text selection during drag */
127181
}
128182
129183
.commit-list {
@@ -145,4 +199,8 @@ onUnmounted(() => {
145199
font-size: 12px;
146200
color: var(--vscode-descriptionForeground);
147201
}
202+
203+
.being-dragged {
204+
background-color: var(--vscode-list-activeSelectionBackground, rgba(0, 0, 0, 0.1));
205+
}
148206
</style>

src/views/webview.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { useGitService } from '@/git'
1515
import { useStorage } from '@/storage'
1616
import { CHANNEL, EXTENSION_SYMBOL, WEBVIEW_CHANNEL } from '@/constant'
1717

18-
import type { Commit, CommitGraph } from '@/git'
18+
import type { CommitGraph } from '@/git'
1919

2020
function getNonce() {
2121
let text = ''
@@ -154,9 +154,16 @@ export const useGitPanelView = createSingletonComposable(() => {
154154
}
155155
}
156156

157+
function clearSelection() {
158+
postMessage({ command: CHANNEL.CLEAR_SELECTED })
159+
gitChangesProvider.clearSelection()
160+
}
161+
157162
return {
158163
viewType: `${EXTENSION_SYMBOL}.history` as const,
159164
refreshHistory,
165+
postMessage,
160166
forceRefresh,
167+
clearSelection,
161168
}
162169
})

0 commit comments

Comments
 (0)