%% -*- erlang -*-
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%

%%%-------------------------------------------------------------------
%%% File    : test.erl
%%% Author  : Dan Gudmundsson <dan.gudmundsson@ericsson.com>
%%% Description :   Test emacs mode indention and font-locking
%%%                 this file is intentionally not indented.
%%%                 Copy the file and indent it and you should end up with test.erl.indented
%%% Created :  6 Oct 2009 by Dan Gudmundsson <dan.gudmundsson@ericsson.com>
%%%-------------------------------------------------------------------

%% Start off with syntax highlighting you have to verify this by looking here 
%% and see that the code looks alright

-module(test).
-compile(export_all).

%% Module attributes should be highlighted

-export([t/1]).
-record(record1, {a, 
 b, 
 c
}).
-record(record2, {
  a,
  b
 }).
	  
-record(record3, {a = 8#42423 bor
    8#4234,
  b = 8#5432 
		  bor 2#1010101
  c = 123 +
234,
		  d}).

-record(record4, {
 a = 8#42423 bor
   8#4234,
 b = 8#5432 
    bor 2#1010101
 c = 123 +
     234,
 d}).


-define(MACRO_1, macro).
-define(MACRO_2(_), macro).

-spec t(integer()) -> any().

-type ann() :: Var :: integer(). 
-type ann2() :: Var :: 
   'return' 
 | 'return_white_spaces' 
  | 'return_comments'
  | 'text' | ann(). 
-type paren() :: 
  (ann2()). 
-type t1() :: atom(). 
-type t2() :: [t1()]. 
-type t3(Atom) :: integer(Atom). 
-type t4() :: t3(foobar). 
-type t5() :: {t1(), t3(foo)}. 
-type t6() :: 1 | 2 | 3 | 
	'foo' | 'bar'. 
-type t7() :: []. 
-type t71() :: [_]. 
-type t8() :: {any(),none(),pid(),port(),
	       reference(),float()}. 
-type t9() :: [1|2|3|foo|bar] | 
    list(a | b | c) | t71(). 
-type t10() :: {1|2|3|foo|t9()} | {}. 
-type t11() :: 1..2. 
-type t13() :: maybe_improper_list(integer(), t11()). 
-type t14() :: [erl_scan:foo() | 
        %% Should be highlighted
	term() |
	bool() |
	byte() |
	char() |
	non_neg_integer() | nonempty_list() |
	pos_integer() |
	neg_integer() |
	number() |
	list() |
	nonempty_improper_list() | nonempty_maybe_improper_list() |
	maybe_improper_list() | string() | iolist() | byte() |
	module() |
	mfa() 	|
	node() 	|
	timeout() |
	no_return() |
	%% Should not be highlighted
	nonempty_() | nonlist() | 
 erl_scan:bar(34, 92) | t13() | m:f(integer() | <<_:_*16>>)].


-type t15() :: {binary(),<<>>,<<_:34>>,<<_:_*42>>,
		<<_:3,_:_*14>>,<<>>} | [<<>>|<<_:34>>|<<_:16>>|
<<_:3,_:_*1472>>|<<_:19,_:_*14>>| <<_:34>>|
<<_:34>>|<<_:34>>|<<_:34>>]. 
-type t16() :: fun(). 
-type t17() :: fun((...) -> paren()). 
-type t18() :: fun(() -> t17() | t16()). 
-type t19() :: fun((t18()) -> t16()) |
  fun((nonempty_maybe_improper_list('integer', any())|
      1|2|3|a|b|<<_:3,_:_*14>>|integer()) ->
nonempty_maybe_improper_list('integer', any())|
1|2|3|a|b|<<_:3,_:_*14>>|integer()). 
-type t20() :: [t19(), ...]. 
-type t21() :: tuple(). 
-type t21(A) :: A. 
-type t22() :: t21(integer()). 
-type t23() :: #rec1{}. 
-type t24() :: #rec2{a :: t23(), b :: [atom()]}. 
-type t25() :: #rec3{f123 :: [t24() | 
1|2|3|4|a|b|c|d| 
nonempty_maybe_improper_list(integer, any())]}. 
-type t99() ::
{t2(),t4(),t5(),t6(),t7(),t8(),t10(),t14(),
t15(),t20(),t21(), t22(),t25()}. 
-spec t1(FooBar :: t99()) -> t99();
(t2()) -> t2();
	(t4()) -> t4() when is_subtype(t4(), t24);
(t23()) -> t23() when is_subtype(t23(), atom()),
                      is_subtype(t23(), t14());
(t24()) -> t24() when is_subtype(t24(), atom()),
                      is_subtype(t24(), t14()),
	   is_subtype(t24(), t4()).

-spec over(I :: integer()) -> R1 :: foo:typen();
  (A :: atom()) -> R2 :: foo:atomen();
 (T :: tuple()) -> R3 :: bar:typen().

