44 Symbol,
55} = primordials ;
66
7- const acorn = require ( 'internal/deps/acorn/acorn/dist/acorn' ) ;
7+ const { MathMin } = primordials ;
8+
9+ const { tokTypes : tt , Parser : AcornParser } =
10+ require ( 'internal/deps/acorn/acorn/dist/acorn' ) ;
811const privateMethods =
912 require ( 'internal/deps/acorn-plugins/acorn-private-methods/index' ) ;
1013const classFields =
@@ -13,7 +16,30 @@ const numericSeparator =
1316 require ( 'internal/deps/acorn-plugins/acorn-numeric-separator/index' ) ;
1417const staticClassFeatures =
1518 require ( 'internal/deps/acorn-plugins/acorn-static-class-features/index' ) ;
16- const { tokTypes : tt , Parser : AcornParser } = acorn ;
19+
20+ const { sendInspectorCommand } = require ( 'internal/util/inspector' ) ;
21+
22+ const {
23+ ERR_INSPECTOR_NOT_AVAILABLE
24+ } = require ( 'internal/errors' ) . codes ;
25+
26+ const {
27+ clearLine,
28+ cursorTo,
29+ moveCursor,
30+ } = require ( 'readline' ) ;
31+
32+ const { inspect } = require ( 'util' ) ;
33+
34+ const debug = require ( 'internal/util/debuglog' ) . debuglog ( 'repl' ) ;
35+
36+ const inspectOptions = {
37+ depth : 1 ,
38+ colors : false ,
39+ compact : true ,
40+ breakLength : Infinity
41+ } ;
42+ const inspectedOptions = inspect ( inspectOptions , { colors : false } ) ;
1743
1844// If the error is that we've unexpectedly ended the input,
1945// then let the user try to recover by adding more input.
@@ -91,7 +117,144 @@ function isRecoverableError(e, code) {
91117 }
92118}
93119
120+ function setupPreview ( repl , contextSymbol , bufferSymbol , active ) {
121+ // Simple terminals can't handle previews.
122+ if ( process . env . TERM === 'dumb' || ! active ) {
123+ return { showInputPreview ( ) { } , clearPreview ( ) { } } ;
124+ }
125+
126+ let preview = null ;
127+ let lastPreview = '' ;
128+
129+ const clearPreview = ( ) => {
130+ if ( preview !== null ) {
131+ moveCursor ( repl . output , 0 , 1 ) ;
132+ clearLine ( repl . output ) ;
133+ moveCursor ( repl . output , 0 , - 1 ) ;
134+ lastPreview = preview ;
135+ preview = null ;
136+ }
137+ } ;
138+
139+ // This returns a code preview for arbitrary input code.
140+ function getPreviewInput ( input , callback ) {
141+ // For similar reasons as `defaultEval`, wrap expressions starting with a
142+ // curly brace with parenthesis.
143+ if ( input . startsWith ( '{' ) && ! input . endsWith ( ';' ) ) {
144+ input = `(${ input } )` ;
145+ }
146+ sendInspectorCommand ( ( session ) => {
147+ session . post ( 'Runtime.evaluate' , {
148+ expression : input ,
149+ throwOnSideEffect : true ,
150+ timeout : 333 ,
151+ contextId : repl [ contextSymbol ] ,
152+ } , ( error , preview ) => {
153+ if ( error ) {
154+ callback ( error ) ;
155+ return ;
156+ }
157+ const { result } = preview ;
158+ if ( result . value !== undefined ) {
159+ callback ( null , inspect ( result . value , inspectOptions ) ) ;
160+ // Ignore EvalErrors, SyntaxErrors and ReferenceErrors. It is not clear
161+ // where they came from and if they are recoverable or not. Other errors
162+ // may be inspected.
163+ } else if ( preview . exceptionDetails &&
164+ ( result . className === 'EvalError' ||
165+ result . className === 'SyntaxError' ||
166+ result . className === 'ReferenceError' ) ) {
167+ callback ( null , null ) ;
168+ } else if ( result . objectId ) {
169+ session . post ( 'Runtime.callFunctionOn' , {
170+ functionDeclaration : `(v) => util.inspect(v, ${ inspectedOptions } )` ,
171+ objectId : result . objectId ,
172+ arguments : [ result ]
173+ } , ( error , preview ) => {
174+ if ( error ) {
175+ callback ( error ) ;
176+ } else {
177+ callback ( null , preview . result . value ) ;
178+ }
179+ } ) ;
180+ } else {
181+ // Either not serializable or undefined.
182+ callback ( null , result . unserializableValue || result . type ) ;
183+ }
184+ } ) ;
185+ } , ( ) => callback ( new ERR_INSPECTOR_NOT_AVAILABLE ( ) ) ) ;
186+ }
187+
188+ const showInputPreview = ( ) => {
189+ // Prevent duplicated previews after a refresh.
190+ if ( preview !== null ) {
191+ return ;
192+ }
193+
194+ const line = repl . line . trim ( ) ;
195+
196+ // Do not preview if the command is buffered or if the line is empty.
197+ if ( repl [ bufferSymbol ] || line === '' ) {
198+ return ;
199+ }
200+
201+ getPreviewInput ( line , ( error , inspected ) => {
202+ // Ignore the output if the value is identical to the current line and the
203+ // former preview is not identical to this preview.
204+ if ( ( line === inspected && lastPreview !== inspected ) ||
205+ inspected === null ) {
206+ return ;
207+ }
208+ if ( error ) {
209+ debug ( 'Error while generating preview' , error ) ;
210+ return ;
211+ }
212+ // Do not preview `undefined` if colors are deactivated or explicitly
213+ // requested.
214+ if ( inspected === 'undefined' &&
215+ ( ! repl . useColors || repl . ignoreUndefined ) ) {
216+ return ;
217+ }
218+
219+ preview = inspected ;
220+
221+ // Limit the output to maximum 250 characters. Otherwise it becomes a)
222+ // difficult to read and b) non terminal REPLs would visualize the whole
223+ // output.
224+ const maxColumns = MathMin ( repl . columns , 250 ) ;
225+
226+ if ( inspected . length > maxColumns ) {
227+ inspected = `${ inspected . slice ( 0 , maxColumns - 6 ) } ...` ;
228+ }
229+ const lineBreakPos = inspected . indexOf ( '\n' ) ;
230+ if ( lineBreakPos !== - 1 ) {
231+ inspected = `${ inspected . slice ( 0 , lineBreakPos ) } ` ;
232+ }
233+ const result = repl . useColors ?
234+ `\u001b[90m${ inspected } \u001b[39m` :
235+ `// ${ inspected } ` ;
236+
237+ repl . output . write ( `\n${ result } ` ) ;
238+ moveCursor ( repl . output , 0 , - 1 ) ;
239+ cursorTo ( repl . output , repl . cursor + repl . _prompt . length ) ;
240+ } ) ;
241+ } ;
242+
243+ // Refresh prints the whole screen again and the preview will be removed
244+ // during that procedure. Print the preview again. This also makes sure
245+ // the preview is always correct after resizing the terminal window.
246+ const tmpRefresh = repl . _refreshLine . bind ( repl ) ;
247+ repl . _refreshLine = ( ) => {
248+ preview = null ;
249+ tmpRefresh ( ) ;
250+ showInputPreview ( ) ;
251+ } ;
252+
253+ return { showInputPreview, clearPreview } ;
254+ }
255+
94256module . exports = {
95257 isRecoverableError,
96- kStandaloneREPL : Symbol ( 'kStandaloneREPL' )
258+ kStandaloneREPL : Symbol ( 'kStandaloneREPL' ) ,
259+ setupPreview
97260} ;
0 commit comments