// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/5/LICENSE

(function() {
  var mode = CodeMirror.getMode({indentUnit: 4}, "verilog");
  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }

  MT("binary_literals",
     "[number 1'b0]",
     "[number 1'b1]",
     "[number 1'bx]",
     "[number 1'bz]",
     "[number 1'bX]",
     "[number 1'bZ]",
     "[number 1'B0]",
     "[number 1'B1]",
     "[number 1'Bx]",
     "[number 1'Bz]",
     "[number 1'BX]",
     "[number 1'BZ]",
     "[number 1'b0]",
     "[number 1'b1]",
     "[number 2'b01]",
     "[number 2'bxz]",
     "[number 2'b11]",
     "[number 2'b10]",
     "[number 2'b1Z]",
     "[number 12'b0101_0101_0101]",
     "[number 1'b 0]",
     "[number 'b0101]"
  );

  MT("octal_literals",
     "[number 3'o7]",
     "[number 3'O7]",
     "[number 3'so7]",
     "[number 3'SO7]"
  );

  MT("decimal_literals",
     "[number 0]",
     "[number 1]",
     "[number 7]",
     "[number 123_456]",
     "[number 'd33]",
     "[number 8'd255]",
     "[number 8'D255]",
     "[number 8'sd255]",
     "[number 8'SD255]",
     "[number 32'd123]",
     "[number 32 'd123]",
     "[number 32 'd 123]"
  );

  MT("hex_literals",
     "[number 4'h0]",
     "[number 4'ha]",
     "[number 4'hF]",
     "[number 4'hx]",
     "[number 4'hz]",
     "[number 4'hX]",
     "[number 4'hZ]",
     "[number 32'hdc78]",
     "[number 32'hDC78]",
     "[number 32 'hDC78]",
     "[number 32'h DC78]",
     "[number 32 'h DC78]",
     "[number 32'h44x7]",
     "[number 32'hFFF?]"
  );

  MT("real_number_literals",
     "[number 1.2]",
     "[number 0.1]",
     "[number 2394.26331]",
     "[number 1.2E12]",
     "[number 1.2e12]",
     "[number 1.30e-2]",
     "[number 0.1e-0]",
     "[number 23E10]",
     "[number 29E-2]",
     "[number 236.123_763_e-12]"
  );

  MT("operators",
     "[meta ^]"
  );

  MT("keywords",
     "[keyword logic]",
     "[keyword logic] [variable foo]",
     "[keyword reg] [variable abc]"
  );

  MT("variables",
     "[variable _leading_underscore]",
     "[variable _if]",
     "[number 12] [variable foo]",
     "[variable foo] [number 14]"
  );

  MT("tick_defines",
     "[def `FOO]",
     "[def `foo]",
     "[def `FOO_bar]"
  );

  MT("system_calls",
     "[meta $display]",
     "[meta $vpi_printf]"
  );

  MT("line_comment", "[comment // Hello world]");

  // Alignment tests
  MT("align_port_map_style1",
     /**
      * mod mod(.a(a),
      *         .b(b)
      *        );
      */
     "[variable mod] [variable mod][bracket (].[variable a][bracket (][variable a][bracket )],",
     "        .[variable b][bracket (][variable b][bracket )]",
     "       [bracket )];",
     ""
  );

  MT("align_port_map_style2",
     /**
      * mod mod(
      *     .a(a),
      *     .b(b)
      * );
      */
     "[variable mod] [variable mod][bracket (]",
     "    .[variable a][bracket (][variable a][bracket )],",
     "    .[variable b][bracket (][variable b][bracket )]",
     "[bracket )];",
     ""
  );

  MT("align_assignments",
     /**
      * always @(posedge clk) begin
      *    if (rst)
      *       data_out <= 8'b0 +
      *                   8'b1;
      *    else
      *       data_out = 8'b0 +
      *                  8'b1;
      *    data_out =
      *       8'b0 + 8'b1;
      * end
      */
     "[keyword always] [def @][bracket (][keyword posedge] [variable clk][bracket )] [keyword begin]",
     "    [keyword if] [bracket (][variable rst][bracket )]",
     "        [variable data_out] [meta <=] [number 8'b0] [meta +]",
     "                    [number 8'b1];",
     "    [keyword else]",
     "        [variable data_out] [meta =] [number 8'b0] [meta +]",
     "                   [number 8'b1];",
     "    [variable data_out] [meta =] [number 8'b0] [meta +]",
     "               [number 8'b1];",
     "[keyword end]",
     ""
  );

  // Indentation tests
  MT("indent_single_statement_if",
      "[keyword if] [bracket (][variable foo][bracket )]",
      "    [keyword break];",
      ""
  );

  MT("no_indent_after_single_line_if",
      "[keyword if] [bracket (][variable foo][bracket )] [keyword break];",
      ""
  );

  MT("indent_after_if_begin_same_line",
      "[keyword if] [bracket (][variable foo][bracket )] [keyword begin]",
      "    [keyword break];",
      "    [keyword break];",
      "[keyword end]",
      ""
  );

  MT("indent_after_if_begin_next_line",
      "[keyword if] [bracket (][variable foo][bracket )]",
      "    [keyword begin]",
      "        [keyword break];",
      "        [keyword break];",
      "    [keyword end]",
      ""
  );

  MT("indent_single_statement_if_else",
      "[keyword if] [bracket (][variable foo][bracket )]",
      "    [keyword break];",
      "[keyword else]",
      "    [keyword break];",
      ""
  );

  MT("indent_if_else_begin_same_line",
      "[keyword if] [bracket (][variable foo][bracket )] [keyword begin]",
      "    [keyword break];",
      "    [keyword break];",
      "[keyword end] [keyword else] [keyword begin]",
      "    [keyword break];",
      "    [keyword break];",
      "[keyword end]",
      ""
  );

  MT("indent_if_else_begin_next_line",
      "[keyword if] [bracket (][variable foo][bracket )]",
      "    [keyword begin]",
      "        [keyword break];",
      "        [keyword break];",
      "    [keyword end]",
      "[keyword else]",
      "    [keyword begin]",
      "        [keyword break];",
      "        [keyword break];",
      "    [keyword end]",
      ""
  );

  MT("indent_if_nested_without_begin",
      "[keyword if] [bracket (][variable foo][bracket )]",
      "    [keyword if] [bracket (][variable foo][bracket )]",
      "        [keyword if] [bracket (][variable foo][bracket )]",
      "            [keyword break];",
      ""
  );

  MT("indent_case",
      "[keyword case] [bracket (][variable state][bracket )]",
      "    [variable FOO]:",
      "        [keyword break];",
      "    [variable BAR]:",
      "        [keyword break];",
      "[keyword endcase]",
      ""
  );

  MT("unindent_after_end_with_preceding_text",
      "[keyword begin]",
      "    [keyword break]; [keyword end]",
      ""
  );

  MT("export_function_one_line_does_not_indent",
     "[keyword export] [string \"DPI-C\"] [keyword function] [variable helloFromSV];",
     ""
  );

  MT("export_task_one_line_does_not_indent",
     "[keyword export] [string \"DPI-C\"] [keyword task] [variable helloFromSV];",
     ""
  );

  MT("export_function_two_lines_indents_properly",
    "[keyword export]",
    "    [string \"DPI-C\"] [keyword function] [variable helloFromSV];",
    ""
  );

  MT("export_task_two_lines_indents_properly",
    "[keyword export]",
    "    [string \"DPI-C\"] [keyword task] [variable helloFromSV];",
    ""
  );

  MT("import_function_one_line_does_not_indent",
    "[keyword import] [string \"DPI-C\"] [keyword function] [variable helloFromC];",
    ""
  );

  MT("import_task_one_line_does_not_indent",
    "[keyword import] [string \"DPI-C\"] [keyword task] [variable helloFromC];",
    ""
  );

  MT("import_package_single_line_does_not_indent",
    "[keyword import] [variable p]::[variable x];",
    "[keyword import] [variable p]::[variable y];",
    ""
  );

  MT("covergroup_with_function_indents_properly",
    "[keyword covergroup] [variable cg] [keyword with] [keyword function] [variable sample][bracket (][keyword bit] [variable b][bracket )];",
    "    [variable c] : [keyword coverpoint] [variable c];",
    "[keyword endgroup]: [variable cg]",
    ""
  );

  MT("indent_uvm_macros",
     /**
      *  `uvm_object_utils_begin(foo)
      *    `uvm_field_event(foo, UVM_ALL_ON)
      *  `uvm_object_utils_end
      */
     "[def `uvm_object_utils_begin][bracket (][variable foo][bracket )]",
     "    [def `uvm_field_event][bracket (][variable foo], [variable UVM_ALL_ON][bracket )]",
     "[def `uvm_object_utils_end]",
     ""
  );

  MT("indent_uvm_macros2",
     /**
      * `uvm_do_with(mem_read,{
      *    bar_nb == 0;
      * })
      */
     "[def `uvm_do_with][bracket (][variable mem_read],[bracket {]",
     "    [variable bar_nb] [meta ==] [number 0];",
     "[bracket })]",
     ""
  );

  MT("indent_wait_disable_fork",
     /**
      * virtual task body();
      *    repeat (20) begin
      *       fork
      *          `uvm_create_on(t,p_seq)
      *       join_none
      *    end
      *    wait fork;
      *    disable fork;
      * endtask : body
      */
     "[keyword virtual] [keyword task] [variable body][bracket ()];",
     "    [keyword repeat] [bracket (][number 20][bracket )] [keyword begin]",
     "        [keyword fork]",
     "            [def `uvm_create_on][bracket (][variable t],[variable p_seq][bracket )]",
     "        [keyword join_none]",
     "    [keyword end]",
     "    [keyword wait] [keyword fork];",
     "    [keyword disable] [keyword fork];",
     "[keyword endtask] : [variable body]",
     ""
  );

  MT("indent_typedef_class",
     /**
      * typedef class asdf;
      * typedef p p_t[];
      * typedef enum {
      *    ASDF
      * } t;
      */
     "[keyword typedef] [keyword class] [variable asdf];",
     "[keyword typedef] [variable p] [variable p_t][bracket [[]]];",
     "[keyword typedef] [keyword enum] [bracket {]",
     "    [variable ASDF]",
     "[bracket }] [variable t];",
     ""
  );

  MT("indent_case_with_macro",
     /**
      * // It should be assumed that Macros can have ';' inside, or 'begin'/'end' blocks.
      * // As such, 'case' statement should indent correctly with macros inside.
      * case(foo)
      *    ASDF : this.foo = seqNum;
      *    ABCD : `update(f)
      *    EFGH : `update(g)
      * endcase
      */
     "[keyword case][bracket (][variable foo][bracket )]",
     "    [variable ASDF] : [keyword this].[variable foo] [meta =] [variable seqNum];",
     "    [variable ABCD] : [def `update][bracket (][variable f][bracket )]",
     "    [variable EFGH] : [def `update][bracket (][variable g][bracket )]",
     "[keyword endcase]",
     ""
  );

  MT("indent_extern_function",
     /**
      * extern virtual function void do(ref packet trans);
      * extern virtual function void do2(ref packet trans);
      */
     "[keyword extern] [keyword virtual] [keyword function] [keyword void] [variable do1][bracket (][keyword ref] [variable packet] [variable trans][bracket )];",
     "[keyword extern] [keyword virtual] [keyword function] [keyword void] [variable do2][bracket (][keyword ref] [variable packet] [variable trans][bracket )];",
     ""
  );

  MT("indent_assignment",
     /**
      * for (int i=1;i < fun;i++) begin
      *    foo = 2 << asdf || 11'h35 >> abcd
      *          && 8'h6 | 1'b1;
      * end
      */
     "[keyword for] [bracket (][keyword int] [variable i][meta =][number 1];[variable i] [meta <] [variable fun];[variable i][meta ++][bracket )] [keyword begin]",
     "    [variable foo] [meta =] [number 2] [meta <<] [variable asdf] [meta ||] [number 11'h35] [meta >>] [variable abcd]",
     "          [meta &&] [number 8'h6] [meta |] [number 1'b1];",
     "[keyword end]",
     ""
  );

  MT("indent_foreach_constraint",
     /**
      * `uvm_rand_send_with(wrTlp, {
      *    length ==1;
      *    foreach (Data[i]) {
      *       payload[i] == Data[i];
      *    }
      * })
      */
     "[def `uvm_rand_send_with][bracket (][variable wrTlp], [bracket {]",
     "    [variable length] [meta ==][number 1];",
     "    [keyword foreach] [bracket (][variable Data][bracket [[][variable i][bracket ]])] [bracket {]",
     "        [variable payload][bracket [[][variable i][bracket ]]] [meta ==] [variable Data][bracket [[][variable i][bracket ]]];",
     "    [bracket }]",
     "[bracket })]",
     ""
  );

  MT("indent_compiler_directives",
     /**
      * `ifdef DUT
      * `else
      *     `ifndef FOO
      *         `define FOO
      *     `endif
      * `endif
      * `timescale 1ns/1ns
      */
     "[def `ifdef] [variable DUT]",
     "[def `else]",
     "    [def `ifndef] [variable FOO]",
     "        [def `define] [variable FOO]",
     "    [def `endif]",
     "[def `endif]",
     "[def `timescale] [number 1][variable ns][meta /][number 1][variable ns]",
     ""
  );

})();
