1
1
import { inject , injectable , named } from 'inversify' ;
2
+ import { CancellationToken } from 'monaco-editor' ;
2
3
import * as path from 'path' ;
3
4
import { Uri } from 'vscode' ;
4
- import { IFileSystem } from '../../common/platform/types' ;
5
+ import { IApplicationShell } from '../../common/application/types' ;
6
+ import { traceError } from '../../common/logger' ;
7
+ import { IFileSystem , TemporaryDirectory } from '../../common/platform/types' ;
8
+ import * as localize from '../../common/utils/localize' ;
9
+ import { sendTelemetryEvent } from '../../telemetry' ;
10
+ import { Telemetry } from '../constants' ;
5
11
import { ProgressReporter } from '../progress/progressReporter' ;
6
12
import { INotebookModel } from '../types' ;
13
+ import { ExportDependencyChecker } from './exportDependencyChecker' ;
14
+ import { ExportFileOpener } from './exportFileOpener' ;
7
15
import { ExportUtil } from './exportUtil' ;
8
16
import { ExportFormat , IExport , IExportManager , IExportManagerFilePicker } from './types' ;
9
17
@@ -16,10 +24,59 @@ export class ExportManager implements IExportManager {
16
24
@inject ( IFileSystem ) private readonly fileSystem : IFileSystem ,
17
25
@inject ( IExportManagerFilePicker ) private readonly filePicker : IExportManagerFilePicker ,
18
26
@inject ( ProgressReporter ) private readonly progressReporter : ProgressReporter ,
19
- @inject ( ExportUtil ) private readonly exportUtil : ExportUtil
27
+ @inject ( ExportUtil ) private readonly exportUtil : ExportUtil ,
28
+ @inject ( IApplicationShell ) private readonly applicationShell : IApplicationShell ,
29
+ @inject ( ExportFileOpener ) private readonly exportFileOpener : ExportFileOpener ,
30
+ @inject ( ExportDependencyChecker ) private exportDepedencyChecker : ExportDependencyChecker
20
31
) { }
21
32
22
- public async export (
33
+ public async export ( format : ExportFormat , model : INotebookModel , defaultFileName ?: string ) : Promise < undefined > {
34
+ let target ;
35
+ try {
36
+ await this . exportDepedencyChecker . checkDependencies ( format ) ;
37
+ target = await this . getTargetFile ( format , model , defaultFileName ) ;
38
+ if ( ! target ) {
39
+ return ;
40
+ }
41
+ await this . performExport ( format , model , target ) ;
42
+ } catch ( e ) {
43
+ let msg = e ;
44
+ traceError ( 'Export failed' , e ) ;
45
+ sendTelemetryEvent ( Telemetry . ExportNotebookAsFailed , undefined , { format : format } ) ;
46
+
47
+ if ( format === ExportFormat . pdf ) {
48
+ msg = localize . DataScience . exportToPDFDependencyMessage ( ) ;
49
+ }
50
+
51
+ this . showExportFailed ( msg ) ;
52
+ }
53
+ }
54
+
55
+ private async performExport ( format : ExportFormat , model : INotebookModel , target : Uri ) {
56
+ /* Need to make a temp directory here, instead of just a temp file. This is because
57
+ we need to store the contents of the notebook in a file that is named the same
58
+ as what we want the title of the exported file to be. To ensure this file path will be unique
59
+ we store it in a temp directory. The name of the file matters because when
60
+ exporting to certain formats the filename is used within the exported document as the title. */
61
+ const tempDir = await this . exportUtil . generateTempDir ( ) ;
62
+ const source = await this . makeSourceFile ( target , model , tempDir ) ;
63
+
64
+ const reporter = this . progressReporter . createProgressIndicator ( `Exporting to ${ format } ` , true ) ;
65
+ try {
66
+ await this . exportToFormat ( source , target , format , reporter . token ) ;
67
+ } finally {
68
+ tempDir . dispose ( ) ;
69
+ reporter . dispose ( ) ;
70
+ }
71
+
72
+ if ( reporter . token . isCancellationRequested ) {
73
+ sendTelemetryEvent ( Telemetry . ExportNotebookAs , undefined , { format : format , cancelled : true } ) ;
74
+ return ;
75
+ }
76
+ await this . exportFileOpener . openFile ( format , target ) ;
77
+ }
78
+
79
+ private async getTargetFile (
23
80
format : ExportFormat ,
24
81
model : INotebookModel ,
25
82
defaultFileName ?: string
@@ -28,56 +85,47 @@ export class ExportManager implements IExportManager {
28
85
29
86
if ( format !== ExportFormat . python ) {
30
87
target = await this . filePicker . getExportFileLocation ( format , model . file , defaultFileName ) ;
31
- if ( ! target ) {
32
- return ;
33
- }
34
88
} else {
35
89
target = Uri . file ( ( await this . fileSystem . createTemporaryFile ( '.py' ) ) . filePath ) ;
36
90
}
37
91
38
- // Need to make a temp directory here, instead of just a temp file. This is because
39
- // we need to store the contents of the notebook in a file that is named the same
40
- // as what we want the title of the exported file to be. To ensure this file path will be unique
41
- // we store it in a temp directory. The name of the file matters because when
42
- // exporting to certain formats the filename is used within the exported document as the title.
92
+ return target ;
93
+ }
94
+
95
+ private async makeSourceFile ( target : Uri , model : INotebookModel , tempDir : TemporaryDirectory ) : Promise < Uri > {
96
+ // Creates a temporary file with the same base name as the target file
43
97
const fileName = path . basename ( target . fsPath , path . extname ( target . fsPath ) ) ;
44
- const tempDir = await this . exportUtil . generateTempDir ( ) ;
45
98
const sourceFilePath = await this . exportUtil . makeFileInDirectory ( model , `${ fileName } .ipynb` , tempDir . path ) ;
46
- const source = Uri . file ( sourceFilePath ) ;
99
+ return Uri . file ( sourceFilePath ) ;
100
+ }
47
101
102
+ private showExportFailed ( msg : string ) {
103
+ // tslint:disable-next-line: messages-must-be-localized
104
+ this . applicationShell . showErrorMessage ( `${ localize . DataScience . failedExportMessage ( ) } ${ msg } ` ) . then ( ) ;
105
+ }
106
+
107
+ private async exportToFormat ( source : Uri , target : Uri , format : ExportFormat , cancelToken : CancellationToken ) {
48
108
if ( format === ExportFormat . pdf ) {
49
109
// When exporting to PDF we need to remove any SVG output. This is due to an error
50
110
// with nbconvert and a dependency of its called InkScape.
51
111
await this . exportUtil . removeSvgs ( source ) ;
52
112
}
53
113
54
- const reporter = this . progressReporter . createProgressIndicator ( `Exporting to ${ format } ` , true ) ;
55
- try {
56
- switch ( format ) {
57
- case ExportFormat . python :
58
- await this . exportToPython . export ( source , target , reporter . token ) ;
59
- break ;
114
+ switch ( format ) {
115
+ case ExportFormat . python :
116
+ await this . exportToPython . export ( source , target , cancelToken ) ;
117
+ break ;
60
118
61
- case ExportFormat . pdf :
62
- await this . exportToPDF . export ( source , target , reporter . token ) ;
63
- break ;
119
+ case ExportFormat . pdf :
120
+ await this . exportToPDF . export ( source , target , cancelToken ) ;
121
+ break ;
64
122
65
- case ExportFormat . html :
66
- await this . exportToHTML . export ( source , target , reporter . token ) ;
67
- break ;
123
+ case ExportFormat . html :
124
+ await this . exportToHTML . export ( source , target , cancelToken ) ;
125
+ break ;
68
126
69
- default :
70
- break ;
71
- }
72
- } finally {
73
- tempDir . dispose ( ) ;
74
- reporter . dispose ( ) ;
127
+ default :
128
+ break ;
75
129
}
76
-
77
- if ( reporter . token . isCancellationRequested ) {
78
- return ;
79
- }
80
-
81
- return target ;
82
130
}
83
131
}
0 commit comments