1 /** 2 * Copyright: Copyright (c) 2012 Jacob Carlborg. All rights reserved. 3 * Authors: Jacob Carlborg 4 * Version: Initial created: Jan 29, 2012 5 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 */ 7 module dstep.translator.Output; 8 9 import std.array; 10 import std.typecons; 11 import std.variant; 12 13 import clang.Cursor; 14 import clang.SourceLocation; 15 import clang.SourceRange; 16 import clang.Util; 17 18 import dstep.translator.CommentIndex; 19 import dstep.translator.Context; 20 import dstep.translator.IncludeHandler; 21 import dstep.translator.Type; 22 23 struct SourceLeaf 24 { 25 string spelling; 26 SourceRange extent; 27 } 28 29 SourceLeaf makeSourceLeaf( 30 string spelling, 31 SourceRange extent = SourceRange.empty) 32 { 33 SourceLeaf result; 34 result.spelling = spelling; 35 result.extent = extent; 36 return result; 37 } 38 39 struct SourceNode 40 { 41 alias Child = Algebraic!(SourceNode*, SourceLeaf); 42 string prefix; 43 string suffix; 44 string separator; 45 Child[] children; 46 SourceRange extent; 47 } 48 49 SourceNode makeSourceNode( 50 string prefix, 51 string[] children, 52 string separator, 53 string suffix, 54 SourceRange extent = SourceRange.empty) 55 { 56 import std.algorithm; 57 SourceNode node; 58 node.prefix = prefix; 59 node.suffix = suffix; 60 node.separator = separator; 61 node.children = children.map!(x => SourceNode.Child(SourceLeaf(x))).array; 62 node.extent = extent; 63 return node; 64 } 65 66 SourceNode makeSourceNode(SourceLeaf leaf) 67 { 68 SourceNode node; 69 node.prefix = leaf.spelling; 70 node.extent = leaf.extent; 71 return node; 72 } 73 74 SourceNode makeSourceNode( 75 string spelling, 76 SourceRange extent = SourceRange.empty) 77 { 78 SourceNode node; 79 node.prefix = spelling; 80 node.extent = extent; 81 return node; 82 } 83 84 SourceNode suffixWith(SourceNode node, string suffix) 85 { 86 SourceNode result = node; 87 result.suffix = result.suffix ~ suffix; 88 return result; 89 } 90 91 SourceNode prefixWith(SourceNode node, string prefix) 92 { 93 SourceNode result = node; 94 result.prefix = prefix ~ result.prefix; 95 return result; 96 } 97 98 SourceNode wrapWith(SourceNode node, string prefix, string suffix) 99 { 100 SourceNode result = node; 101 result.prefix = prefix ~ result.prefix; 102 result.suffix = result.suffix ~ suffix; 103 return result; 104 } 105 106 SourceNode flatten(in SourceNode node) 107 { 108 void flatten(ref Appender!(char[]) output, in SourceNode node) 109 { 110 output.put(node.prefix); 111 112 foreach (index, child; node.children) 113 { 114 if (child.type() == typeid(SourceLeaf)) 115 output ~= child.get!(SourceLeaf).spelling; 116 else 117 flatten(output, *child.get!(SourceNode*)); 118 119 if (index + 1 != node.children.length) 120 output.put(node.separator ~ " "); 121 } 122 123 output.put(node.suffix); 124 } 125 126 Appender!(char[]) output; 127 flatten(output, node); 128 return output.data.idup.makeSourceNode(); 129 } 130 131 string makeString(in SourceNode node) 132 { 133 import std.range; 134 auto flattened = node.flatten(); 135 assert(flattened.children.empty); 136 assert(flattened.suffix.empty); 137 return flattened.prefix; 138 } 139 140 void adaptiveSourceNode(Output output, in SourceNode node) 141 { 142 auto format = node.prefix ~ "%@" ~ node.separator ~ "%@" ~ node.suffix; 143 144 output.adaptiveLine(node.extent, format) in 145 { 146 foreach (child; node.children) 147 { 148 if (child.type() == typeid(SourceLeaf)) 149 { 150 auto leaf = child.get!(SourceLeaf); 151 output.adaptiveLine(leaf.extent, leaf.spelling); 152 } 153 else 154 adaptiveSourceNode(output, *child.get!(SourceNode*)); 155 } 156 }; 157 } 158 159 unittest 160 { 161 SourceNode node = makeSourceNode( 162 "prefix(", [], ",", ");"); 163 164 assert(makeString(node) == "prefix();"); 165 } 166 167 unittest 168 { 169 SourceNode node = makeSourceNode( 170 "prefix(", ["a", "b"], ",", ");"); 171 172 assert(makeString(node) == "prefix(a, b);"); 173 } 174 175 unittest 176 { 177 SourceNode node = makeSourceNode("prefix(a, b);"); 178 179 assert(makeString(node) == "prefix(a, b);"); 180 } 181 182 class Output 183 { 184 private enum Entity 185 { 186 bottom, 187 separator, 188 comment, 189 singleLine, 190 adaptiveLine, 191 multiLine, 192 subscopeWeak, 193 subscopeStrong, 194 } 195 196 private enum ChunkType 197 { 198 opening, 199 closing, 200 item, 201 } 202 203 private struct Chunk 204 { 205 ChunkType type; 206 string content; 207 string separator; 208 } 209 210 const size_t marginSize = 80; 211 const size_t indentSize = 4; 212 private Appender!(char[]) buffer; 213 private Appender!(char[]) weak; 214 private Entity[] stack; 215 private Entity first = Entity.bottom; 216 private Appender!(Chunk[]) chunks; 217 private CommentIndex commentIndex = null; 218 219 private uint lastestOffset = 0; 220 private uint lastestLine = 0; 221 private uint headerEndOffset = 0; 222 223 this(Output parent) 224 { 225 stack ~= Entity.bottom; 226 227 // formattedWrite will not write anything 228 // to the output range if put was not invoked before. 229 buffer.put(""); 230 weak.put(""); 231 commentIndex = parent.commentIndex; 232 lastestOffset = parent.lastestOffset; 233 lastestLine = parent.lastestLine; 234 } 235 236 this( 237 CommentIndex commentIndex = null, 238 size_t marginSize = 80, 239 size_t indentSize = 4) 240 { 241 this.marginSize = marginSize; 242 this.indentSize = indentSize; 243 244 stack ~= Entity.bottom; 245 246 // formattedWrite will not write anything 247 // to the output range if put was not invoked before. 248 buffer.put(""); 249 weak.put(""); 250 251 this.commentIndex = commentIndex; 252 } 253 254 public void reset() 255 { 256 buffer.clear(); 257 weak.clear(); 258 259 // formattedWrite will not write anything 260 // to the output range if put was not invoked before. 261 buffer.put(""); 262 weak.put(""); 263 264 stack = [ Entity.bottom ]; 265 266 first = Entity.bottom; 267 chunks.clear(); 268 269 lastestOffset = 0; 270 lastestLine = 0; 271 headerEndOffset = 0; 272 } 273 274 public bool empty() 275 { 276 return stack.length == 1 && stack.back == Entity.bottom; 277 } 278 279 public void separator() 280 { 281 if (stack.back == Entity.singleLine || 282 stack.back == Entity.adaptiveLine || 283 stack.back == Entity.comment) 284 stack.back = Entity.separator; 285 } 286 287 public void output(Output output) 288 { 289 if (output.stack.length == 1 && output.stack.back == Entity.singleLine) 290 singleLine(output.data()); 291 else 292 { 293 import std..string : splitLines, KeepTerminator; 294 295 if (output.stack.back != Entity.bottom) 296 flush(); 297 298 if (stack.back != Entity.bottom && 299 output.stack.back != Entity.bottom) 300 buffer.put("\n"); 301 302 if (stack.back != Entity.bottom && 303 output.first != Entity.bottom && 304 (stack.back != Entity.singleLine || 305 output.first != Entity.singleLine) && 306 (stack.back != Entity.singleLine || 307 output.first != Entity.adaptiveLine) && 308 (stack.back != Entity.adaptiveLine || 309 output.first != Entity.adaptiveLine)) 310 buffer.put("\n"); 311 312 foreach (line; output.data().splitLines(KeepTerminator.yes)) 313 { 314 indent(); 315 buffer.put(line); 316 } 317 318 stack.popBack(); 319 stack ~= output.stack; 320 321 if (first == Entity.bottom) 322 first = output.first; 323 324 import std.algorithm.comparison; 325 326 lastestOffset = max(lastestOffset, output.lastestOffset); 327 lastestLine = max(lastestLine, output.lastestLine); 328 } 329 } 330 331 public void append(Char, Args...)(in Char[] fmt, Args args) 332 { 333 import std.format; 334 335 if (stack.back != Entity.singleLine && 336 stack.back != Entity.adaptiveLine) 337 singleLine(fmt, args); 338 else if (weak.data.empty) 339 formattedWrite(buffer, fmt, args); 340 else 341 formattedWrite(weak, fmt, args); 342 } 343 344 private void singleLineImpl(Char, Args...)(in Char[] fmt, Args args) 345 { 346 import std.format; 347 348 indent(); 349 formattedWrite(buffer, fmt, args); 350 stack.back = Entity.singleLine; 351 352 if (first == Entity.bottom) 353 first = Entity.singleLine; 354 } 355 356 public void singleLine(Char, Args...)(in Char[] fmt, Args args) 357 { 358 import std.format; 359 360 if (stack.length > 1 && 361 stack.back == Entity.bottom && 362 stack[$ - 2] == Entity.subscopeWeak) 363 { 364 // We are on the first line of weak sub-scope. 365 // Save the line for later handling. 366 formattedWrite(weak, fmt, args); 367 stack.back = Entity.singleLine; 368 } 369 else 370 { 371 flush(); 372 373 if (stack.back != Entity.singleLine && 374 stack.back != Entity.adaptiveLine && 375 stack.back != Entity.bottom && 376 stack.back != Entity.comment) 377 buffer.put("\n"); 378 379 if (stack.back != Entity.bottom) 380 buffer.put("\n"); 381 382 singleLineImpl(fmt, args); 383 } 384 } 385 386 public void singleLine(Char, Args...)( 387 in SourceRange extent, 388 in Char[] fmt, 389 Args args) 390 { 391 flushLocation(extent); 392 singleLine(fmt, args); 393 } 394 395 private Tuple!(string, string, string) adaptiveLineParts(Char, Args...)( 396 in Char[] fmt, 397 Args args) 398 { 399 import std.algorithm.searching; 400 import std.format : format; 401 402 size_t findPlaceholder(in Char[] fmt) 403 { 404 foreach (i; 0 .. fmt.length) 405 { 406 if (fmt[i] == '@' && i != 0) 407 { 408 size_t j = i; 409 410 while (i != 0 && fmt[i - 1] == '%') 411 --i; 412 413 if ((i - j) % 2 == 1) 414 return i; 415 } 416 } 417 418 return fmt.length; 419 } 420 421 size_t begin = findPlaceholder(fmt); 422 423 if (begin != fmt.length) 424 { 425 string separator, opening, closing; 426 size_t end = findPlaceholder(fmt[begin + 2 .. $]) + begin + 2; 427 428 if (end != fmt.length) 429 separator = fmt[begin + 2 .. end].idup; 430 else 431 end = begin; 432 433 size_t minUnique = count(fmt, '@') + 1; 434 auto sentinel = replicate("@", minUnique); 435 436 auto split = format( 437 fmt[0 .. begin] ~ 438 sentinel ~ 439 fmt[end + 2 .. $], 440 args).findSplit(sentinel); 441 442 return tuple(split[0], separator, split[2]); 443 } 444 else 445 { 446 return tuple(format(fmt, args), "", ""); 447 } 448 } 449 450 private string adaptiveLineImpl(Char, Args...)(in Char[] fmt, Args args) 451 { 452 if (stack.length != 1 && 453 stack[$ - 2] != Entity.adaptiveLine || 454 stack.length == 1) 455 { 456 if (stack.back != Entity.singleLine && 457 stack.back != Entity.adaptiveLine && 458 stack.back != Entity.bottom && 459 stack.back != Entity.comment) 460 buffer.put("\n"); 461 462 if (stack.back != Entity.bottom) 463 buffer.put("\n"); 464 } 465 466 stack.back = Entity.adaptiveLine; 467 stack ~= Entity.bottom; 468 469 if (first == Entity.bottom) 470 first = Entity.adaptiveLine; 471 472 auto parts = adaptiveLineParts(fmt, args); 473 474 chunks.put(Chunk(ChunkType.opening, parts[0], parts[1])); 475 return parts[2]; 476 } 477 478 public Indent adaptiveLine(Char, Args...)(in Char[] fmt, Args args) 479 { 480 return Indent(this, adaptiveLineImpl(fmt, args)); 481 } 482 483 /** 484 * adaptiveLine adds a line to the output that is automatically broken to 485 * multiple lines, if it's too long (80 characters). 486 * 487 * It takes a special format specifier that is replaced with other nested 488 * adaptive lines. The specifier has form `%@<separator>%@`, where 489 * <separator> is a string that separates the items/lines (the spaces and 490 * end-lines are added automatically). 491 */ 492 public Indent adaptiveLine(Char, Args...)( 493 in SourceRange extent, 494 in Char[] fmt, 495 Args args) 496 { 497 SourceLocation start = extent.start; 498 SourceLocation end = extent.end; 499 500 flushLocationBegin(start.line, start.column, start.offset); 501 502 return Indent( 503 this, 504 adaptiveLineImpl(fmt, args), 505 end.line, 506 end.column, 507 end.offset); 508 } 509 510 /// 511 unittest 512 { 513 // Simple item. 514 auto example1 = new Output(); 515 516 example1.adaptiveLine("foo"); 517 518 assert(example1.data() == "foo"); 519 520 // Inline function. 521 auto example2 = new Output(); 522 523 example2.adaptiveLine("void foo(%@,%@);") in { 524 example2.adaptiveLine("int bar"); 525 example2.adaptiveLine("int baz"); 526 }; 527 528 assert(example2.data() == "void foo(int bar, int baz);"); 529 530 // Broken function. 531 auto example3 = new Output(); 532 533 example3.adaptiveLine("void foo(%@,%@);") in { 534 example3.adaptiveLine("int bar0"); 535 example3.adaptiveLine("int bar1"); 536 example3.adaptiveLine("int bar2"); 537 example3.adaptiveLine("int bar3"); 538 example3.adaptiveLine("int bar4"); 539 example3.adaptiveLine("int bar5"); 540 example3.adaptiveLine("int bar6"); 541 example3.adaptiveLine("int bar7"); 542 example3.adaptiveLine("int bar8"); 543 }; 544 545 assert(example3.data() == 546 q"D 547 void foo( 548 int bar0, 549 int bar1, 550 int bar2, 551 int bar3, 552 int bar4, 553 int bar5, 554 int bar6, 555 int bar7, 556 int bar8); 557 D"[0 .. $ - 1]); 558 559 // The adaptive lines can be nested multiple times. Breaking algorithm 560 // will try to break only the most outer levels. The %@<sep>%@ can be 561 // mixed with standard format specifiers. 562 auto example4 = new Output(); 563 564 example4.adaptiveLine("foo%d%s(%@,%@);", 123, "bar") in { 565 example4.adaptiveLine("bar0"); 566 example4.adaptiveLine("bar1"); 567 example4.adaptiveLine("bar2"); 568 example4.adaptiveLine("baz(%@ +%@)") in { 569 example4.adaptiveLine("0"); 570 example4.adaptiveLine("1"); 571 example4.adaptiveLine("2"); 572 example4.adaptiveLine("3"); 573 example4.adaptiveLine("4"); 574 }; 575 example4.adaptiveLine("bar4"); 576 example4.adaptiveLine("bar5"); 577 example4.adaptiveLine("bar6"); 578 example4.adaptiveLine("bar7"); 579 example4.adaptiveLine("bar8"); 580 }; 581 582 assert(example4.data() == 583 q"D 584 foo123bar( 585 bar0, 586 bar1, 587 bar2, 588 baz(0 + 1 + 2 + 3 + 4), 589 bar4, 590 bar5, 591 bar6, 592 bar7, 593 bar8); 594 D"[0 .. $ - 1]); 595 596 } 597 598 public Indent multiLine(Char, Args...)(in Char[] fmt, Args args) 599 { 600 import std.format; 601 602 flush(); 603 604 if (stack.back != Entity.bottom) 605 buffer.put("\n\n"); 606 607 indent(); 608 formattedWrite(buffer, fmt, args); 609 buffer.put("\n"); 610 stack.back = Entity.multiLine; 611 stack ~= Entity.bottom; 612 613 if (first == Entity.bottom) 614 first = Entity.multiLine; 615 616 return Indent(this); 617 } 618 619 private void subscopeStrongImpl(Char, Args...)( 620 in Char[] fmt, 621 Args args) 622 { 623 import std.format; 624 625 flush(); 626 627 if (stack.back == Entity.comment) 628 buffer.put("\n"); 629 else if (stack.back != Entity.bottom) 630 buffer.put("\n\n"); 631 632 indent(); 633 formattedWrite(buffer, fmt, args); 634 buffer.put("\n"); 635 indent(); 636 buffer.put("{\n"); 637 stack.back = Entity.subscopeStrong; 638 stack ~= Entity.bottom; 639 640 if (first == Entity.bottom) 641 first = Entity.subscopeStrong; 642 } 643 644 public Indent subscopeStrong(Char, Args...)( 645 in Char[] fmt, 646 Args args) 647 { 648 subscopeStrongImpl!(Char, Args)(fmt, args); 649 return Indent(this, "}"); 650 } 651 652 public Indent subscopeStrong(Char, Args...)( 653 in SourceRange extent, 654 in Char[] fmt, 655 Args args) 656 { 657 SourceLocation start = extent.start; 658 SourceLocation end = extent.end; 659 660 flushLocationBegin(start.line, start.column, start.offset); 661 subscopeStrongImpl(fmt, args); 662 return Indent(this, "}", end.line, end.column, end.offset); 663 } 664 665 public Indent subscopeWeak(Char, Args...)(in Char[] fmt, Args args) 666 { 667 import std.format; 668 669 flush(); 670 671 if (stack.back != Entity.bottom) 672 buffer.put("\n\n"); 673 674 indent(); 675 formattedWrite(buffer, fmt, args); 676 buffer.put("\n"); 677 stack.back = Entity.subscopeWeak; 678 stack ~= Entity.bottom; 679 680 if (first == Entity.bottom) 681 first = Entity.subscopeWeak; 682 683 return Indent(this, "}"); 684 } 685 686 private void writeComment(in CommentIndex.Comment comment) 687 { 688 import std.algorithm; 689 import std.ascii; 690 import std.format; 691 import std..string; 692 693 auto lines = lineSplitter!(KeepTerminator.yes)(comment.content); 694 695 if (!lines.empty) 696 { 697 size_t indentAmount = comment.indentAmount; 698 699 if (lastestLine != comment.line) 700 indent(); 701 702 buffer.put(lines.front); 703 lines.popFront; 704 705 foreach (line; lines) 706 { 707 if (line.all!isWhite) 708 { 709 buffer.put("\n"); 710 } 711 else 712 { 713 indent(); 714 buffer.put(line[indentAmount .. $]); 715 } 716 } 717 } 718 } 719 720 private void comment(in CommentIndex.Comment comment) 721 { 722 if (lastestLine == comment.line) 723 { 724 buffer.put(" "); 725 } 726 else if (stack.back != Entity.bottom) 727 { 728 if (lastestLine + 1 < comment.line || 729 (stack.back != Entity.singleLine && 730 stack.back != Entity.adaptiveLine && 731 stack.back != Entity.comment)) 732 buffer.put('\n'); 733 734 buffer.put('\n'); 735 } 736 737 writeComment(comment); 738 739 stack.back = Entity.comment; 740 741 if (first == Entity.bottom) 742 first = Entity.comment; 743 744 lastestLine = comment.extent.end.line; 745 lastestOffset = comment.extent.end.offset; 746 } 747 748 private void flushComments(uint offset) 749 { 750 if (lastestOffset < offset && commentIndex) 751 { 752 auto comments = commentIndex.queryComments(lastestOffset, offset); 753 754 foreach (c; comments) 755 comment(c); 756 757 lastestOffset = offset; 758 } 759 } 760 761 public void finalize() 762 { 763 if (!buffer.data.empty) 764 { 765 buffer.put("\n"); 766 767 if (stack.back == Entity.separator) 768 buffer.put("\n"); 769 } 770 } 771 772 private void flushLocationBegin( 773 uint beginLine, 774 uint beginColumn, 775 uint beginOffset, 776 bool separate = true) 777 { 778 flushComments(beginOffset); 779 780 if (separate && lastestLine + 1 < beginLine) 781 separator(); 782 } 783 784 private void flushLocationEnd( 785 uint endLine, 786 uint endColumn, 787 uint endOffset) 788 { 789 flushComments(endOffset); 790 791 import std.algorithm.comparison; 792 793 lastestLine = max(endLine, lastestLine); 794 lastestOffset = max(endOffset, lastestOffset); 795 } 796 797 public void flushLocation( 798 uint beginLine, 799 uint beginColumn, 800 uint beginOffset, 801 uint endLine, 802 uint endColumn, 803 uint endOffset, 804 bool separate = true) 805 { 806 flushLocationBegin(beginLine, beginColumn, beginOffset, separate); 807 flushLocationEnd(endLine, endColumn, endOffset); 808 } 809 810 public void flushLocation( 811 uint line, 812 uint column, 813 uint offset, 814 bool separate = true) 815 { 816 flushLocation(line, column, offset, line, column, offset, separate); 817 } 818 819 public void flushLocation(in SourceLocation location, bool separate = true) 820 { 821 flushLocation( 822 location.line, 823 location.column, 824 location.offset, 825 separate); 826 } 827 828 public void flushLocation(in SourceRange range, bool separate = true) 829 { 830 SourceLocation begin = range.start; 831 SourceLocation end = range.end; 832 833 flushLocation( 834 begin.line, 835 begin.column, 836 begin.offset, 837 end.line, 838 end.column, 839 end.offset, 840 separate); 841 } 842 843 public void flushLocation(in Cursor cursor, bool separate = true) 844 { 845 flushLocation(cursor.extent, separate); 846 } 847 848 public bool flushHeaderComment() 849 { 850 if (commentIndex && commentIndex.isHeaderCommentPresent) 851 { 852 auto location = commentIndex.queryHeaderCommentExtent.end; 853 headerEndOffset = location.offset + 2; 854 flushLocation(location, false); 855 return true; 856 } 857 else 858 { 859 return false; 860 } 861 } 862 863 public string data(string suffix = "") 864 { 865 if (!suffix.empty) 866 return (buffer.data() ~ suffix).idup; 867 else 868 return buffer.data().idup; 869 } 870 871 public string header() 872 { 873 import std.algorithm.comparison; 874 875 return buffer.data[0 .. min(headerEndOffset, $)].idup; 876 } 877 878 public string content() 879 { 880 import std.algorithm.comparison; 881 882 return buffer.data[min(headerEndOffset, $) .. $].idup; 883 } 884 885 struct Indent 886 { 887 private Output output; 888 private string sentinel; 889 private uint line = 0; 890 private uint column = 0; 891 private uint offset = 0; 892 893 ~this() 894 { 895 if (output.stack.length > 1 && 896 output.stack[$ - 2] == Entity.adaptiveLine) 897 { 898 if (offset != 0) 899 output.flushLocationEnd(line, column, offset); 900 901 output.stack.popBack(); 902 903 auto data = output.chunks.data; 904 905 if (data.back.type == ChunkType.opening) 906 { 907 data.back.type = ChunkType.item; 908 data.back.content ~= sentinel; 909 } 910 else 911 { 912 output.chunks.put(Chunk(ChunkType.closing, sentinel)); 913 } 914 915 if (output.stack.length == 1 || 916 output.stack[$ - 2] != Entity.adaptiveLine) 917 output.resolveAdaptive(); 918 } 919 else 920 { 921 bool flushed = output.flush(false); 922 923 if (offset != 0) 924 output.flushLocationEnd(line, column, offset); 925 926 auto back = output.stack.back; 927 output.stack.popBack(); 928 929 if (!sentinel.empty && !flushed) 930 { 931 if (back != Entity.bottom) 932 output.buffer.put("\n"); 933 934 output.indent(); 935 output.buffer.put(sentinel); 936 } 937 } 938 } 939 940 void opBinary(string op : "in")(void delegate () nested) 941 { 942 nested(); 943 } 944 } 945 946 private bool flush(bool brace = true) 947 { 948 // Handle a case when there is only line in sub-scope. 949 // When there is only single line, no braces should be put. 950 951 if (!weak.data.empty) 952 { 953 string weak = this.weak.data.idup; 954 this.weak.clear(); 955 956 if (brace) 957 { 958 indent(-1); 959 buffer.put("{\n"); 960 } 961 962 stack.back = Entity.singleLine; 963 singleLineImpl(weak); 964 weak = null; 965 966 return true; 967 } 968 else if (stack.length > 1 && 969 stack[$ - 2] == Entity.subscopeWeak && 970 stack.back == Entity.bottom && 971 brace) 972 { 973 indent(-1); 974 buffer.put("{\n"); 975 } 976 977 return false; 978 } 979 980 private Tuple!(size_t, size_t) resolveAdaptiveWidth(size_t itr) 981 { 982 auto data = chunks.data; 983 string[] separators = [ data[itr].separator ]; 984 size_t jtr = itr + 1; 985 size_t width = data[itr].content.length; 986 987 size_t separator(size_t jtr, size_t width) 988 { 989 if (jtr + 1 < data.length && 990 separators.length != 0 && 991 data[jtr + 1].type != ChunkType.closing && 992 width != 0) 993 return separators.back.length + 1; 994 else 995 return 0; 996 } 997 998 while (separators.length != 0) 999 { 1000 final switch (data[jtr].type) 1001 { 1002 case ChunkType.opening: 1003 width += data[jtr].content.length; 1004 separators ~= data[jtr].separator; 1005 break; 1006 1007 case ChunkType.closing: 1008 separators = separators[0 .. $ - 1]; 1009 width += 1010 data[jtr].content.length + 1011 separator(jtr, width); 1012 break; 1013 1014 case ChunkType.item: 1015 width += 1016 data[jtr].content.length + 1017 separator(jtr, width); 1018 break; 1019 } 1020 1021 ++jtr; 1022 } 1023 1024 return tuple(width, jtr); 1025 } 1026 1027 private void resolveAdaptiveAppend(size_t itr) 1028 { 1029 auto data = chunks.data; 1030 string[] separators = [ data[itr].separator ]; 1031 size_t jtr = itr + 1; 1032 1033 buffer.put(data[itr].content); 1034 1035 while (separators.length != 0) 1036 { 1037 final switch (data[jtr].type) 1038 { 1039 case ChunkType.opening: 1040 buffer.put(data[jtr].content); 1041 separators ~= data[jtr].separator; 1042 break; 1043 1044 case ChunkType.closing: 1045 separators = separators[0 .. $ - 1]; 1046 buffer.put(data[jtr].content); 1047 1048 if (jtr + 1 < data.length && separators.length != 0 && 1049 data[jtr + 1].type != ChunkType.closing) 1050 { 1051 buffer.put(separators.back); 1052 buffer.put(" "); 1053 } 1054 1055 break; 1056 1057 case ChunkType.item: 1058 buffer.put(data[jtr].content); 1059 1060 if (jtr + 1 < data.length && separators.length != 0 && 1061 data[jtr + 1].type != ChunkType.closing) 1062 { 1063 buffer.put(separators.back); 1064 buffer.put(" "); 1065 } 1066 1067 break; 1068 } 1069 1070 ++jtr; 1071 } 1072 } 1073 1074 private void resolveAdaptive() 1075 { 1076 string[] separators; 1077 size_t amount = (stack.length - 1) * indentSize; 1078 size_t itr = 0; 1079 1080 auto data = chunks.data; 1081 auto feed = ""; 1082 1083 while (itr < data.length) 1084 { 1085 if (data[itr].type == ChunkType.opening) 1086 { 1087 auto tuple = resolveAdaptiveWidth(itr); 1088 1089 size_t width = 1090 amount + separators.length * indentSize + tuple[0]; 1091 1092 if (width < marginSize) 1093 { 1094 buffer.put(feed); 1095 indent(separators.length); 1096 resolveAdaptiveAppend(itr); 1097 1098 if (tuple[1] < data.length && 1099 data[tuple[1]].type != ChunkType.closing) 1100 buffer.put(separators.back); 1101 1102 itr = tuple[1]; 1103 } 1104 else 1105 { 1106 buffer.put(feed); 1107 indent(separators.length); 1108 buffer.put(data[itr].content); 1109 separators ~= data[itr].separator; 1110 1111 ++itr; 1112 } 1113 1114 feed = "\n"; 1115 } 1116 else if (data[itr].type == ChunkType.closing) 1117 { 1118 separators = separators[0..$-1]; 1119 buffer.put(data[itr].content); 1120 1121 if (itr + 1 < data.length && 1122 data[itr + 1].type != ChunkType.closing && 1123 separators.length != 0) 1124 buffer.put(separators.back); 1125 1126 ++itr; 1127 1128 feed = "\n"; 1129 } 1130 else 1131 { 1132 buffer.put(feed); 1133 indent(separators.length); 1134 1135 if (!data[itr].content.empty) 1136 { 1137 buffer.put(data[itr].content); 1138 1139 if (itr + 1 < data.length && 1140 data[itr + 1].type != ChunkType.closing) 1141 buffer.put(separators.back); 1142 1143 feed = "\n"; 1144 } 1145 1146 ++itr; 1147 } 1148 } 1149 1150 chunks = appender!(Chunk[])(); 1151 } 1152 1153 private void indent() 1154 { 1155 foreach (x; 0 .. stack.length - 1) 1156 buffer.put(" "); 1157 } 1158 1159 private void indent(int shift) 1160 { 1161 foreach (x; 0 .. stack.length - 1 + shift) 1162 buffer.put(" "); 1163 } 1164 1165 private void indent(size_t shift) 1166 { 1167 foreach (x; 0 .. stack.length - 1 + shift) 1168 buffer.put(" "); 1169 } 1170 } 1171 1172 class StructData 1173 { 1174 string name; 1175 protected Context context; 1176 1177 Output[] instanceVariables; 1178 1179 bool isFwdDeclaration; 1180 1181 this(Context context) 1182 { 1183 this.context = context; 1184 } 1185 1186 @property Output data () 1187 { 1188 import std.format : format; 1189 1190 Output output = new Output(); 1191 1192 if (name.length) 1193 name = ' ' ~ name; 1194 1195 if (isFwdDeclaration) 1196 { 1197 isFwdDeclaration = true; 1198 output.singleLine("%s%s;", type, name); 1199 return output; 1200 } 1201 else 1202 { 1203 output.subscopeStrong("%s%s", type, name) in { 1204 addDeclarations(output, instanceVariables); 1205 }; 1206 1207 return output; 1208 } 1209 } 1210 1211 protected: 1212 1213 @property string type () 1214 { 1215 return "struct"; 1216 } 1217 1218 void addDeclarations (Output output, Output[] declarations) 1219 { 1220 foreach (i, e ; declarations) 1221 output.output(e); 1222 } 1223 } 1224 1225 class ClassData : StructData 1226 { 1227 Output[] members; 1228 1229 string name; 1230 string superclass; 1231 string[] interfaces; 1232 1233 Set!string propertyList; 1234 1235 private Set!string mangledMethods; 1236 1237 this (Context context) 1238 { 1239 super(context); 1240 } 1241 1242 string getMethodName (FunctionCursor func, string name = "", bool translateIdentifier = true) 1243 { 1244 import std.range : empty; 1245 1246 auto mangledName = mangle(func, name); 1247 auto selector = func.spelling; 1248 1249 if (!(mangledName in mangledMethods)) 1250 { 1251 mangledMethods.add(mangledName); 1252 name = name.empty ? selector : name; 1253 return translateSelector(name, false, translateIdentifier); 1254 } 1255 1256 return translateSelector(name, true, translateIdentifier); 1257 } 1258 1259 private string mangle (FunctionCursor func, string name) 1260 { 1261 import std.range : empty; 1262 auto selector = func.spelling; 1263 name = name.empty ? translateSelector(selector) : name; 1264 auto mangledName = name; 1265 1266 foreach (param ; func.parameters) 1267 mangledName ~= translateType(context, param).prefixWith("_").makeString(); 1268 1269 return mangledName; 1270 } 1271 1272 @property override Output data () 1273 { 1274 import std.format; 1275 import std.array; 1276 1277 auto header = appender!string(); 1278 1279 formattedWrite( 1280 header, 1281 "%s %s", 1282 type, 1283 name); 1284 1285 if (superclass.length) 1286 { 1287 header.put(" : "); 1288 header.put(superclass); 1289 } 1290 1291 writeInterfaces(header); 1292 1293 Output output = new Output(); 1294 1295 output.subscopeStrong(header.data) in { 1296 writeMembers(output); 1297 }; 1298 1299 return output; 1300 } 1301 1302 override protected @property string type () 1303 { 1304 return "class"; 1305 } 1306 1307 private: 1308 1309 void writeInterfaces (ref Appender!string header) 1310 { 1311 import std.range : empty; 1312 1313 if (interfaces.length) 1314 { 1315 if (superclass.empty) 1316 header.put(" : "); 1317 1318 foreach (i, s ; interfaces) 1319 { 1320 if (i != 0) 1321 header.put(", "); 1322 1323 header.put(s); 1324 } 1325 } 1326 } 1327 1328 void writeMembers (Output output) 1329 { 1330 addDeclarations(output, members); 1331 } 1332 } 1333 1334 class InterfaceData : ClassData 1335 { 1336 this(Context context) 1337 { 1338 super(context); 1339 } 1340 1341 protected @property override string type () 1342 { 1343 return "interface"; 1344 } 1345 } 1346 1347 class ClassExtensionData : ClassData 1348 { 1349 this(Context context) 1350 { 1351 super(context); 1352 } 1353 1354 protected @property override string type () 1355 { 1356 return "__classext"; 1357 } 1358 }