@@ -8,35 +8,42 @@ import {
8
8
showDialog ,
9
9
showErrorMessage
10
10
} from '@jupyterlab/apputils' ;
11
- import { CodeEditor } from '@jupyterlab/codeeditor' ;
11
+ import {
12
+ CodeEditor ,
13
+ CodeEditorWrapper ,
14
+ IEditorFactoryService
15
+ } from '@jupyterlab/codeeditor' ;
16
+ import { IEditorLanguageRegistry } from '@jupyterlab/codemirror' ;
12
17
import { PathExt , URLExt } from '@jupyterlab/coreutils' ;
13
18
import { FileBrowser , FileBrowserModel } from '@jupyterlab/filebrowser' ;
14
19
import { Contents } from '@jupyterlab/services' ;
15
20
import { ISettingRegistry } from '@jupyterlab/settingregistry' ;
16
21
import { ITerminal } from '@jupyterlab/terminal' ;
17
22
import { ITranslator , TranslationBundle } from '@jupyterlab/translation' ;
18
23
import {
19
- closeIcon ,
20
24
ContextMenuSvg ,
21
25
Toolbar ,
22
- ToolbarButton
26
+ ToolbarButton ,
27
+ closeIcon ,
28
+ saveIcon
23
29
} from '@jupyterlab/ui-components' ;
24
- import { ArrayExt } from '@lumino/algorithm' ;
30
+ import { ArrayExt , find } from '@lumino/algorithm' ;
25
31
import { CommandRegistry } from '@lumino/commands' ;
26
32
import { PromiseDelegate } from '@lumino/coreutils' ;
27
33
import { Message } from '@lumino/messaging' ;
28
34
import { ContextMenu , DockPanel , Menu , Panel , Widget } from '@lumino/widgets' ;
29
35
import * as React from 'react' ;
30
36
import { CancelledError } from './cancelledError' ;
31
37
import { BranchPicker } from './components/BranchPicker' ;
38
+ import { CONTEXT_COMMANDS } from './components/FileList' ;
39
+ import { ManageRemoteDialogue } from './components/ManageRemoteDialogue' ;
32
40
import { NewTagDialogBox } from './components/NewTagDialog' ;
33
- import { DiffModel } from './components/diff/model' ;
34
41
import { createPlainTextDiff } from './components/diff/PlainTextDiff' ;
35
42
import { PreviewMainAreaWidget } from './components/diff/PreviewMainAreaWidget' ;
36
- import { CONTEXT_COMMANDS } from './components/FileList' ;
37
- import { ManageRemoteDialogue } from './components/ManageRemoteDialogue' ;
43
+ import { DiffModel } from './components/diff/model' ;
38
44
import { AUTH_ERROR_MESSAGES , requestAPI } from './git' ;
39
- import { getDiffProvider , GitExtension } from './model' ;
45
+ import { GitExtension , getDiffProvider } from './model' ;
46
+ import { showDetails , showError } from './notifications' ;
40
47
import {
41
48
addIcon ,
42
49
diffIcon ,
@@ -50,10 +57,8 @@ import {
50
57
import { CommandIDs , ContextCommandIDs , Git , IGitExtension } from './tokens' ;
51
58
import { AdvancedPushForm } from './widgets/AdvancedPushForm' ;
52
59
import { GitCredentialsForm } from './widgets/CredentialsBox' ;
53
- import { discardAllChanges } from './widgets/discardAllChanges' ;
54
60
import { CheckboxForm } from './widgets/GitResetToRemoteForm' ;
55
- import { IEditorLanguageRegistry } from '@jupyterlab/codemirror' ;
56
- import { showDetails , showError } from './notifications' ;
61
+ import { discardAllChanges } from './widgets/discardAllChanges' ;
57
62
58
63
export interface IGitCloneArgs {
59
64
/**
@@ -126,7 +131,7 @@ function pluralizedContextLabel(singular: string, plural: string) {
126
131
export function addCommands (
127
132
app : JupyterFrontEnd ,
128
133
gitModel : GitExtension ,
129
- editorFactory : CodeEditor . Factory ,
134
+ editorFactory : IEditorFactoryService ,
130
135
languageRegistry : IEditorLanguageRegistry ,
131
136
fileBrowserModel : FileBrowserModel ,
132
137
settings : ISettingRegistry . ISettings ,
@@ -314,13 +319,129 @@ export function addCommands(
314
319
}
315
320
} ) ;
316
321
322
+ async function showGitignore ( error : any ) {
323
+ const model = new CodeEditor . Model ( { } ) ;
324
+ const repoPath = gitModel . getRelativeFilePath ( ) ;
325
+ const id = repoPath + '/.git-ignore' ;
326
+ const contentData = await gitModel . readGitIgnore ( ) ;
327
+
328
+ const gitIgnoreWidget = find (
329
+ shell . widgets ( ) ,
330
+ shellWidget => shellWidget . id === id
331
+ ) ;
332
+ if ( gitIgnoreWidget ) {
333
+ shell . activateById ( id ) ;
334
+ return ;
335
+ }
336
+ model . sharedModel . setSource ( contentData ? contentData : '' ) ;
337
+ const editor = new CodeEditorWrapper ( {
338
+ factory : editorFactory . newDocumentEditor . bind ( editorFactory ) ,
339
+ model : model
340
+ } ) ;
341
+ const modelChangedSignal = model . sharedModel . changed ;
342
+ editor . disposed . connect ( ( ) => {
343
+ model . dispose ( ) ;
344
+ } ) ;
345
+ const preview = new MainAreaWidget ( {
346
+ content : editor
347
+ } ) ;
348
+
349
+ preview . title . label = '.gitignore' ;
350
+ preview . id = id ;
351
+ preview . title . icon = gitIcon ;
352
+ preview . title . closable = true ;
353
+ preview . title . caption = repoPath + '/.gitignore' ;
354
+ const saveButton = new ToolbarButton ( {
355
+ icon : saveIcon ,
356
+ onClick : async ( ) => {
357
+ if ( saved ) {
358
+ return ;
359
+ }
360
+ const newContent = model . sharedModel . getSource ( ) ;
361
+ try {
362
+ await gitModel . writeGitIgnore ( newContent ) ;
363
+ preview . title . className = '' ;
364
+ saved = true ;
365
+ } catch ( error ) {
366
+ console . log ( 'Could not save .gitignore' ) ;
367
+ }
368
+ } ,
369
+ tooltip : trans . __ ( 'Saves .gitignore' )
370
+ } ) ;
371
+ let saved = true ;
372
+ preview . toolbar . addItem ( 'save' , saveButton ) ;
373
+ shell . add ( preview ) ;
374
+ modelChangedSignal . connect ( ( ) => {
375
+ if ( saved ) {
376
+ saved = false ;
377
+ preview . title . className = 'not-saved' ;
378
+ }
379
+ } ) ;
380
+ }
381
+
382
+ /* Helper: Show gitignore hidden file */
383
+ async function showGitignoreHiddenFile ( error : any , hidePrompt : boolean ) {
384
+ if ( hidePrompt ) {
385
+ return showGitignore ( error ) ;
386
+ }
387
+ const result = await showDialog ( {
388
+ title : trans . __ ( 'Warning: The .gitignore file is a hidden file.' ) ,
389
+ body : (
390
+ < div >
391
+ { trans . __ (
392
+ 'Hidden files by default cannot be accessed with the regular code editor. In order to open the .gitignore file you must:'
393
+ ) }
394
+ < ol >
395
+ < li >
396
+ { trans . __ (
397
+ 'Print the command below to create a jupyter_server_config.py file with defaults commented out. If you already have the file located in .jupyter, skip this step.'
398
+ ) }
399
+ < div style = { { padding : '0.5rem' } } >
400
+ { 'jupyter server --generate-config' }
401
+ </ div >
402
+ </ li >
403
+ < li >
404
+ { trans . __ (
405
+ 'Open jupyter_server_config.py, uncomment out the following line and set it to True:'
406
+ ) }
407
+ < div style = { { padding : '0.5rem' } } >
408
+ { 'c.ContentsManager.allow_hidden = False' }
409
+ </ div >
410
+ </ li >
411
+ </ ol >
412
+ </ div >
413
+ ) ,
414
+ buttons : [
415
+ Dialog . cancelButton ( { label : trans . __ ( 'Cancel' ) } ) ,
416
+ Dialog . okButton ( { label : trans . __ ( 'Show .gitignore file anyways' ) } )
417
+ ] ,
418
+ checkbox : {
419
+ label : trans . __ ( 'Do not show this warning again' ) ,
420
+ checked : false
421
+ }
422
+ } ) ;
423
+ if ( result . button . accept ) {
424
+ settings . set ( 'hideHiddenFileWarning' , result . isChecked ) ;
425
+ showGitignore ( error ) ;
426
+ }
427
+ }
428
+
317
429
/** Add git open gitignore command */
318
430
commands . addCommand ( CommandIDs . gitOpenGitignore , {
319
431
label : trans . __ ( 'Open .gitignore' ) ,
320
432
caption : trans . __ ( 'Open .gitignore' ) ,
321
433
isEnabled : ( ) => gitModel . pathRepository !== null ,
322
434
execute : async ( ) => {
323
- await gitModel . ensureGitignore ( ) ;
435
+ try {
436
+ await gitModel . ensureGitignore ( ) ;
437
+ } catch ( error : any ) {
438
+ if ( error ?. name === 'hiddenFile' ) {
439
+ await showGitignoreHiddenFile (
440
+ error ,
441
+ settings . composite [ 'hideHiddenFileWarning' ] as boolean
442
+ ) ;
443
+ }
444
+ }
324
445
}
325
446
} ) ;
326
447
@@ -586,7 +707,7 @@ export function addCommands(
586
707
( options =>
587
708
createPlainTextDiff ( {
588
709
...options ,
589
- editorFactory,
710
+ editorFactory : editorFactory . newInlineEditor . bind ( editorFactory ) ,
590
711
languageRegistry
591
712
} ) ) ) ;
592
713
@@ -1523,7 +1644,16 @@ export function addCommands(
1523
1644
const { files } = args as any as CommandArguments . IGitContextAction ;
1524
1645
for ( const file of files ) {
1525
1646
if ( file ) {
1526
- await gitModel . ignore ( file . to , false ) ;
1647
+ try {
1648
+ await gitModel . ignore ( file . to , false ) ;
1649
+ } catch ( error : any ) {
1650
+ if ( error ?. name === 'hiddenFile' ) {
1651
+ await showGitignoreHiddenFile (
1652
+ error ,
1653
+ settings . composite [ 'hideHiddenFileWarning' ] as boolean
1654
+ ) ;
1655
+ }
1656
+ }
1527
1657
}
1528
1658
}
1529
1659
}
0 commit comments