-spec mod:t2() -> any(). 

-spec handle_cast(Cast :: {'exchange', node(), [[name(),...]]} 
  | {'del_member', name(), pid()},
     #state{}) -> {'noreply', #state{}}.

-spec handle_cast(Cast :: 
   {'exchange', node(), [[name(),...]]} 
 | {'del_member', name(), pid()},
   #state{}) -> {'noreply', #state{}}.

-spec all(fun((T) -> boolean()), List :: [T]) ->
 boolean() when is_subtype(T, term()). % (*)

-spec get_closest_pid(term()) -> 
       Return :: pid()
 | {'error', {'no_process', term()}
   | {'no_such_group', term()}}.

-opaque attributes_data() :: 
[{'column', column()} | {'line', info_line()} |
 {'text', string()}] |  {line(),column()}.   
-record(r,{
	  f1 :: attributes_data(),
f222 = foo:bar(34, #rec3{}, 234234234423, 
               aassdsfsdfsdf, 2234242323) :: 
[t24() | 1|2|3|4|a|b|c|d| 
 nonempty_maybe_improper_list(integer, any())],
f333 :: [t24() | 1|2|3|4|a|b|c|d| 
	 nonempty_maybe_improper_list(integer, any())],
f3 = x:y(),
f4 = x:z() :: t99(),
f17 :: 'undefined',
f18 :: 1 | 2 | 'undefined',
f19 = 3 :: integer()|undefined,
f5 = 3 :: undefined|integer()}). 

-record(state, {
  sequence_number = 1          :: integer()
  }).


highlighting(X)			  % Function definitions should be highlighted
  when is_integer(X) ->	  % and so should `when' and `is_integer' be
 %% Highlighting
 %% Various characters (we keep an `atom' after to see that highlighting ends)
 $a,atom,			  % Characters should be marked
 "string",atom,		  % and strings
 'asdasd',atom,		  % quote should be atoms??
 'VaV',atom,
 'aVa',atom,
 '\'atom',atom,
 'atom\'',atom,
 'at\'om',atom,
 '#1',atom,

 $", atom,					% atom should be ok
 $', atom, 
 
 "string$", atom,  "string$", atom,  		% currently buggy I know...
 "string\$", atom,   				% workaround for bug above
 
 "char $in string", atom, 

 'atom$', atom,  'atom$', atom,
 'atom\$', atom,

 'char $in atom', atom,

 $[, ${, $\\, atom,
 ?MACRO_1,
 ?MACRO_2(foo),

 %% Numerical constants
 16#DD,					% AD Should not be highlighted
 32#dd,					% AD Should not be highlighted
 32#ddAB,				        % AD Should not be highlighted
 32#101,				        % AD Should not be highlighted
 32#ABTR,				        % AD Should not be highlighted

 %% Variables
 Variables = lists:foo(),
 _Variables = lists:foo(),                      % AD
 AppSpec = Xyz/2,
 Module42 = Xyz(foo, bar),
 Module:foo(),
 _Module:foo(),				% AD
 FooÅÅ = lists:reverse([tl,hd,tl,hd]),	% AD Should highlight FooÅÅ
 _FooÅÅ = 42,				% AD Should highlight _FooÅÅ

 %% Bifs
 erlang:registered(),
 registered(),
 hd(tl(tl(hd([a,b,c])))),
 erlang:anything(lists),
 %% Guards
 is_atom(foo), is_float(2.3), is_integer(32), is_number(4323.3),
 is_function(Fun), is_pid(self()), 
 not_a_guard:is_list([]),
 %% Other Types

 atom,			  % not (currently) hightlighted
 234234, 
 234.43,

 [list, are, not, higlighted],    
 {nor, is, tuple},
 ok.
    
%%%
%%%  Indentation
%%%

%%%  Left

%%   Indented

%    Right


indent_basics(X, Y, Z) 
  when X > 42,
Z < 13;
Y =:= 4711 ->
 %% comments
 % right comments
 case lists:filter(fun(_, AlongName, 
	      B, 
    C) ->
		       true
		   end, 
		   [a,v,b])
 of
 [] ->
 Y = 5 * 43,
 ok;
 [_|_] -> 
 Y = 5 * 43,
 ok
 end,
 Y,
 %% List, tuples and binaries
 [a, 
  b, c
 ],
 [ a, 
   b, c
  ],

 [
 a,
 b
],
 {a, 
  b,c
 },
 { a, 
  b,c
    },

 {
 a,
 b
 },
    
<<1:8,
  2:8
  >>,
 <<
 1:8,
 2:8
  >>,
 << 1:8,
   2:8
    >>,

 (a,
  b,
  c
 ),

    ( a,
     b,
     c
    ),

    
 (
  a,
  b,
  c
 ),

 call(2#42423 bor 
   #4234,
  2#5432,
 other_arg),
 ok;
indent_basics(Xlongname, 
     #struct{a=Foo,
     b=Bar}, 
       [X|
	Y]) ->
 testing_next_clause,
 ok;
indent_basics(					% AD added clause
 X, 						% not sure how this should look
 Y,
 Z)
 when
 X < 42, Z > 13;
 Y =:= 4711 ->
 foo;
indent_basics(X, Y, Z) when			% AD added clause
 X < 42, Z > 13;				% testing when indentation
 Y =:= 4711 ->
 foo;
indent_basics(X, Y, Z)				% AD added clause
 when						% testing when indentation
 X < 42, Z > 13;				% unsure about this one
 Y =:= 4711 ->
 foo.



indent_nested() ->
 [
 {foo, 2, "string"},
 {bar, 3, "another string"}
  ].


indent_icr(Z) -> 				% icr = if case receive
 %% If
 if Z >= 0 ->
	 X = 43 div 4,
	 foo(X);
    Z =< 10 ->
	 X = 43 div 4,
	 foo(X);
    Z == 5 orelse
    Z == 7 ->
	 X = 43 div 4,
	 foo(X);
    true ->
	 if_works
  end,
 %% Case
 case {Z, foo, bar} of
  {Z,_,_} ->
  X = 43 div 4,
  foo(X);
  {Z,_,_}	when
  Z =:= 42 ->				% AD line should be indented as a when
  X = 43 div 4,
  foo(X);
  {Z,_,_}
  when Z < 10 ->			% AD when should be indented
  X = 43 div 4,
  foo(X);
  {Z,_,_}
  when					% AD when should be indented
  Z < 10				% and the guards should follow when
  andalso				% unsure about how though
  true ->
  X = 43 div 4,
  foo(X)
 end,
 %% begin
 begin
 sune,
 X = 74234 + foo(8456) +
  345 div 43,
 ok
 end,


 %% receive
  receive 
  {Z,_,_} ->
    X = 43 div 4,
    foo(X);
  Z ->
   X = 43 div 4,
   foo(X) 
  end,
  receive
  {Z,_,_} ->
  X = 43 div 4,
  foo(X);
 Z 					% AD added clause
  when Z =:= 1 ->			% This line should be indented by 2
  X = 43 div 4,
  foo(X);
 Z when					% AD added clause
  Z =:= 2 ->				% This line should be indented by 2
  X = 43 div 4,
  foo(X);
 Z ->
    X = 43 div 4,
    foo(X) 
 after infinity ->
    foo(X),
    asd(X),
    5*43
 end,
 receive
 after 10 -> 
 foo(X),
 asd(X),
 5*43
 end,
 ok.
 
indent_fun() ->
    %% Changed fun to one indention level
Var = spawn(fun(X) 
       when X == 2;
	     X > 10 -> 
  hello,
  case Hello() of 
   true when is_atom(X) -> 
      foo;
  false ->
      bar
  end;
 (Foo) when is_atom(Foo), 
   is_integer(X) ->
  X = 6* 45,
  Y = true andalso
	  kalle
 end),
%% check EEP37 named funs
Fn1 = fun Fact(N) when N > 0 ->
   F = Fact(N-1),
          N * F;
Fact(0) ->
                 1
  end,
%% check anonymous funs too
   Fn2 = fun(0) ->
1;
   (N) ->
   N
 end,
  ok.

indent_try_catch() ->
 try
 io:format(stdout, "Parsing file ~s, ", 
 [St0#leex.xfile]),
 {ok,Line3,REAs,Actions,St3} = 
 parse_rules(Xfile, Line2, Macs, St2)
 catch
  exit:{badarg,R} ->
  foo(R),
  io:format(stdout, 
   "ERROR reason ~p~n",
    R);
  error:R 				% AD added clause
  when R =:= 42 ->			% when should be indented
  foo(R);
  error:R 				% AD added clause
  when					% when should be indented
  R =:= 42 ->				% but unsure about this (maybe 2 more)
  foo(R);
  error:R when				% AD added clause
  R =:= foo ->				% line should be 2 indented (works)
  foo(R);
  error:R ->
  foo(R),
  io:format(stdout, 
   "ERROR reason ~p~n",
    R)
 after
  foo('after'),
  file:close(Xfile)
 end;
indent_try_catch() ->
 try
   foo(bar)
 of
 X when true andalso
 kalle ->
  io:format(stdout, "Parsing file ~s, ",
  [St0#leex.xfile]),
  {ok,Line3,REAs,Actions,St3} =
  parse_rules(Xfile, Line2, Macs, St2);
 X 					% AD added clause
  when false andalso			% when should be 2 indented
  bengt ->
  gurka();
 X when					% AD added clause
  false andalso				% line should be 2 indented
  not bengt ->
  gurka();
 X ->
  io:format(stdout, "Parsing file ~s, ",
  [St0#leex.xfile]),
  {ok,Line3,REAs,Actions,St3} =
  parse_rules(Xfile, Line2, Macs, St2)
 catch
 exit:{badarg,R} ->
    foo(R),
    io:format(stdout, 
	      "ERROR reason ~p~n",
	      R);
 error:R ->
    foo(R),
    io:format(stdout, 
	      "ERROR reason ~p~n",
	      R)
 after
 foo('after'),
  file:close(Xfile),
  bar(with_long_arg,
       with_second_arg)
 end;
 indent_try_catch() ->
 try foo()
 after 
  foo(),
  bar(with_long_arg,
      with_second_arg)
 end.

indent_catch() ->
  D = B +
  float(43.1),
    
  B = catch oskar(X),
  
  A = catch (baz + 
      bax),
 catch foo(),

  C = catch B + 
     float(43.1),
 
  case catch foo(X) of
  A ->
 B
 end,

 case
  catch foo(X)
 of
 A ->
   B
  end,

  case
  foo(X)
   of
 A ->
	  catch B,
	  X
   end,

 try sune of
 _ -> foo
   catch _:_ -> baf
   end,

    try
sune
   of
 _ ->
   X = 5,
   (catch foo(X)),
   X + 10
   catch _:_ -> baf
   end,

   try
 (catch sune)
   of
 _ ->
   catch foo()  %% BUGBUG can't handle catch inside try without parentheses
   catch _:_ ->
    baf
   end,

   try
(catch exit())
   catch
_ ->
    catch baf()
   end,
 ok.

indent_binary() ->
  X = lists:foldr(fun(M) ->
    <<Ma/binary, " ">>
    end, [], A),
  A = <<X/binary, 0:8>>,
  B.


indent_comprehensions() ->
%% I don't have a good idea how we want to handle this 
%% but they are here to show how they are indented today.
Result1 = [X || 
  #record{a=X} <- lists:seq(1, 10),
  true = (X rem 2)
     ],
Result2 = [X || <<X:32,_:32>> <= <<0:512>>,
    true = (X rem 2)
       ],

Binary1 = << <<X:8>> || 
  #record{a=X} <- lists:seq(1, 10),
  true = (X rem 2)
      >>,

Binary2 = << <<X:8>> || <<X:32,_:32>> <= <<0:512>>,
  true = (X rem 2)
      >>,
ok.

%% This causes an error in earlier erlang-mode versions.
foo() ->
[#foo{
foo = foo}].

%% Record indentation
some_function_with_a_very_long_name() ->
 #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
 field1=a,
 field2=b},
 case dummy_function_with_a_very_very_long_name(x) of
 #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
 field1=a,
 field2=b} ->
 ok;
 Var = #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
 field1=a,
 field2=b} ->
 Var#'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{
 field1=a,
 field2=b};
 #xyz{
 a=1,
 b=2} ->
 ok
 end.

another_function_with_a_very_very_long_name() ->
 #rec{
 field1=1,
 field2=1}.

some_function_name_xyz(xyzzy, #some_record{
 field1=Field1,
 field2=Field2}) ->
 SomeVariable = f(#'Some-long-record-name'{
 field_a = 1,
 'inter-xyz-parameters' =
 #'Some-other-very-long-record-name'{
 field2 = Field1,
 field2 = Field2}}),
 {ok, SomeVariable}.

commas_first() ->
 {abc, [ {some_var, 1}
        , {some_other_var, 2}
        , {erlang_ftw, 9}
        , {erlang_cookie, 'cookie'}
        , {cmds,
            [ {one,   "sudo ls"}
              , {one,   "sudo ls"}
              , {two,   "sudo ls"}
              , {three, "sudo ls"}
              , {four,  "sudo ls"}
              , {three, "sudo ls"}
            ] }
        , {ssh_username, "yow"}
        , {cluster,
            [ {aaaa, [ {"10.198.55.12" , "" }
                     , {"10.198.55.13" , "" }
                     ] }
            , {bbbb, [ {"10.198.55.151", "" }
                     , {"10.198.55.123", "" }
                     , {"10.198.55.34" , "" }
                     , {"10.198.55.85" , "" }
                     , {"10.198.55.67" , "" }
                     ] }
            , {cccc, [ {"10.198.55.68" , "" }
                     , {"10.198.55.69" , "" }
                     ] }
            ] }
]
}.


%% this used to result in a scan-sexp error
[{
}].

%% this used to result in 2x the correct indentation within the function
%% body, due to the function name being mistaken for a keyword
catcher(N) ->
try generate_exception(N) of
Val -> {N, normal, Val}
catch
throw:X -> {N, caught, thrown, X};
exit:X -> {N, caught, exited, X};
error:X -> {N, caught, error, X}
end.
