FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
vscode-pull-request-github/src/github/copilotPrWatcher.ts at main · gvilums/vscode-pull-request-github · GitHub
gvilums
/
vscode-pull-request-github
Public
forked from
microsoft/vscode-pull-request-github
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Pull requests
0
Actions
Projects
Security and quality
0
Insights
Additional navigation options
Code
Pull requests
Actions
Projects
Security and quality
Insights
Expand file tree
Breadcrumbs
vscode-pull-request-github
/
src
/
github
/
copilotPrWatcher.ts
Copy path
More file actions
More file actions
Latest commit
History
History
History
303 lines (268 loc) · 9.98 KB
Breadcrumbs
vscode-pull-request-github
/
src
/
github
/
copilotPrWatcher.ts
Copy path
File metadata and controls
303 lines (268 loc) · 9.98 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import
*
as
vscode
from
'vscode'
;
import
{
GithubItemStateEnum
}
from
'./interface'
;
import
{
PullRequestModel
}
from
'./pullRequestModel'
;
import
{
PullRequestOverviewPanel
}
from
'./pullRequestOverview'
;
import
{
RepositoriesManager
}
from
'./repositoriesManager'
;
import
{
debounce
}
from
'../common/async'
;
import
{
COPILOT_ACCOUNTS
}
from
'../common/comment'
;
import
{
COPILOT_LOGINS
,
copilotEventToStatus
,
CopilotPRStatus
}
from
'../common/copilot'
;
import
{
Disposable
}
from
'../common/lifecycle'
;
import
{
DEV_MODE
,
PR_SETTINGS_NAMESPACE
,
QUERIES
}
from
'../common/settingKeys'
;
import
{
PrsTreeModel
}
from
'../view/prsTreeModel'
;
export
function
isCopilotQuery
(
query
:
string
)
:
boolean
{
const
lowerQuery
=
query
.
toLowerCase
(
)
;
return
COPILOT_LOGINS
.
some
(
login
=>
lowerQuery
.
includes
(
`author:
${
login
.
toLowerCase
(
)
}
`
)
)
;
}
export
function
getCopilotQuery
(
)
:
string
|
undefined
{
const
queries
=
vscode
.
workspace
.
getConfiguration
(
PR_SETTINGS_NAMESPACE
)
.
get
<
{
label
:
string
;
query
:
string
}
[
]
>
(
QUERIES
,
[
]
)
;
return
queries
.
find
(
query
=>
isCopilotQuery
(
query
.
query
)
)
?.
query
;
}
export
interface
CodingAgentPRAndStatus
{
item
:
PullRequestModel
;
status
:
CopilotPRStatus
;
}
export
class
CopilotStateModel
extends
Disposable
{
public
static
ID
=
'CopilotStateModel'
;
private
_isInitialized
=
false
;
private
readonly
_states
:
Map
<
string
,
CodingAgentPRAndStatus
>
=
new
Map
(
)
;
private
readonly
_showNotification
:
Set
<
string
>
=
new
Set
(
)
;
private
readonly
_onDidChangeStates
=
this
.
_register
(
new
vscode
.
EventEmitter
<
void
>
(
)
)
;
readonly
onDidChangeCopilotStates
=
this
.
_onDidChangeStates
.
event
;
private
readonly
_onDidChangeNotifications
=
this
.
_register
(
new
vscode
.
EventEmitter
<
PullRequestModel
[
]
>
(
)
)
;
readonly
onDidChangeCopilotNotifications
=
this
.
_onDidChangeNotifications
.
event
;
makeKey
(
owner
:
string
,
repo
:
string
,
prNumber
?:
number
)
:
string
{
if
(
prNumber
===
undefined
)
{
return
`
${
owner
}
/
${
repo
}
`
;
}
return
`
${
owner
}
/
${
repo
}
#
${
prNumber
}
`
;
}
deleteKey
(
key
:
string
)
:
void
{
if
(
this
.
_states
.
has
(
key
)
)
{
const
item
=
this
.
_states
.
get
(
key
)
!
;
this
.
_states
.
delete
(
key
)
;
if
(
this
.
_showNotification
.
has
(
key
)
)
{
this
.
_showNotification
.
delete
(
key
)
;
this
.
_onDidChangeNotifications
.
fire
(
[
item
.
item
]
)
;
}
this
.
_onDidChangeStates
.
fire
(
)
;
}
}
set
(
statuses
:
CodingAgentPRAndStatus
[
]
)
:
void
{
const
changedModels
:
PullRequestModel
[
]
=
[
]
;
const
changedKeys
:
string
[
]
=
[
]
;
for
(
const
{
item
,
status
}
of
statuses
)
{
const
key
=
this
.
makeKey
(
item
.
remote
.
owner
,
item
.
remote
.
repositoryName
,
item
.
number
)
;
const
currentStatus
=
this
.
_states
.
get
(
key
)
;
if
(
currentStatus
?.
status
===
status
)
{
continue
;
}
this
.
_states
.
set
(
key
,
{
item
,
status
}
)
;
if
(
status
===
CopilotPRStatus
.
Started
)
{
continue
;
}
changedModels
.
push
(
item
)
;
changedKeys
.
push
(
key
)
;
}
if
(
changedModels
.
length
>
0
)
{
if
(
this
.
_isInitialized
)
{
changedKeys
.
forEach
(
key
=>
this
.
_showNotification
.
add
(
key
)
)
;
this
.
_onDidChangeNotifications
.
fire
(
changedModels
)
;
}
this
.
_onDidChangeStates
.
fire
(
)
;
}
}
get
(
owner
:
string
,
repo
:
string
,
prNumber
:
number
)
:
CopilotPRStatus
{
const
key
=
this
.
makeKey
(
owner
,
repo
,
prNumber
)
;
return
this
.
_states
.
get
(
key
)
?.
status
??
CopilotPRStatus
.
None
;
}
keys
(
)
:
string
[
]
{
return
Array
.
from
(
this
.
_states
.
keys
(
)
)
;
}
clearNotification
(
owner
:
string
,
repo
:
string
,
prNumber
:
number
)
:
void
{
const
key
=
this
.
makeKey
(
owner
,
repo
,
prNumber
)
;
if
(
this
.
_showNotification
.
has
(
key
)
)
{
this
.
_showNotification
.
delete
(
key
)
;
const
item
=
this
.
_states
.
get
(
key
)
?.
item
;
if
(
item
)
{
this
.
_onDidChangeNotifications
.
fire
(
[
item
]
)
;
}
}
}
clearAllNotifications
(
owner
?:
string
,
repo
?:
string
)
:
void
{
if
(
this
.
_showNotification
.
size
>
0
)
{
const
items
:
PullRequestModel
[
]
=
[
]
;
// If owner and repo are specified, only clear notifications for that repo
if
(
owner
&&
repo
)
{
const
keysToRemove
:
string
[
]
=
[
]
;
const
prefix
=
`
${
this
.
makeKey
(
owner
,
repo
)
}
#`
;
for
(
const
key
of
this
.
_showNotification
.
keys
(
)
)
{
if
(
key
.
startsWith
(
prefix
)
)
{
const
item
=
this
.
_states
.
get
(
key
)
?.
item
;
if
(
item
)
{
items
.
push
(
item
)
;
}
keysToRemove
.
push
(
key
)
;
}
}
keysToRemove
.
forEach
(
key
=>
this
.
_showNotification
.
delete
(
key
)
)
;
}
else
{
// Clear all notifications
for
(
const
key
of
this
.
_showNotification
.
keys
(
)
)
{
const
item
=
this
.
_states
.
get
(
key
)
?.
item
;
if
(
item
)
{
items
.
push
(
item
)
;
}
}
this
.
_showNotification
.
clear
(
)
;
}
if
(
items
.
length
>
0
)
{
this
.
_onDidChangeNotifications
.
fire
(
items
)
;
}
}
}
get
notifications
(
)
:
ReadonlySet
<
string
>
{
return
this
.
_showNotification
;
}
getNotificationsCount
(
owner
:
string
,
repo
:
string
)
:
number
{
let
total
=
0
;
const
partialKey
=
`
${
this
.
makeKey
(
owner
,
repo
)
}
#`
;
for
(
const
state
of
this
.
_showNotification
.
values
(
)
)
{
if
(
state
.
startsWith
(
partialKey
)
)
{
total
++
;
}
}
return
total
;
}
setInitialized
(
)
{
this
.
_isInitialized
=
true
;
}
get
isInitialized
(
)
:
boolean
{
return
this
.
_isInitialized
;
}
getCounts
(
owner
:
string
,
repo
:
string
)
:
{
total
:
number
;
inProgress
:
number
;
error
:
number
}
{
let
inProgressCount
=
0
;
let
errorCount
=
0
;
for
(
const
state
of
this
.
_states
.
values
(
)
)
{
if
(
state
.
item
.
remote
.
owner
!==
owner
||
state
.
item
.
remote
.
repositoryName
!==
repo
)
{
continue
;
}
if
(
state
.
status
===
CopilotPRStatus
.
Started
)
{
inProgressCount
++
;
}
else
if
(
state
.
status
===
CopilotPRStatus
.
Failed
)
{
errorCount
++
;
}
}
return
{
total
:
this
.
_states
.
size
,
inProgress
:
inProgressCount
,
error
:
errorCount
}
;
}
get
all
(
)
:
CodingAgentPRAndStatus
[
]
{
return
Array
.
from
(
this
.
_states
.
values
(
)
)
;
}
}
export
class
CopilotPRWatcher
extends
Disposable
{
private
readonly
_model
:
CopilotStateModel
;
constructor
(
private
readonly
_reposManager
:
RepositoriesManager
,
private
readonly
_prsTreeModel
:
PrsTreeModel
)
{
super
(
)
;
this
.
_model
=
_prsTreeModel
.
copilotStateModel
;
if
(
this
.
_reposManager
.
folderManagers
.
length
===
0
)
{
const
initDisposable
=
this
.
_reposManager
.
onDidChangeAnyGitHubRepository
(
(
)
=>
{
initDisposable
.
dispose
(
)
;
this
.
_initialize
(
)
;
}
)
;
}
else
{
this
.
_initialize
(
)
;
}
}
private
_initialize
(
)
{
this
.
_prsTreeModel
.
refreshCopilotStateChanges
(
true
)
;
this
.
_pollForChanges
(
)
;
const
updateFullState
=
debounce
(
(
)
=>
this
.
_prsTreeModel
.
refreshCopilotStateChanges
(
true
)
,
50
)
;
this
.
_register
(
this
.
_reposManager
.
onDidChangeAnyPullRequests
(
e
=>
{
if
(
e
.
some
(
pr
=>
COPILOT_ACCOUNTS
[
pr
.
model
.
author
.
login
]
)
)
{
if
(
!
this
.
_model
.
isInitialized
)
{
return
;
}
if
(
e
.
some
(
pr
=>
this
.
_model
.
get
(
pr
.
model
.
remote
.
owner
,
pr
.
model
.
remote
.
repositoryName
,
pr
.
model
.
number
)
===
CopilotPRStatus
.
None
)
)
{
// A PR we don't know about was updated
updateFullState
(
)
;
}
else
{
for
(
const
pr
of
e
)
{
if
(
pr
.
model
instanceof
PullRequestModel
)
{
this
.
_updateSingleState
(
pr
.
model
)
;
}
}
}
}
}
)
)
;
this
.
_register
(
PullRequestOverviewPanel
.
onVisible
(
e
=>
this
.
_model
.
clearNotification
(
e
.
remote
.
owner
,
e
.
remote
.
repositoryName
,
e
.
number
)
)
)
;
this
.
_register
(
vscode
.
workspace
.
onDidChangeConfiguration
(
e
=>
{
if
(
e
.
affectsConfiguration
(
`
${
PR_SETTINGS_NAMESPACE
}
.
${
QUERIES
}
`
)
)
{
this
.
_pollForChanges
(
)
;
}
}
)
)
;
this
.
_register
(
vscode
.
window
.
onDidChangeWindowState
(
e
=>
{
if
(
e
.
active
||
e
.
focused
)
{
// If we are becoming active/focused, and it's been more than the poll interval since the last poll, poll now
if
(
Date
.
now
(
)
-
this
.
_lastPollTime
>
this
.
_pollInterval
)
{
this
.
_pollForChanges
(
)
;
}
}
}
)
)
;
this
.
_register
(
{
dispose
:
(
)
=>
this
.
_pollTimeout
&&
clearTimeout
(
this
.
_pollTimeout
)
}
)
;
}
private
get
_pollInterval
(
)
:
number
{
if
(
vscode
.
window
.
state
.
active
||
vscode
.
window
.
state
.
focused
)
{
return
60
*
1000
*
2
;
// Poll every 2 minutes
}
return
60
*
1000
*
5
;
// Poll every 5 minutes
}
private
_pollTimeout
:
NodeJS
.
Timeout
|
undefined
;
private
_lastPollTime
=
0
;
private
async
_pollForChanges
(
)
:
Promise
<
void
>
{
// Skip polling if dev mode is enabled
const
devMode
=
vscode
.
workspace
.
getConfiguration
(
PR_SETTINGS_NAMESPACE
)
.
get
<
boolean
>
(
DEV_MODE
,
false
)
;
if
(
devMode
)
{
return
;
}
if
(
this
.
_pollTimeout
)
{
clearTimeout
(
this
.
_pollTimeout
)
;
this
.
_pollTimeout
=
undefined
;
}
this
.
_lastPollTime
=
Date
.
now
(
)
;
const
shouldContinue
=
await
this
.
_prsTreeModel
.
refreshCopilotStateChanges
(
true
)
;
if
(
shouldContinue
)
{
this
.
_pollTimeout
=
setTimeout
(
(
)
=>
{
this
.
_pollForChanges
(
)
;
}
,
this
.
_pollInterval
)
;
}
}
private
async
_updateSingleState
(
pr
:
PullRequestModel
)
:
Promise
<
void
>
{
const
changes
:
CodingAgentPRAndStatus
[
]
=
[
]
;
const
copilotEvents
=
await
pr
.
getCopilotTimelineEvents
(
false
,
!
this
.
_model
.
isInitialized
)
;
let
latestEvent
=
copilotEventToStatus
(
copilotEvents
[
copilotEvents
.
length
-
1
]
)
;
if
(
latestEvent
===
CopilotPRStatus
.
None
)
{
if
(
!
COPILOT_ACCOUNTS
[
pr
.
author
.
login
]
)
{
return
;
}
latestEvent
=
CopilotPRStatus
.
Started
;
}
if
(
pr
.
state
!==
GithubItemStateEnum
.
Open
)
{
// PR has been closed or merged, time to remove it.
const
key
=
this
.
_model
.
makeKey
(
pr
.
remote
.
owner
,
pr
.
remote
.
repositoryName
,
pr
.
number
)
;
this
.
_model
.
deleteKey
(
key
)
;
return
;
}
const
lastStatus
=
this
.
_model
.
get
(
pr
.
remote
.
owner
,
pr
.
remote
.
repositoryName
,
pr
.
number
)
??
CopilotPRStatus
.
None
;
if
(
latestEvent
!==
lastStatus
)
{
changes
.
push
(
{
item
:
pr
,
status
:
latestEvent
}
)
;
}
this
.
_model
.
set
(
changes
)
;
}
}
Back
|
FazBrowse Home
|
New Git URL