@@ -7,14 +7,19 @@ import {
7
7
showErrorMessage
8
8
} from '@jupyterlab/apputils' ;
9
9
import { FileBrowser } from '@jupyterlab/filebrowser' ;
10
+ import { IRenderMimeRegistry } from '@jupyterlab/rendermime' ;
10
11
import { ISettingRegistry } from '@jupyterlab/settingregistry' ;
11
12
import { ITerminal } from '@jupyterlab/terminal' ;
12
13
import { CommandRegistry } from '@lumino/commands' ;
13
14
import { Menu } from '@lumino/widgets' ;
14
- import { IGitExtension } from './tokens' ;
15
+ import { Git } from './tokens' ;
15
16
import { GitCredentialsForm } from './widgets/CredentialsBox' ;
16
17
import { doGitClone } from './widgets/gitClone' ;
17
18
import { GitPullPushDialog , Operation } from './widgets/gitPushPull' ;
19
+ import { openListedFile , discardChanges } from './utils' ;
20
+ import { GitExtension } from './model' ;
21
+ import { openDiffView } from './components/diff/DiffWidget' ;
22
+ import { PathExt } from '@jupyterlab/coreutils' ;
18
23
19
24
const RESOURCES = [
20
25
{
@@ -42,16 +47,27 @@ export namespace CommandIDs {
42
47
export const gitOpenGitignore = 'git:open-gitignore' ;
43
48
export const gitPush = 'git:push' ;
44
49
export const gitPull = 'git:pull' ;
50
+ // Context menu commands
51
+ export const gitFileDiffIndex = 'git:context-diffIndex' ;
52
+ export const gitFileDiffWorking = 'git:context-diffWorking' ;
53
+ export const gitFileDiscard = 'git:context-discard' ;
54
+ export const gitFileOpen = 'git:context-open' ;
55
+ export const gitFileUnstage = 'git:context-unstage' ;
56
+ export const gitFileStage = 'git:context-stage' ;
57
+ export const gitFileTrack = 'git:context-track' ;
58
+ export const gitIgnore = 'git:context-ignore' ;
59
+ export const gitIgnoreExtension = 'git:context-ignoreExtension' ;
45
60
}
46
61
47
62
/**
48
63
* Add the commands for the git extension.
49
64
*/
50
65
export function addCommands (
51
66
app : JupyterFrontEnd ,
52
- model : IGitExtension ,
67
+ model : GitExtension ,
53
68
fileBrowser : FileBrowser ,
54
- settings : ISettingRegistry . ISettings
69
+ settings : ISettingRegistry . ISettings ,
70
+ renderMime : IRenderMimeRegistry
55
71
) {
56
72
const { commands, shell } = app ;
57
73
@@ -232,6 +248,144 @@ export function addCommands(
232
248
) ;
233
249
}
234
250
} ) ;
251
+
252
+ /* Context menu commands */
253
+ commands . addCommand ( CommandIDs . gitFileOpen , {
254
+ label : 'Open' ,
255
+ caption : 'Open selected file' ,
256
+ execute : async args => {
257
+ const selectedFile : Git . IStatusFileResult = args as any ;
258
+ await openListedFile ( selectedFile , model ) ;
259
+ } ,
260
+ isEnabled : args => args && args . to !== undefined
261
+ } ) ;
262
+
263
+ commands . addCommand ( CommandIDs . gitFileDiffWorking , {
264
+ label : 'Diff' ,
265
+ caption : 'Diff selected file' ,
266
+ execute : async args => {
267
+ const selectedFile : Git . IStatusFileResult = args as any ;
268
+ await openDiffView (
269
+ selectedFile . to ,
270
+ model ,
271
+ {
272
+ currentRef : { specialRef : 'WORKING' } ,
273
+ previousRef : { gitRef : 'HEAD' }
274
+ } ,
275
+ renderMime ,
276
+ ! selectedFile . is_binary
277
+ ) ;
278
+ } ,
279
+ isEnabled : args => args && args . to !== undefined
280
+ } ) ;
281
+
282
+ commands . addCommand ( CommandIDs . gitFileDiffIndex , {
283
+ label : 'Diff' ,
284
+ caption : 'Diff selected file' ,
285
+ execute : async args => {
286
+ const selectedFile : Git . IStatusFileResult = args as any ;
287
+ await openDiffView (
288
+ selectedFile . to ,
289
+ model ,
290
+ {
291
+ currentRef : { specialRef : 'INDEX' } ,
292
+ previousRef : { gitRef : 'HEAD' }
293
+ } ,
294
+ renderMime ,
295
+ ! selectedFile . is_binary
296
+ ) ;
297
+ } ,
298
+ isEnabled : args => args && args . to !== undefined
299
+ } ) ;
300
+
301
+ commands . addCommand ( CommandIDs . gitFileStage , {
302
+ label : 'Stage' ,
303
+ caption : 'Stage the changes of selected file' ,
304
+ execute : async args => {
305
+ const selectedFile : Git . IStatusFile = args as any ;
306
+ await model . add ( selectedFile . to ) ;
307
+ } ,
308
+ isEnabled : args => args && args . to !== undefined
309
+ } ) ;
310
+
311
+ commands . addCommand ( CommandIDs . gitFileTrack , {
312
+ label : 'Track' ,
313
+ caption : 'Start tracking selected file' ,
314
+ execute : async args => {
315
+ const selectedFile : Git . IStatusFile = args as any ;
316
+ await model . add ( selectedFile . to ) ;
317
+ } ,
318
+ isEnabled : args => args && args . to !== undefined
319
+ } ) ;
320
+
321
+ commands . addCommand ( CommandIDs . gitFileUnstage , {
322
+ label : 'Unstage' ,
323
+ caption : 'Unstage the changes of selected file' ,
324
+ execute : async args => {
325
+ const selectedFile : Git . IStatusFile = args as any ;
326
+ if ( selectedFile . x !== 'D' ) {
327
+ await model . reset ( selectedFile . to ) ;
328
+ }
329
+ } ,
330
+ isEnabled : args => args && args . to !== undefined
331
+ } ) ;
332
+
333
+ commands . addCommand ( CommandIDs . gitFileDiscard , {
334
+ label : 'Discard' ,
335
+ caption : 'Discard recent changes of selected file' ,
336
+ execute : async args => {
337
+ const selectedFile : Git . IStatusFile = args as any ;
338
+ await discardChanges ( selectedFile , model ) ;
339
+ } ,
340
+ isEnabled : args => args && args . to !== undefined
341
+ } ) ;
342
+
343
+ commands . addCommand ( CommandIDs . gitIgnore , {
344
+ label : ( ) => 'Ignore this file (add to .gitignore)' ,
345
+ caption : ( ) => 'Ignore this file (add to .gitignore)' ,
346
+ execute : async args => {
347
+ const selectedFile : Git . IStatusFile = args as any ;
348
+ if ( selectedFile ) {
349
+ await model . ignore ( selectedFile . to , false ) ;
350
+ }
351
+ } ,
352
+ isEnabled : args => args && args . to !== undefined
353
+ } ) ;
354
+
355
+ commands . addCommand ( CommandIDs . gitIgnoreExtension , {
356
+ label : args => {
357
+ const selectedFile : Git . IStatusFile = args as any ;
358
+ return `Ignore ${ PathExt . extname (
359
+ selectedFile . to
360
+ ) } extension (add to .gitignore)`;
361
+ } ,
362
+ caption : 'Ignore this file extension (add to .gitignore)' ,
363
+ execute : async args => {
364
+ const selectedFile : Git . IStatusFile = args as any ;
365
+ if ( selectedFile ) {
366
+ const extension = PathExt . extname ( selectedFile . to ) ;
367
+ if ( extension . length > 0 ) {
368
+ const result = await showDialog ( {
369
+ title : 'Ignore file extension' ,
370
+ body : `Are you sure you want to ignore all ${ extension } files within this git repository?` ,
371
+ buttons : [
372
+ Dialog . cancelButton ( ) ,
373
+ Dialog . okButton ( { label : 'Ignore' } )
374
+ ]
375
+ } ) ;
376
+ if ( result . button . label === 'Ignore' ) {
377
+ await model . ignore ( selectedFile . to , true ) ;
378
+ }
379
+ }
380
+ }
381
+ } ,
382
+ isEnabled : args => args && args . to !== undefined ,
383
+ isVisible : args => {
384
+ const selectedFile : Git . IStatusFile = args as any ;
385
+ const extension = PathExt . extname ( selectedFile . to ) ;
386
+ return extension . length > 0 ;
387
+ }
388
+ } ) ;
235
389
}
236
390
237
391
/**
@@ -295,7 +449,7 @@ namespace Private {
295
449
* @returns Promise for displaying a dialog
296
450
*/
297
451
export async function showGitOperationDialog (
298
- model : IGitExtension ,
452
+ model : GitExtension ,
299
453
operation : Operation
300
454
) : Promise < void > {
301
455
const title = `Git ${ operation } ` ;
0 commit comments