@@ -5,9 +5,17 @@ import type { KernelMessage } from '@jupyterlab/services';
55import * as hashjs from 'hash.js' ;
66import { inject , injectable , multiInject , optional } from 'inversify' ;
77import stripAnsi from 'strip-ansi' ;
8- import { Event , EventEmitter , Position , Range , TextDocumentChangeEvent , TextDocumentContentChangeEvent } from 'vscode' ;
9-
10- import { splitMultilineString } from '../../../datascience-ui/common' ;
8+ import {
9+ Event ,
10+ EventEmitter ,
11+ Position ,
12+ Range ,
13+ TextDocumentChangeEvent ,
14+ TextDocumentContentChangeEvent ,
15+ Uri
16+ } from 'vscode' ;
17+
18+ import { concatMultilineString , splitMultilineString } from '../../../datascience-ui/common' ;
1119import { IDebugService , IDocumentManager } from '../../common/application/types' ;
1220import { traceError , traceInfo } from '../../common/logger' ;
1321
@@ -22,6 +30,8 @@ import {
2230 ICellHashListener ,
2331 ICellHashProvider ,
2432 IDataScienceFileSystem ,
33+ IExecuteOptions ,
34+ IExecuteResult ,
2535 IFileHashes ,
2636 INotebook ,
2737 INotebookExecutionLogger
@@ -45,6 +55,14 @@ interface IRangedCellHash extends ICellHash {
4555// hashes for cells.
4656@injectable ( )
4757export class CellHashProvider implements ICellHashProvider , INotebookExecutionLogger {
58+ public get updated ( ) : Event < void > {
59+ return this . updateEventEmitter . event ;
60+ }
61+
62+ // tslint:disable-next-line: no-any
63+ public get postMessage ( ) : Event < { message : string ; payload : any } > {
64+ return this . postEmitter . event ;
65+ }
4866 // tslint:disable-next-line: no-any
4967 private postEmitter : EventEmitter < { message : string ; payload : any } > = new EventEmitter < {
5068 message : string ;
@@ -73,15 +91,6 @@ export class CellHashProvider implements ICellHashProvider, INotebookExecutionLo
7391 this . traceBackRegexes . clear ( ) ;
7492 }
7593
76- public get updated ( ) : Event < void > {
77- return this . updateEventEmitter . event ;
78- }
79-
80- // tslint:disable-next-line: no-any
81- public get postMessage ( ) : Event < { message : string ; payload : any } > {
82- return this . postEmitter . event ;
83- }
84-
8594 public getHashes ( ) : IFileHashes [ ] {
8695 return [ ...this . hashes . entries ( ) ]
8796 . map ( ( e ) => {
@@ -100,18 +109,18 @@ export class CellHashProvider implements ICellHashProvider, INotebookExecutionLo
100109 this . updateEventEmitter . fire ( ) ;
101110 }
102111
103- public async preExecute ( cell : ICell , silent : boolean ) : Promise < void > {
112+ public async preExecute ( options : IExecuteOptions ) : Promise < void > {
104113 try {
105- if ( ! silent ) {
114+ if ( ! options . silent ) {
106115 // Don't log empty cells
107- const stripped = this . extractExecutableLines ( cell ) ;
116+ const stripped = this . extractExecutableLines ( options . code , options . file ) ;
108117 if ( stripped . length > 0 && stripped . find ( ( s ) => s . trim ( ) . length > 0 ) ) {
109118 // When the user adds new code, we know the execution count is increasing
110119 this . executionCount += 1 ;
111120
112121 // Skip hash on unknown file though
113- if ( cell . file !== Identifiers . EmptyFileName ) {
114- await this . addCellHash ( cell , this . executionCount ) ;
122+ if ( ! options . file && options . file !== Identifiers . EmptyFileName ) {
123+ await this . addExecutionHash ( options , this . executionCount ) ;
115124 }
116125 }
117126 }
@@ -121,7 +130,7 @@ export class CellHashProvider implements ICellHashProvider, INotebookExecutionLo
121130 }
122131 }
123132
124- public async postExecute ( _cell : ICell , _silent : boolean ) : Promise < void > {
133+ public async postExecute ( _options : IExecuteOptions , _result : IExecuteResult ) : Promise < void > {
125134 noop ( ) ;
126135 }
127136
@@ -140,9 +149,11 @@ export class CellHashProvider implements ICellHashProvider, INotebookExecutionLo
140149 return msg ;
141150 }
142151
143- public extractExecutableLines ( cell : ICell ) : string [ ] {
144- const cellMatcher = new CellMatcher ( this . configService . getSettings ( getCellResource ( cell ) ) . datascience ) ;
145- const lines = splitMultilineString ( cell . data . source ) ;
152+ public extractExecutableLines ( code : string , file ?: string ) : string [ ] {
153+ const cellMatcher = new CellMatcher (
154+ this . configService . getSettings ( file ? Uri . file ( file ) : undefined ) . datascience
155+ ) ;
156+ const lines = code . splitLines ( { trim : false , removeEmptyEntries : false } ) ;
146157 // Only strip this off the first line. Otherwise we want the markers in the code.
147158 if ( lines . length > 0 && ( cellMatcher . isCode ( lines [ 0 ] ) || cellMatcher . isMarkdown ( lines [ 0 ] ) ) ) {
148159 return lines . slice ( 1 ) ;
@@ -151,23 +162,35 @@ export class CellHashProvider implements ICellHashProvider, INotebookExecutionLo
151162 }
152163
153164 public generateHashFileName ( cell : ICell , expectedCount : number ) : string {
165+ const code = concatMultilineString ( cell . data . source ) ;
166+ const file = cell . file ;
154167 // First get the true lines from the cell
155- const { stripped } = this . extractStrippedLines ( cell ) ;
168+ const { stripped } = this . extractStrippedLines ( { code , file , id : cell . id } ) ;
156169
157170 // Then use that to make a hash value
158171 const hashedCode = stripped . join ( '' ) ;
159172 const hash = hashjs . sha1 ( ) . update ( hashedCode ) . digest ( 'hex' ) . substr ( 0 , 12 ) ;
160173 return `<ipython-input-${ expectedCount } -${ hash } >` ;
161174 }
162175
176+ public getExecutionCount ( ) : number {
177+ return this . executionCount ;
178+ }
179+
180+ public incExecutionCount ( ) : void {
181+ this . executionCount += 1 ;
182+ }
183+
163184 // tslint:disable-next-line: cyclomatic-complexity
164- public async addCellHash ( cell : ICell , expectedCount : number ) : Promise < void > {
185+ private async addExecutionHash ( options : IExecuteOptions , expectedCount : number ) : Promise < void > {
165186 // Find the text document that matches. We need more information than
166187 // the add code gives us
167- const doc = this . documentManager . textDocuments . find ( ( d ) => this . fs . areLocalPathsSame ( d . fileName , cell . file ) ) ;
188+ const doc = this . documentManager . textDocuments . find ( ( d ) =>
189+ this . fs . areLocalPathsSame ( d . fileName , options . file || '' )
190+ ) ;
168191 if ( doc ) {
169192 // Compute the code that will really be sent to jupyter
170- const { stripped, trueStartLine } = this . extractStrippedLines ( cell ) ;
193+ const { stripped, trueStartLine } = this . extractStrippedLines ( options ) ;
171194
172195 const line = doc . lineAt ( trueStartLine ) ;
173196 const endLine = doc . lineAt ( Math . min ( trueStartLine + stripped . length - 1 , doc . lineCount - 1 ) ) ;
@@ -180,13 +203,15 @@ export class CellHashProvider implements ICellHashProvider, INotebookExecutionLo
180203
181204 // Use the original values however to track edits. This is what we need
182205 // to move around
183- const startOffset = doc . offsetAt ( new Position ( cell . line , 0 ) ) ;
206+ const startOffset = doc . offsetAt ( new Position ( options . line || 0 , 0 ) ) ;
184207 const endOffset = doc . offsetAt ( endLine . rangeIncludingLineBreak . end ) ;
185208
186209 // Compute the runtime line and adjust our cell/stripped source for debugging
187- const runtimeLine = this . adjustRuntimeForDebugging ( cell , stripped , startOffset , endOffset ) ;
210+ const runtimeLine = this . adjustRuntimeForDebugging ( options , stripped ) ;
188211 const hashedCode = stripped . join ( '' ) ;
189- const realCode = doc . getText ( new Range ( new Position ( cell . line , 0 ) , endLine . rangeIncludingLineBreak . end ) ) ;
212+ const realCode = doc . getText (
213+ new Range ( new Position ( options . line || 0 , 0 ) , endLine . rangeIncludingLineBreak . end )
214+ ) ;
190215
191216 const hash : IRangedCellHash = {
192217 hash : hashjs . sha1 ( ) . update ( hashedCode ) . digest ( 'hex' ) . substr ( 0 , 12 ) ,
@@ -201,13 +226,14 @@ export class CellHashProvider implements ICellHashProvider, INotebookExecutionLo
201226 trimmedRightCode : stripped . map ( ( s ) => s . replace ( / [ \t \r ] + \n $ / g, '\n' ) ) . join ( '' ) ,
202227 realCode,
203228 runtimeLine,
204- id : cell . id ,
229+ id : options . id ,
205230 timestamp : Date . now ( )
206231 } ;
207232
208233 traceInfo ( `Adding hash for ${ expectedCount } = ${ hash . hash } with ${ stripped . length } lines` ) ;
209234
210- let list = this . hashes . get ( cell . file ) ;
235+ const hashKey = options . file || '' ;
236+ let list = this . hashes . get ( hashKey ) ;
211237 if ( ! list ) {
212238 list = [ ] ;
213239 }
@@ -230,15 +256,15 @@ export class CellHashProvider implements ICellHashProvider, INotebookExecutionLo
230256 if ( ! inserted ) {
231257 list . push ( hash ) ;
232258 }
233- this . hashes . set ( cell . file , list ) ;
259+ this . hashes . set ( hashKey , list ) ;
234260
235261 // Save a regex to find this file later when looking for
236262 // exceptions in output
237- if ( ! this . traceBackRegexes . has ( cell . file ) ) {
238- const fileDisplayName = this . fs . getDisplayName ( cell . file ) ;
263+ if ( ! this . traceBackRegexes . has ( hashKey ) ) {
264+ const fileDisplayName = this . fs . getDisplayName ( hashKey ) ;
239265 const escaped = _escapeRegExp ( fileDisplayName ) ;
240266 const fileMatchRegex = new RegExp ( `\\[.*?;32m${ escaped } ` ) ;
241- this . traceBackRegexes . set ( cell . file , fileMatchRegex ) ;
267+ this . traceBackRegexes . set ( hashKey , fileMatchRegex ) ;
242268 }
243269
244270 // Tell listeners we have new hashes.
@@ -252,14 +278,6 @@ export class CellHashProvider implements ICellHashProvider, INotebookExecutionLo
252278 }
253279 }
254280
255- public getExecutionCount ( ) : number {
256- return this . executionCount ;
257- }
258-
259- public incExecutionCount ( ) : void {
260- this . executionCount += 1 ;
261- }
262-
263281 private onChangedDocument ( e : TextDocumentChangeEvent ) {
264282 // See if the document is in our list of docs to watch
265283 const perFile = this . hashes . get ( e . document . fileName ) ;
@@ -272,14 +290,14 @@ export class CellHashProvider implements ICellHashProvider, INotebookExecutionLo
272290 }
273291 }
274292
275- private extractStrippedLines ( cell : ICell ) : { stripped : string [ ] ; trueStartLine : number } {
293+ private extractStrippedLines ( options : IExecuteOptions ) : { stripped : string [ ] ; trueStartLine : number } {
276294 // Compute the code that will really be sent to jupyter
277- const lines = splitMultilineString ( cell . data . source ) ;
278- const stripped = this . extractExecutableLines ( cell ) ;
295+ const lines = options . code . splitLines ( { trim : false , removeEmptyEntries : false } ) ;
296+ const stripped = this . extractExecutableLines ( options . code , options . file ) ;
279297
280298 // Figure out our true 'start' line. This is what we need to tell the debugger is
281299 // actually the start of the code as that's what Jupyter will be getting.
282- let trueStartLine = cell . line ;
300+ let trueStartLine = options . line || 0 ;
283301 for ( let i = 0 ; i < stripped . length ; i += 1 ) {
284302 if ( stripped [ i ] !== lines [ i ] ) {
285303 trueStartLine += i + 1 ;
@@ -355,20 +373,15 @@ export class CellHashProvider implements ICellHashProvider, INotebookExecutionLo
355373 } ) ;
356374 }
357375
358- private adjustRuntimeForDebugging (
359- cell : ICell ,
360- source : string [ ] ,
361- _cellStartOffset : number ,
362- _cellEndOffset : number
363- ) : number {
376+ private adjustRuntimeForDebugging ( options : IExecuteOptions , source : string [ ] ) : number {
364377 if (
365378 this . debugService . activeDebugSession &&
366- this . configService . getSettings ( getCellResource ( cell ) ) . datascience . stopOnFirstLineWhileDebugging
379+ this . configService . getSettings ( options . file ? Uri . file ( options . file ) : undefined ) . datascience
380+ . stopOnFirstLineWhileDebugging
367381 ) {
368382 // Inject the breakpoint line
369383 source . splice ( 0 , 0 , 'breakpoint()\n' ) ;
370- cell . data . source = source ;
371- cell . extraLines = [ 0 ] ;
384+ options . code = `breakpoint()\n${ options . code } ` ;
372385
373386 // Start on the second line
374387 return 2 ;
0 commit comments