%% -*- Mode: erlang; indent-tabs-mode: nil -*-
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2009-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions 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).
%% Used to cause an "Unbalanced parentheses" error.
foo(M) ->
M#{a :=<<"a">>
,b:=1}.
foo() ->
#{a =><<"a">>
,b=>1}.
%% 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}).
-record(record5, { a = 1 :: integer()
, b = foobar :: atom()
}).
-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 t26() :: #rec4{ a :: integer()
, b :: any()
}.
-type t27() :: { integer()
, atom()
}.
-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()}}.
-spec add( X :: integer()
, Y :: integer()
) -> integer().
-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.