printer.odin 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924
  1. package odin_printer
  2. import "core:odin/ast"
  3. import "core:odin/tokenizer"
  4. import "core:strings"
  5. import "core:runtime"
  6. import "core:fmt"
  7. import "core:unicode/utf8"
  8. import "core:mem"
  9. Type_Enum :: enum {Line_Comment, Value_Decl, Switch_Stmt, Struct, Assign, Call, Enum, If, For, Proc_Lit};
  10. Line_Type :: bit_set[Type_Enum];
  11. /*
  12. Represents an unwrapped line
  13. */
  14. Line :: struct {
  15. format_tokens: [dynamic]Format_Token,
  16. finalized: bool,
  17. used: bool,
  18. depth: int,
  19. types: Line_Type, //for performance, so you don't have to verify what types are in it by going through the tokens - might give problems when adding linebreaking
  20. }
  21. /*
  22. Represents a singular token in a unwrapped line
  23. */
  24. Format_Token :: struct {
  25. kind: tokenizer.Token_Kind,
  26. text: string,
  27. type: Type_Enum,
  28. spaces_before: int,
  29. parameter_count: int,
  30. }
  31. Printer :: struct {
  32. string_builder: strings.Builder,
  33. config: Config,
  34. depth: int, //the identation depth
  35. comments: [dynamic]^ast.Comment_Group,
  36. latest_comment_index: int,
  37. allocator: mem.Allocator,
  38. file: ^ast.File,
  39. source_position: tokenizer.Pos,
  40. last_source_position: tokenizer.Pos,
  41. lines: [dynamic]Line, //need to look into a better data structure, one that can handle inserting lines rather than appending
  42. skip_semicolon: bool,
  43. current_line: ^Line,
  44. current_line_index: int,
  45. last_line_index: int,
  46. last_token: ^Format_Token,
  47. merge_next_token: bool,
  48. space_next_token: bool,
  49. debug: bool,
  50. }
  51. Config :: struct {
  52. spaces: int, //Spaces per indentation
  53. newline_limit: int, //The limit of newlines between statements and declarations.
  54. tabs: bool, //Enable or disable tabs
  55. convert_do: bool, //Convert all do statements to brace blocks
  56. semicolons: bool, //Enable semicolons
  57. split_multiple_stmts: bool,
  58. align_switch: bool,
  59. brace_style: Brace_Style,
  60. align_assignments: bool,
  61. align_structs: bool,
  62. align_style: Alignment_Style,
  63. align_enums: bool,
  64. align_length_break: int,
  65. indent_cases: bool,
  66. newline_style: Newline_Style,
  67. }
  68. Brace_Style :: enum {
  69. _1TBS,
  70. Allman,
  71. Stroustrup,
  72. K_And_R,
  73. }
  74. Block_Type :: enum {
  75. None,
  76. If_Stmt,
  77. Proc,
  78. Generic,
  79. Comp_Lit,
  80. Switch_Stmt,
  81. }
  82. Alignment_Style :: enum {
  83. Align_On_Type_And_Equals,
  84. Align_On_Colon_And_Equals,
  85. }
  86. Newline_Style :: enum {
  87. CRLF,
  88. LF,
  89. }
  90. default_style := Config {
  91. spaces = 4,
  92. newline_limit = 2,
  93. convert_do = false,
  94. semicolons = true,
  95. tabs = true,
  96. brace_style = ._1TBS,
  97. split_multiple_stmts = true,
  98. align_assignments = true,
  99. align_style = .Align_On_Type_And_Equals,
  100. indent_cases = false,
  101. align_switch = true,
  102. align_structs = true,
  103. align_enums = true,
  104. newline_style = .CRLF,
  105. align_length_break = 9,
  106. };
  107. make_printer :: proc(config: Config, allocator := context.allocator) -> Printer {
  108. return {
  109. config = config,
  110. allocator = allocator,
  111. debug = false,
  112. };
  113. }
  114. print :: proc(p: ^Printer, file: ^ast.File) -> string {
  115. p.comments = file.comments;
  116. if len(file.decls) > 0 {
  117. p.lines = make([dynamic]Line, 0, (file.decls[len(file.decls) - 1].end.line - file.decls[0].pos.line) * 2, context.temp_allocator);
  118. }
  119. set_source_position(p, file.pkg_token.pos);
  120. p.last_source_position.line = 1;
  121. set_line(p, 0);
  122. push_generic_token(p, .Package, 0);
  123. push_ident_token(p, file.pkg_name, 1);
  124. for decl in file.decls {
  125. visit_decl(p, cast(^ast.Decl)decl);
  126. }
  127. if len(p.comments) > 0 {
  128. infinite := p.comments[len(p.comments) - 1].end;
  129. infinite.offset = 9999999;
  130. push_comments(p, infinite);
  131. }
  132. fix_lines(p);
  133. builder := strings.make_builder(0, mem.megabytes(5), p.allocator);
  134. last_line := 0;
  135. newline: string;
  136. if p.config.newline_style == .LF {
  137. newline = "\n";
  138. } else {
  139. newline = "\r\n";
  140. }
  141. for line, line_index in p.lines {
  142. diff_line := line_index - last_line;
  143. for i := 0; i < diff_line; i += 1 {
  144. strings.write_string(&builder, newline);
  145. }
  146. if p.config.tabs {
  147. for i := 0; i < line.depth; i += 1 {
  148. strings.write_byte(&builder, '\t');
  149. }
  150. } else {
  151. for i := 0; i < line.depth * p.config.spaces; i += 1 {
  152. strings.write_byte(&builder, ' ');
  153. }
  154. }
  155. if p.debug {
  156. strings.write_string(&builder, fmt.tprintf("line %v: ", line_index));
  157. }
  158. for format_token in line.format_tokens {
  159. for i := 0; i < format_token.spaces_before; i += 1 {
  160. strings.write_byte(&builder, ' ');
  161. }
  162. strings.write_string(&builder, format_token.text);
  163. }
  164. last_line = line_index;
  165. }
  166. strings.write_string(&builder, newline);
  167. return strings.to_string(builder);
  168. }
  169. fix_lines :: proc(p: ^Printer) {
  170. align_var_decls(p);
  171. format_generic(p);
  172. align_comments(p); //align them last since they rely on the other alignments
  173. }
  174. format_value_decl :: proc(p: ^Printer, index: int) {
  175. eq_found := false;
  176. eq_token: Format_Token;
  177. eq_line: int;
  178. largest := 0;
  179. found_eq: for line, line_index in p.lines[index:] {
  180. for format_token in line.format_tokens {
  181. largest += len(format_token.text) + format_token.spaces_before;
  182. if format_token.kind == .Eq {
  183. eq_token = format_token;
  184. eq_line = line_index + index;
  185. eq_found = true;
  186. break found_eq;
  187. }
  188. }
  189. }
  190. if !eq_found {
  191. return;
  192. }
  193. align_next := false;
  194. //check to see if there is a binary operator in the last token(this is guaranteed by the ast visit), otherwise it's not multilined
  195. for line, line_index in p.lines[eq_line:] {
  196. if len(line.format_tokens) == 0 {
  197. break;
  198. }
  199. if align_next {
  200. line.format_tokens[0].spaces_before = largest + 1;
  201. align_next = false;
  202. }
  203. kind := find_last_token(line.format_tokens).kind;
  204. if tokenizer.Token_Kind.B_Operator_Begin < kind && kind <= tokenizer.Token_Kind.Cmp_Or {
  205. align_next = true;
  206. }
  207. if !align_next {
  208. break;
  209. }
  210. }
  211. }
  212. find_last_token :: proc(format_tokens: [dynamic]Format_Token) -> Format_Token {
  213. for i := len(format_tokens) - 1; i >= 0; i -= 1 {
  214. if format_tokens[i].kind != .Comment {
  215. return format_tokens[i];
  216. }
  217. }
  218. panic("not possible");
  219. }
  220. format_assignment :: proc(p: ^Printer, index: int) {
  221. }
  222. format_call :: proc(p: ^Printer, line_index: int, format_index: int) {
  223. paren_found := false;
  224. paren_token: Format_Token;
  225. paren_line: int;
  226. paren_token_index: int;
  227. largest := 0;
  228. found_paren: for line, i in p.lines[line_index:] {
  229. for format_token, j in line.format_tokens {
  230. largest += len(format_token.text) + format_token.spaces_before;
  231. if i == 0 && j < format_index {
  232. continue;
  233. }
  234. if format_token.kind == .Open_Paren && format_token.type == .Call {
  235. paren_token = format_token;
  236. paren_line = line_index + i;
  237. paren_found = true;
  238. paren_token_index = j;
  239. break found_paren;
  240. }
  241. }
  242. }
  243. if !paren_found {
  244. panic("Should not be possible");
  245. }
  246. paren_count := 1;
  247. done := false;
  248. for line, line_index in p.lines[paren_line:] {
  249. if len(line.format_tokens) == 0 {
  250. continue;
  251. }
  252. for format_token, i in line.format_tokens {
  253. if format_token.kind == .Comment {
  254. continue;
  255. }
  256. if line_index == 0 && i <= paren_token_index {
  257. continue;
  258. }
  259. if format_token.kind == .Open_Paren {
  260. paren_count += 1;
  261. } else if format_token.kind == .Close_Paren {
  262. paren_count -= 1;
  263. }
  264. if paren_count == 0 {
  265. done = true;
  266. }
  267. }
  268. if line_index != 0 {
  269. line.format_tokens[0].spaces_before = largest;
  270. }
  271. if done {
  272. return;
  273. }
  274. }
  275. }
  276. format_keyword_to_brace :: proc(p: ^Printer, line_index: int, format_index: int, keyword: tokenizer.Token_Kind) {
  277. keyword_found := false;
  278. keyword_token: Format_Token;
  279. keyword_line: int;
  280. largest := 0;
  281. brace_count := 0;
  282. done := false;
  283. found_keyword: for line, i in p.lines[line_index:] {
  284. for format_token in line.format_tokens {
  285. largest += len(format_token.text) + format_token.spaces_before;
  286. if format_token.kind == keyword {
  287. keyword_token = format_token;
  288. keyword_line = line_index + i;
  289. keyword_found = true;
  290. break found_keyword;
  291. }
  292. }
  293. }
  294. if !keyword_found {
  295. panic("Should not be possible");
  296. }
  297. for line, line_index in p.lines[keyword_line:] {
  298. if len(line.format_tokens) == 0 {
  299. continue;
  300. }
  301. for format_token, i in line.format_tokens {
  302. if format_token.kind == .Comment {
  303. break;
  304. } else if format_token.kind == .Undef {
  305. return;
  306. }
  307. if line_index == 0 && i <= format_index {
  308. continue;
  309. }
  310. if format_token.kind == .Open_Brace {
  311. brace_count += 1;
  312. } else if format_token.kind == .Close_Brace {
  313. brace_count -= 1;
  314. }
  315. if brace_count == 1 {
  316. done = true;
  317. }
  318. }
  319. if line_index != 0 {
  320. line.format_tokens[0].spaces_before = largest + 1;
  321. }
  322. if done {
  323. return;
  324. }
  325. }
  326. }
  327. format_generic :: proc(p: ^Printer) {
  328. next_struct_line := 0;
  329. for line, line_index in p.lines {
  330. if len(line.format_tokens) <= 0 {
  331. continue;
  332. }
  333. for format_token, token_index in line.format_tokens {
  334. #partial switch format_token.kind {
  335. case .For, .If, .When, .Switch:
  336. format_keyword_to_brace(p, line_index, token_index, format_token.kind);
  337. case .Proc:
  338. if format_token.type == .Proc_Lit {
  339. format_keyword_to_brace(p, line_index, token_index, format_token.kind);
  340. }
  341. case:
  342. if format_token.type == .Call {
  343. format_call(p, line_index, token_index);
  344. }
  345. }
  346. }
  347. if .Switch_Stmt in line.types && p.config.align_switch {
  348. align_switch_stmt(p, line_index);
  349. }
  350. if .Enum in line.types && p.config.align_enums {
  351. align_enum(p, line_index);
  352. }
  353. if .Struct in line.types && p.config.align_structs && next_struct_line <= 0 {
  354. next_struct_line = align_struct(p, line_index);
  355. }
  356. if .Value_Decl in line.types {
  357. format_value_decl(p, line_index);
  358. }
  359. if .Assign in line.types {
  360. format_assignment(p, line_index);
  361. }
  362. next_struct_line -= 1;
  363. }
  364. }
  365. align_var_decls :: proc(p: ^Printer) {
  366. current_line: int;
  367. current_typed: bool;
  368. current_not_mutable: bool;
  369. largest_lhs := 0;
  370. largest_rhs := 0;
  371. TokenAndLength :: struct {
  372. format_token: ^Format_Token,
  373. length: int,
  374. };
  375. colon_tokens := make([dynamic]TokenAndLength, 0, 10, context.temp_allocator);
  376. type_tokens := make([dynamic]TokenAndLength, 0, 10, context.temp_allocator);
  377. equal_tokens := make([dynamic]TokenAndLength, 0, 10, context.temp_allocator);
  378. for line, line_index in p.lines {
  379. //It is only possible to align value decls that are one one line, otherwise just ignore them
  380. if .Value_Decl not_in line.types {
  381. continue;
  382. }
  383. typed := true;
  384. not_mutable := false;
  385. continue_flag := false;
  386. for i := 0; i < len(line.format_tokens); i += 1 {
  387. if line.format_tokens[i].kind == .Colon && line.format_tokens[min(i + 1, len(line.format_tokens) - 1)].kind == .Eq {
  388. typed = false;
  389. }
  390. if line.format_tokens[i].kind == .Colon && line.format_tokens[min(i + 1, len(line.format_tokens) - 1)].kind == .Colon {
  391. not_mutable = true;
  392. }
  393. if line.format_tokens[i].kind == .Union ||
  394. line.format_tokens[i].kind == .Enum ||
  395. line.format_tokens[i].kind == .Struct ||
  396. line.format_tokens[i].kind == .For ||
  397. line.format_tokens[i].kind == .If ||
  398. line.format_tokens[i].kind == .Comment {
  399. continue_flag = true;
  400. }
  401. //enforced undef is always on the last line, if it exists
  402. if line.format_tokens[i].kind == .Proc && line.format_tokens[len(line.format_tokens)-1].kind != .Undef {
  403. continue_flag = true;
  404. }
  405. }
  406. if continue_flag {
  407. continue;
  408. }
  409. if line_index != current_line + 1 || typed != current_typed || not_mutable != current_not_mutable {
  410. if p.config.align_style == .Align_On_Colon_And_Equals || !current_typed || current_not_mutable {
  411. for colon_token in colon_tokens {
  412. colon_token.format_token.spaces_before = largest_lhs - colon_token.length + 1;
  413. }
  414. } else if p.config.align_style == .Align_On_Type_And_Equals {
  415. for type_token in type_tokens {
  416. type_token.format_token.spaces_before = largest_lhs - type_token.length + 1;
  417. }
  418. }
  419. if current_typed {
  420. for equal_token in equal_tokens {
  421. equal_token.format_token.spaces_before = largest_rhs - equal_token.length + 1;
  422. }
  423. } else {
  424. for equal_token in equal_tokens {
  425. equal_token.format_token.spaces_before = 0;
  426. }
  427. }
  428. clear(&colon_tokens);
  429. clear(&type_tokens);
  430. clear(&equal_tokens);
  431. largest_rhs = 0;
  432. largest_lhs = 0;
  433. current_typed = typed;
  434. current_not_mutable = not_mutable;
  435. }
  436. current_line = line_index;
  437. current_token_index := 0;
  438. lhs_length := 0;
  439. rhs_length := 0;
  440. //calcuate the length of lhs of a value decl i.e. `a, b:`
  441. for; current_token_index < len(line.format_tokens); current_token_index += 1 {
  442. lhs_length += len(line.format_tokens[current_token_index].text) + line.format_tokens[current_token_index].spaces_before;
  443. if line.format_tokens[current_token_index].kind == .Colon {
  444. append(&colon_tokens, TokenAndLength {format_token = &line.format_tokens[current_token_index], length = lhs_length});
  445. if len(line.format_tokens) > current_token_index && line.format_tokens[current_token_index + 1].kind != .Eq {
  446. append(&type_tokens, TokenAndLength {format_token = &line.format_tokens[current_token_index + 1], length = lhs_length});
  447. }
  448. current_token_index += 1;
  449. largest_lhs = max(largest_lhs, lhs_length);
  450. break;
  451. }
  452. }
  453. //calcuate the length of the rhs i.e. `[dynamic]int = 123123`
  454. for; current_token_index < len(line.format_tokens); current_token_index += 1 {
  455. rhs_length += len(line.format_tokens[current_token_index].text) + line.format_tokens[current_token_index].spaces_before;
  456. if line.format_tokens[current_token_index].kind == .Eq {
  457. append(&equal_tokens, TokenAndLength {format_token = &line.format_tokens[current_token_index], length = rhs_length});
  458. largest_rhs = max(largest_rhs, rhs_length);
  459. break;
  460. }
  461. }
  462. }
  463. //repeating myself, move to sub procedure
  464. if p.config.align_style == .Align_On_Colon_And_Equals || !current_typed || current_not_mutable {
  465. for colon_token in colon_tokens {
  466. colon_token.format_token.spaces_before = largest_lhs - colon_token.length + 1;
  467. }
  468. } else if p.config.align_style == .Align_On_Type_And_Equals {
  469. for type_token in type_tokens {
  470. type_token.format_token.spaces_before = largest_lhs - type_token.length + 1;
  471. }
  472. }
  473. if current_typed {
  474. for equal_token in equal_tokens {
  475. equal_token.format_token.spaces_before = largest_rhs - equal_token.length + 1;
  476. }
  477. } else {
  478. for equal_token in equal_tokens {
  479. equal_token.format_token.spaces_before = 0;
  480. }
  481. }
  482. }
  483. align_switch_stmt :: proc(p: ^Printer, index: int) {
  484. switch_found := false;
  485. brace_token: Format_Token;
  486. brace_line: int;
  487. found_switch_brace: for line, line_index in p.lines[index:] {
  488. for format_token in line.format_tokens {
  489. if format_token.kind == .Open_Brace && switch_found {
  490. brace_token = format_token;
  491. brace_line = line_index + index;
  492. break found_switch_brace;
  493. } else if format_token.kind == .Open_Brace {
  494. break;
  495. } else if format_token.kind == .Switch {
  496. switch_found = true;
  497. }
  498. }
  499. }
  500. if !switch_found {
  501. return;
  502. }
  503. largest := 0;
  504. case_count := 0;
  505. TokenAndLength :: struct {
  506. format_token: ^Format_Token,
  507. length: int,
  508. };
  509. format_tokens := make([dynamic]TokenAndLength, 0, brace_token.parameter_count, context.temp_allocator);
  510. //find all the switch cases that are one lined
  511. for line, line_index in p.lines[brace_line + 1:] {
  512. case_found := false;
  513. colon_found := false;
  514. length := 0;
  515. for format_token, i in line.format_tokens {
  516. if format_token.kind == .Comment {
  517. break;
  518. }
  519. //this will only happen if the case is one lined
  520. if case_found && colon_found {
  521. append(&format_tokens, TokenAndLength {format_token = &line.format_tokens[i], length = length});
  522. largest = max(length, largest);
  523. break;
  524. }
  525. if format_token.kind == .Case {
  526. case_found = true;
  527. case_count += 1;
  528. } else if format_token.kind == .Colon {
  529. colon_found = true;
  530. }
  531. length += len(format_token.text) + format_token.spaces_before;
  532. }
  533. if case_count >= brace_token.parameter_count {
  534. break;
  535. }
  536. }
  537. for token in format_tokens {
  538. token.format_token.spaces_before = largest - token.length + 1;
  539. }
  540. }
  541. align_enum :: proc(p: ^Printer, index: int) {
  542. enum_found := false;
  543. brace_token: Format_Token;
  544. brace_line: int;
  545. found_enum_brace: for line, line_index in p.lines[index:] {
  546. for format_token in line.format_tokens {
  547. if format_token.kind == .Open_Brace && enum_found {
  548. brace_token = format_token;
  549. brace_line = line_index + index;
  550. break found_enum_brace;
  551. } else if format_token.kind == .Open_Brace {
  552. break;
  553. } else if format_token.kind == .Enum {
  554. enum_found = true;
  555. }
  556. }
  557. }
  558. if !enum_found {
  559. return;
  560. }
  561. largest := 0;
  562. comma_count := 0;
  563. TokenAndLength :: struct {
  564. format_token: ^Format_Token,
  565. length: int,
  566. };
  567. format_tokens := make([dynamic]TokenAndLength, 0, brace_token.parameter_count, context.temp_allocator);
  568. for line, line_index in p.lines[brace_line + 1:] {
  569. length := 0;
  570. for format_token, i in line.format_tokens {
  571. if format_token.kind == .Comment {
  572. break;
  573. }
  574. if format_token.kind == .Eq {
  575. append(&format_tokens, TokenAndLength {format_token = &line.format_tokens[i], length = length});
  576. largest = max(length, largest);
  577. break;
  578. } else if format_token.kind == .Comma {
  579. comma_count += 1;
  580. }
  581. length += len(format_token.text) + format_token.spaces_before;
  582. }
  583. if comma_count >= brace_token.parameter_count {
  584. break;
  585. }
  586. }
  587. for token in format_tokens {
  588. token.format_token.spaces_before = largest - token.length + 1;
  589. }
  590. }
  591. align_struct :: proc(p: ^Printer, index: int) -> int {
  592. struct_found := false;
  593. brace_token: Format_Token;
  594. brace_line: int;
  595. found_struct_brace: for line, line_index in p.lines[index:] {
  596. for format_token in line.format_tokens {
  597. if format_token.kind == .Open_Brace && struct_found {
  598. brace_token = format_token;
  599. brace_line = line_index + index;
  600. break found_struct_brace;
  601. } else if format_token.kind == .Open_Brace {
  602. break;
  603. } else if format_token.kind == .Struct {
  604. struct_found = true;
  605. }
  606. }
  607. }
  608. if !struct_found {
  609. return 0;
  610. }
  611. largest := 0;
  612. colon_count := 0;
  613. nested := false;
  614. seen_brace := false;
  615. TokenAndLength :: struct {
  616. format_token: ^Format_Token,
  617. length: int,
  618. };
  619. format_tokens := make([]TokenAndLength, brace_token.parameter_count, context.temp_allocator);
  620. if brace_token.parameter_count == 0 {
  621. return 0;
  622. }
  623. end_line_index := 0;
  624. for line, line_index in p.lines[brace_line + 1:] {
  625. length := 0;
  626. for format_token, i in line.format_tokens {
  627. //give up on nested structs
  628. if format_token.kind == .Comment {
  629. break;
  630. } else if format_token.kind == .Open_Paren {
  631. break;
  632. } else if format_token.kind == .Open_Brace {
  633. seen_brace = true;
  634. } else if format_token.kind == .Close_Brace {
  635. seen_brace = false;
  636. } else if seen_brace {
  637. continue;
  638. }
  639. if format_token.kind == .Colon {
  640. format_tokens[colon_count] = {format_token = &line.format_tokens[i + 1], length = length};
  641. if format_tokens[colon_count].format_token.kind == .Struct {
  642. nested = true;
  643. }
  644. colon_count += 1;
  645. largest = max(length, largest);
  646. }
  647. length += len(format_token.text) + format_token.spaces_before;
  648. }
  649. if nested {
  650. end_line_index = line_index + brace_line + 1;
  651. }
  652. if colon_count >= brace_token.parameter_count {
  653. break;
  654. }
  655. }
  656. //give up aligning nested, it never looks good
  657. if nested {
  658. for line, line_index in p.lines[end_line_index:] {
  659. for format_token in line.format_tokens {
  660. if format_token.kind == .Close_Brace {
  661. return end_line_index + line_index - index;
  662. }
  663. }
  664. }
  665. }
  666. for token in format_tokens {
  667. token.format_token.spaces_before = largest - token.length + 1;
  668. }
  669. return 0;
  670. }
  671. align_comments :: proc(p: ^Printer) {
  672. Comment_Align_Info :: struct {
  673. length: int,
  674. begin: int,
  675. end: int,
  676. depth: int,
  677. };
  678. comment_infos := make([dynamic]Comment_Align_Info, 0, context.temp_allocator);
  679. current_info: Comment_Align_Info;
  680. for line, line_index in p.lines {
  681. if len(line.format_tokens) <= 0 {
  682. continue;
  683. }
  684. if .Line_Comment in line.types {
  685. if current_info.end + 1 != line_index || current_info.depth != line.depth ||
  686. (current_info.begin == current_info.end && current_info.length == 0) {
  687. if (current_info.begin != 0 && current_info.end != 0) || current_info.length > 0 {
  688. append(&comment_infos, current_info);
  689. }
  690. current_info.begin = line_index;
  691. current_info.end = line_index;
  692. current_info.depth = line.depth;
  693. current_info.length = 0;
  694. }
  695. length := 0;
  696. for format_token, i in line.format_tokens {
  697. if format_token.kind == .Comment {
  698. current_info.length = max(current_info.length, length);
  699. current_info.end = line_index;
  700. }
  701. length += format_token.spaces_before + len(format_token.text);
  702. }
  703. }
  704. }
  705. if (current_info.begin != 0 && current_info.end != 0) || current_info.length > 0 {
  706. append(&comment_infos, current_info);
  707. }
  708. for info in comment_infos {
  709. if info.begin == info.end || info.length == 0 {
  710. continue;
  711. }
  712. for i := info.begin; i <= info.end; i += 1 {
  713. l := p.lines[i];
  714. length := 0;
  715. for format_token, i in l.format_tokens {
  716. if format_token.kind == .Comment {
  717. if len(l.format_tokens) == 1 {
  718. l.format_tokens[i].spaces_before = info.length + 1;
  719. } else {
  720. l.format_tokens[i].spaces_before = info.length - length + 1;
  721. }
  722. }
  723. length += format_token.spaces_before + len(format_token.text);
  724. }
  725. }
  726. }
  727. }