66import { tmpdir } from 'os' ;
77import { localize } from 'vs/nls' ;
88import { TextFileService } from 'vs/workbench/services/textfile/common/textFileService' ;
9- import { ITextFileService , ITextFileContent } from 'vs/workbench/services/textfile/common/textfiles' ;
9+ import { ITextFileService , ITextFileStreamContent , ITextFileContent } from 'vs/workbench/services/textfile/common/textfiles' ;
1010import { registerSingleton } from 'vs/platform/instantiation/common/extensions' ;
1111import { URI } from 'vs/base/common/uri' ;
12- import { ITextSnapshot , IWriteTextFileOptions , IFileStatWithMetadata , IResourceEncoding , IReadTextFileOptions , stringToSnapshot , ICreateFileOptions , FileOperationError , FileOperationResult , IResourceEncodings } from 'vs/platform/files/common/files' ;
12+ import { ITextSnapshot , IWriteTextFileOptions , IFileStatWithMetadata , IResourceEncoding , IReadTextFileOptions , stringToSnapshot , ICreateFileOptions , FileOperationError , FileOperationResult , IResourceEncodings , IFileStreamContent } from 'vs/platform/files/common/files' ;
1313import { Schemas } from 'vs/base/common/network' ;
1414import { exists , stat , chmod , rimraf } from 'vs/base/node/pfs' ;
1515import { join , dirname } from 'vs/base/common/path' ;
1616import { isMacintosh , isLinux } from 'vs/base/common/platform' ;
1717import product from 'vs/platform/product/node/product' ;
1818import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration' ;
1919import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace' ;
20- import { UTF8 , UTF8_with_bom , UTF16be , UTF16le , encodingExists , IDetectedEncodingResult , detectEncodingByBOM , encodeStream , UTF8_BOM , UTF16be_BOM , UTF16le_BOM , toDecodeStream , IDecodeStreamOptions } from 'vs/base/node/encoding' ;
20+ import { UTF8 , UTF8_with_bom , UTF16be , UTF16le , encodingExists , IDetectedEncodingResult , detectEncodingByBOM , encodeStream , UTF8_BOM , UTF16be_BOM , UTF16le_BOM , toDecodeStream , IDecodeStreamResult } from 'vs/base/node/encoding' ;
2121import { WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces' ;
2222import { joinPath , extname , isEqualOrParent } from 'vs/base/common/resources' ;
2323import { Disposable } from 'vs/base/common/lifecycle' ;
@@ -26,6 +26,7 @@ import { VSBufferReadable, VSBuffer, VSBufferReadableStream } from 'vs/base/comm
2626import { Readable } from 'stream' ;
2727import { isUndefinedOrNull } from 'vs/base/common/types' ;
2828import { createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel' ;
29+ import { MAX_FILE_SIZE , MAX_HEAP_SIZE } from 'vs/platform/files/node/fileConstants' ;
2930
3031export class NodeTextFileService extends TextFileService {
3132
@@ -39,28 +40,72 @@ export class NodeTextFileService extends TextFileService {
3940 }
4041
4142 async read ( resource : URI , options ?: IReadTextFileOptions ) : Promise < ITextFileContent > {
42- const stream = await this . fileService . readFileStream ( resource , options ) ;
43+ const [ bufferStream , decoder ] = await this . doRead ( resource , options ) ;
4344
44- const readable = this . streamToNodeReadable ( stream . value ) ;
45+ return {
46+ ...bufferStream ,
47+ encoding : decoder . detected . encoding || UTF8 ,
48+ value : await this . nodeReadableToString ( decoder . stream )
49+ } ;
50+ }
4551
46- const decodeStreamOpts : IDecodeStreamOptions = {
47- guessEncoding : options && options . autoGuessEncoding ,
48- overwriteEncoding : detected => {
49- return this . encoding . getReadEncoding ( resource , options , { encoding : detected , seemsBinary : false } ) ;
50- }
52+ async readStream ( resource : URI , options ?: IReadTextFileOptions ) : Promise < ITextFileStreamContent > {
53+ const [ bufferStream , decoder ] = await this . doRead ( resource , options ) ;
54+
55+ return {
56+ ...bufferStream ,
57+ encoding : decoder . detected . encoding || UTF8 ,
58+ value : await createTextBufferFactoryFromStream ( decoder . stream )
5159 } ;
60+ }
5261
53- const result = await toDecodeStream ( readable , decodeStreamOpts ) ;
62+ private async doRead ( resource : URI , options ?: IReadTextFileOptions ) : Promise < [ IFileStreamContent , IDecodeStreamResult ] > {
5463
55- if ( options && options . acceptTextOnly && result . detected . seemsBinary ) {
64+ // ensure limits
65+ options = this . ensureLimits ( options ) ;
66+
67+ // read stream raw
68+ const bufferStream = await this . fileService . readFileStream ( resource , options ) ;
69+
70+ // read through encoding library
71+ const decoder = await toDecodeStream ( this . streamToNodeReadable ( bufferStream . value ) , {
72+ guessEncoding : options && options . autoGuessEncoding ,
73+ overwriteEncoding : detected => this . encoding . getReadEncoding ( resource , options , { encoding : detected , seemsBinary : false } )
74+ } ) ;
75+
76+ // validate binary
77+ if ( options && options . acceptTextOnly && decoder . detected . seemsBinary ) {
5678 throw new FileOperationError ( localize ( 'fileBinaryError' , "File seems to be binary and cannot be opened as text" ) , FileOperationResult . FILE_IS_BINARY , options ) ;
5779 }
5880
59- return {
60- ...stream ,
61- encoding : result . detected . encoding || UTF8 ,
62- value : await createTextBufferFactoryFromStream ( result . stream )
63- } ;
81+ return [ bufferStream , decoder ] ;
82+ }
83+
84+ private ensureLimits ( options ?: IReadTextFileOptions ) : IReadTextFileOptions {
85+ let ensuredOptions : IReadTextFileOptions ;
86+ if ( ! options ) {
87+ ensuredOptions = Object . create ( null ) ;
88+ } else {
89+ ensuredOptions = options ;
90+ }
91+
92+ let ensuredLimits : { size ?: number ; memory ?: number ; } ;
93+ if ( ! ensuredOptions . limits ) {
94+ ensuredLimits = Object . create ( null ) ;
95+ ensuredOptions . limits = ensuredLimits ;
96+ } else {
97+ ensuredLimits = ensuredOptions . limits ;
98+ }
99+
100+ if ( typeof ensuredLimits . size !== 'number' ) {
101+ ensuredLimits . size = MAX_FILE_SIZE ;
102+ }
103+
104+ if ( typeof ensuredLimits . memory !== 'number' ) {
105+ ensuredLimits . memory = Math . max ( typeof this . environmentService . args [ 'max-memory' ] === 'string' ? parseInt ( this . environmentService . args [ 'max-memory' ] ) * 1024 * 1024 || 0 : 0 , MAX_HEAP_SIZE ) ;
106+ }
107+
108+ return ensuredOptions ;
64109 }
65110
66111 private streamToNodeReadable ( stream : VSBufferReadableStream ) : Readable {
@@ -107,6 +152,16 @@ export class NodeTextFileService extends TextFileService {
107152 } ;
108153 }
109154
155+ private nodeReadableToString ( stream : NodeJS . ReadableStream ) : Promise < string > {
156+ return new Promise ( ( resolve , reject ) => {
157+ let result = '' ;
158+
159+ stream . on ( 'data' , chunk => result += chunk ) ;
160+ stream . on ( 'error' , reject ) ;
161+ stream . on ( 'end' , ( ) => resolve ( result ) ) ;
162+ } ) ;
163+ }
164+
110165 protected async doCreate ( resource : URI , value ?: string , options ?: ICreateFileOptions ) : Promise < IFileStatWithMetadata > {
111166
112167 // check for encoding
0 commit comments