@@ -9,7 +9,7 @@ import type {
99 IToken ,
1010 StatementCstNode
1111} from "java-parser" ;
12- import type { AstPath , Doc , ParserOptions } from "prettier" ;
12+ import type { AstPath , Doc , Options , ParserOptions } from "prettier" ;
1313import { builders } from "prettier/doc" ;
1414import type { JavaComment } from "../comments.js" ;
1515import parser from "../parser.js" ;
@@ -328,6 +328,94 @@ export function printClassType(
328328 } ) ;
329329}
330330
331+ export function printTextBlock ( path : AstPath < JavaTerminal > ) {
332+ const [ open , ...lines ] = path . node . image . split ( "\n" ) ;
333+ const baseIndent = findBaseIndent ( lines ) ;
334+ const textBlock = join ( hardline , [
335+ open ,
336+ ...lines . map ( line => line . slice ( baseIndent ) )
337+ ] ) ;
338+ const ancestor = path . getNode ( 16 ) as JavaNonTerminal | null ;
339+ return ancestor ?. name === "variableInitializer" ||
340+ ( ancestor ?. name === "binaryExpression" &&
341+ ancestor . children . AssignmentOperator )
342+ ? indent ( textBlock )
343+ : textBlock ;
344+ }
345+
346+ export function embedTextBlock ( path : AstPath < JavaTerminal > ) {
347+ const language = findEmbeddedLanguage ( path ) ;
348+ if ( ! language ) {
349+ return null ;
350+ }
351+ const text = path . node . image
352+ . replace ( / ^ " " " \n / , "" )
353+ . replace ( / " " " $ / , "" )
354+ . replace ( / \\ u + ( [ 0 - 9 a - f A - F ] { 4 } ) / g, ( _ , hex ) =>
355+ String . fromCharCode ( parseInt ( hex , 16 ) )
356+ ) ;
357+ const unindentedText = stripIndent ( text ) ;
358+ const decodedText = translateEscapes ( unindentedText ) ;
359+
360+ return async (
361+ textToDoc : ( text : string , options : Options ) => Promise < Doc >
362+ ) => {
363+ const doc = await textToDoc ( decodedText , { parser : language } ) ;
364+ return group ( indent ( [ '"""' , hardline , doc , hardline , '"""' ] ) ) ;
365+ } ;
366+ }
367+
368+ function findEmbeddedLanguage ( path : AstPath < JavaNode > ) {
369+ return path . ancestors
370+ . find (
371+ node =>
372+ ( isNonTerminal ( node ) && node . name === "blockStatement" ) ||
373+ node . comments ?. some ( ( { leading } ) => leading )
374+ )
375+ ?. comments ?. filter ( ( { leading } ) => leading )
376+ . reverse ( )
377+ . map (
378+ ( { image } ) =>
379+ image . match ( / ^ (?: \/ \/ | \/ \* ) \s * l a n g u a g e \s * = \s * ( [ ^ \s ] + ) / ) ?. [ 1 ]
380+ )
381+ . find ( language => language )
382+ ?. toLowerCase ( ) ;
383+ }
384+
385+ function stripIndent ( text : string ) {
386+ const lines = text . split ( "\n" ) ;
387+ const indent = findBaseIndent ( lines ) ;
388+ return lines . map ( line => line . slice ( indent ) ) . join ( "\n" ) ;
389+ }
390+
391+ function translateEscapes ( text : string ) {
392+ return text . replace (
393+ / \\ (?: ( [ b f n t r " ' \\ ] ) | ( [ 0 - 7 ] { 1 , 3 } ) | \n ) / g,
394+ ( _ , single , octal ) => {
395+ if ( single ) {
396+ switch ( single ) {
397+ case "b" :
398+ return "\b" ;
399+ case "f" :
400+ return "\f" ;
401+ case "n" :
402+ return "\n" ;
403+ case "t" :
404+ return "\t" ;
405+ case "r" :
406+ return "\r" ;
407+ default :
408+ return single ;
409+ }
410+ } else if ( octal ) {
411+ return String . fromCharCode ( parseInt ( octal , 8 ) ) ;
412+ } else {
413+ return "" ;
414+ }
415+ }
416+ ) ;
417+ }
418+
331419export function isBinaryExpression ( expression : ExpressionCstNode ) {
332420 const conditionalExpression =
333421 expression . children . conditionalExpression ?. [ 0 ] . children ;
0 commit comments