1515 */
1616
1717import path from 'node:path' ;
18+ import * as fs from 'node:fs' ;
1819import * as fsUtils from '../utils/fs-utils' ;
1920import * as vscodeUtils from '../utils/vscode-utils' ;
2021import { ITextParser } from './text-parser' ;
@@ -40,6 +41,12 @@ export enum ETextFileResult {
4041 NotExists = 3
4142}
4243
44+ type ExternalFileStamp = {
45+ path : string ;
46+ mtimeMs : number ;
47+ size : number ;
48+ } ;
49+
4350/**
4451 * Represents a text file with parsing, rendering, and error handling capabilities.
4552 * Provides methods and properties for managing file content, file metadata, and associated parsers/renderers.
@@ -170,6 +177,16 @@ export interface ITextFile extends IErrorList {
170177 /** Clears file content and errors */
171178 clear ( ) : void ;
172179
180+ /**
181+ * Refreshes the stored external file stamp baseline.
182+ */
183+ refreshExternalFileStamp ( ) : void ;
184+
185+ /**
186+ * Checks whether the file changed externally since the last stamp refresh/check.
187+ */
188+ hasExternalFileChanged ( ) : boolean ;
189+
173190 /** Copy text and parse it to the object, does not change filename
174191 * @param src source file
175192 */
@@ -194,6 +211,7 @@ export class TextFile extends ErrorList implements ITextFile {
194211 protected textRenderer ?: ITextRenderer ;
195212
196213 private _readOnly = false ;
214+ private externalFileStamp ?: ExternalFileStamp ;
197215
198216 /**
199217 * Constructs a TextFile instance
@@ -243,6 +261,7 @@ export class TextFile extends ErrorList implements ITextFile {
243261 this . _dirty = false ;
244262 this . contentString = '' ;
245263 this . contentObject = undefined ;
264+ this . externalFileStamp = undefined ;
246265 this . clearErrors ( ) ;
247266 }
248267
@@ -285,6 +304,7 @@ export class TextFile extends ErrorList implements ITextFile {
285304 if ( value !== this . _fileName ) {
286305 this . _fileName = value ;
287306 this . _fileDir = path . dirname ( value ) ;
307+ this . externalFileStamp = undefined ;
288308 }
289309 }
290310
@@ -360,6 +380,7 @@ export class TextFile extends ErrorList implements ITextFile {
360380 this . fileName = fileName ;
361381 }
362382 const result = this . doLoad ( ) ;
383+ this . refreshExternalFileStamp ( ) ;
363384 this . showErrorMessage ( result ) ;
364385 return result ;
365386 }
@@ -402,10 +423,47 @@ export class TextFile extends ErrorList implements ITextFile {
402423 this . _dirty = true ; // force saving
403424 }
404425 const result = this . doSave ( ) ;
426+ this . refreshExternalFileStamp ( ) ;
405427 this . showErrorMessage ( result ) ;
406428 return result ;
407429 }
408430
431+ private getCurrentFileStamp ( ) : ExternalFileStamp | undefined {
432+ if ( ! this . fileName || ! fsUtils . fileExists ( this . fileName ) ) {
433+ return undefined ;
434+ }
435+ try {
436+ const stat = fs . statSync ( this . fileName ) ;
437+ return {
438+ path : this . fileName ,
439+ mtimeMs : stat . mtimeMs ,
440+ size : stat . size ,
441+ } ;
442+ } catch {
443+ return undefined ;
444+ }
445+ }
446+
447+ public refreshExternalFileStamp ( ) : void {
448+ this . externalFileStamp = this . getCurrentFileStamp ( ) ;
449+ }
450+
451+ public hasExternalFileChanged ( ) : boolean {
452+ const currentStamp = this . getCurrentFileStamp ( ) ;
453+ if ( ! this . externalFileStamp ) {
454+ this . externalFileStamp = currentStamp ;
455+ return false ;
456+ }
457+
458+ const changed = ! currentStamp
459+ || currentStamp . path !== this . externalFileStamp . path
460+ || currentStamp . mtimeMs !== this . externalFileStamp . mtimeMs
461+ || currentStamp . size !== this . externalFileStamp . size ;
462+
463+ this . externalFileStamp = currentStamp ;
464+ return changed ;
465+ }
466+
409467 /**
410468 * Saves file content to disk
411469 * @returns Save result
0 commit comments