Current section

Files

Jump to
spectator src spectator@internal@components@processes_live.erl
Raw

src/spectator@internal@components@processes_live.erl

-module(spectator@internal@components@processes_live).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([render/10, app/0]).
-export_type([model/0, msg/0]).
-type model() :: {model,
gleam@option:option(gleam@erlang@process:subject(msg())),
list(spectator@internal@api:process_item()),
spectator@internal@api:info_sort_criteria(),
spectator@internal@api:sort_direction(),
gleam@option:option(spectator@internal@api:process_item()),
gleam@option:option(spectator@internal@api:process_details()),
gleam@option:option(spectator@internal@api:status()),
gleam@option:option(gleam@dynamic:dynamic_())}.
-opaque msg() :: refresh |
{received_otp_details, spectator@internal@api:otp_details()} |
{created_subject, gleam@erlang@process:subject(msg())} |
{process_clicked, spectator@internal@api:process_item()} |
{heading_clicked, spectator@internal@api:info_sort_criteria()} |
{otp_state_clicked,
spectator@internal@api:process_item(),
spectator@internal@api:sys_state()}.
-file("/Users/jonas/Projects/spectator/src/spectator/internal/components/processes_live.gleam", 40).
-spec emit_after(
integer(),
msg(),
gleam@option:option(gleam@erlang@process:subject(msg()))
) -> lustre@effect:effect(msg()).
emit_after(Delay, Msg, Subject) ->
case Subject of
{some, Self} ->
lustre@effect:from(
fun(_) ->
_ = gleam@erlang@process:send_after(Self, Delay, Msg),
nil
end
);
none ->
lustre@server_component:select(
fun(Dispatch, Subject@1) ->
Selector = begin
_pipe = gleam_erlang_ffi:new_selector(),
gleam@erlang@process:selecting(
_pipe,
Subject@1,
fun(Msg@1) -> Msg@1 end
)
end,
_ = gleam@erlang@process:send_after(Subject@1, Delay, Msg),
Dispatch({created_subject, Subject@1}),
Selector
end
)
end.
-file("/Users/jonas/Projects/spectator/src/spectator/internal/components/processes_live.gleam", 62).
-spec request_otp_details(
gleam@erlang@process:pid_(),
gleam@option:option(gleam@erlang@process:subject(msg()))
) -> lustre@effect:effect(msg()).
request_otp_details(Pid, Subject) ->
case Subject of
{some, Sub} ->
lustre@effect:from(
fun(_) ->
spectator@internal@api:request_otp_data(
Pid,
fun(Details) ->
gleam@erlang@process:send(
Sub,
{received_otp_details, Details}
)
end
),
nil
end
);
none ->
lustre@effect:none()
end.
-file("/Users/jonas/Projects/spectator/src/spectator/internal/components/processes_live.gleam", 78).
-spec init(any()) -> {model(), lustre@effect:effect(msg())}.
init(_) ->
Info = spectator@internal@api:get_info_list(),
Default_sort_criteria = reductions,
Default_sort_direction = descending,
Sorted = spectator@internal@api:sort_info_list(
Info,
Default_sort_criteria,
descending
),
{{model,
none,
Sorted,
Default_sort_criteria,
Default_sort_direction,
none,
none,
none,
none},
emit_after(500, refresh, none)}.
-file("/Users/jonas/Projects/spectator/src/spectator/internal/components/processes_live.gleam", 109).
-spec do_refresh(model()) -> model().
do_refresh(Model) ->
Info = spectator@internal@api:get_info_list(),
Sorted = spectator@internal@api:sort_info_list(
Info,
erlang:element(4, Model),
erlang:element(5, Model)
),
Active_process@1 = case erlang:element(6, Model) of
none ->
none;
{some, Active_process} ->
case spectator_ffi:get_info(erlang:element(2, Active_process)) of
{ok, Info@1} ->
{some,
{process_item,
erlang:element(2, Active_process),
Info@1}};
{error, _} ->
none
end
end,
Details@1 = case Active_process@1 of
none ->
none;
{some, Ap} ->
case spectator_ffi:get_details(erlang:element(2, Ap)) of
{ok, Details} ->
{some, Details};
{error, _} ->
none
end
end,
erlang:setelement(
7,
erlang:setelement(
3,
erlang:setelement(6, Model, Active_process@1),
Sorted
),
Details@1
).
-file("/Users/jonas/Projects/spectator/src/spectator/internal/components/processes_live.gleam", 136).
-spec update(model(), msg()) -> {model(), lustre@effect:effect(msg())}.
update(Model, Msg) ->
case Msg of
refresh ->
New_model = do_refresh(Model),
case erlang:element(6, New_model) of
{some, P} when erlang:element(6, erlang:element(3, P)) < 10 ->
{New_model,
lustre@effect:batch(
[request_otp_details(
erlang:element(2, P),
erlang:element(2, Model)
),
emit_after(500, refresh, none)]
)};
_ ->
{New_model, emit_after(500, refresh, none)}
end;
{created_subject, Subject} ->
{erlang:setelement(2, Model, {some, Subject}), lustre@effect:none()};
{process_clicked, P@1} ->
New_model@1 = begin
_pipe = erlang:setelement(
8,
erlang:setelement(
9,
erlang:setelement(6, Model, {some, P@1}),
none
),
none
),
do_refresh(_pipe)
end,
{New_model@1,
request_otp_details(
erlang:element(2, P@1),
erlang:element(2, Model)
)};
{heading_clicked, Criteria} ->
case Criteria of
C when C =:= erlang:element(4, Model) ->
New_model@2 = erlang:setelement(
5,
Model,
spectator@internal@api:invert_sort_direction(
erlang:element(5, Model)
)
),
{do_refresh(New_model@2), lustre@effect:none()};
C@1 ->
New_model@3 = erlang:setelement(4, Model, C@1),
{do_refresh(New_model@3), lustre@effect:none()}
end;
{received_otp_details, Details} ->
case erlang:element(6, Model) of
{some, P@2} when erlang:element(2, P@2) =:= erlang:element(
2,
Details
) ->
{erlang:setelement(
8,
erlang:setelement(
9,
Model,
{some, erlang:element(4, Details)}
),
{some, erlang:element(3, Details)}
),
lustre@effect:none()};
_ ->
{Model, lustre@effect:none()}
end;
{otp_state_clicked, P@3, Target_sys_state} ->
case Target_sys_state of
suspended ->
sys:resume(erlang:element(2, P@3)),
{do_refresh(Model),
request_otp_details(
erlang:element(2, P@3),
erlang:element(2, Model)
)};
running ->
sys:suspend(erlang:element(2, P@3)),
{do_refresh(Model),
request_otp_details(
erlang:element(2, P@3),
erlang:element(2, Model)
)}
end
end.
-file("/Users/jonas/Projects/spectator/src/spectator/internal/components/processes_live.gleam", 227).
-spec render_name(spectator@internal@api:process_item()) -> lustre@internals@vdom:element(any()).
render_name(Process) ->
case erlang:element(4, erlang:element(3, Process)) of
none ->
spectator@internal@views@display:pid(erlang:element(2, Process));
{some, Name} ->
lustre@element@html:text(erlang:atom_to_binary(Name))
end.
-file("/Users/jonas/Projects/spectator/src/spectator/internal/components/processes_live.gleam", 234).
-spec render_tag(spectator@internal@api:process_item()) -> lustre@internals@vdom:element(any()).
render_tag(Process) ->
case erlang:element(8, erlang:element(3, Process)) of
none ->
spectator@internal@views@display:function(
erlang:element(3, erlang:element(3, Process))
);
{some, <<"__spectator_internal "/utf8, Rest/binary>>} ->
lustre@element@html:text(<<"🔍 "/utf8, Rest/binary>>);
{some, Tag} ->
lustre@element@html:text(<<"🔖 "/utf8, Tag/binary>>)
end.
-file("/Users/jonas/Projects/spectator/src/spectator/internal/components/processes_live.gleam", 242).
-spec render_primitive_list(list(spectator@internal@api:system_primitive())) -> list(lustre@internals@vdom:element(any())).
render_primitive_list(Primitives) ->
_pipe = gleam@list:map(
Primitives,
fun spectator@internal@views@display:system_primitive/1
),
gleam@list:intersperse(_pipe, lustre@element@html:text(<<", "/utf8>>)).
-file("/Users/jonas/Projects/spectator/src/spectator/internal/components/processes_live.gleam", 247).
-spec render_details(
gleam@option:option(spectator@internal@api:process_item()),
gleam@option:option(spectator@internal@api:process_details()),
gleam@option:option(spectator@internal@api:status()),
gleam@option:option(gleam@dynamic:dynamic_()),
fun((spectator@internal@api:process_item(), spectator@internal@api:sys_state()) -> AQFS)
) -> lustre@internals@vdom:element(AQFS).
render_details(P, D, Status, State, Handle_otp_state_click) ->
case {P, D} of
{{some, Proc}, {some, Details}} ->
lustre@element@html:'div'(
[lustre@attribute:class(<<"details"/utf8>>)],
[lustre@element@html:'div'(
[lustre@attribute:class(<<"general"/utf8>>)],
[lustre@element@html:'div'(
[lustre@attribute:class(
<<"panel-heading"/utf8>>
)],
[case erlang:element(8, erlang:element(3, Proc)) of
{some,
<<"__spectator_internal "/utf8, Rest/binary>>} ->
lustre@element@html:text(
<<"🔍 Spectator "/utf8,
Rest/binary>>
);
{some, Tag} ->
lustre@element@html:text(
<<<<"🔖 "/utf8, Tag/binary>>/binary,
" details"/utf8>>
);
none ->
lustre@element@html:text(
<<"🎛️ Process Details"/utf8>>
)
end]
),
lustre@element@html:'div'(
[lustre@attribute:class(
<<"panel-content"/utf8>>
)],
[lustre@element@html:dl(
[],
[lustre@element@html:dt(
[],
[lustre@element@html:text(
<<"Process Id"/utf8>>
)]
),
lustre@element@html:dd(
[],
[spectator@internal@views@display:pid(
erlang:element(2, Proc)
)]
)]
),
case erlang:element(
8,
erlang:element(3, Proc)
) of
none ->
lustre@element@html:text(
<<""/utf8>>
);
{some, Tag@1} ->
lustre@element@html:dl(
[],
[lustre@element@html:dt(
[],
[lustre@element@html:text(
<<"Tag"/utf8>>
)]
),
lustre@element@html:dd(
[],
[lustre@element@html:text(
Tag@1
)]
)]
)
end,
case erlang:element(
4,
erlang:element(3, Proc)
) of
none ->
lustre@element@html:text(
<<""/utf8>>
);
{some, Name} ->
lustre@element@html:dl(
[],
[lustre@element@html:dt(
[],
[lustre@element@html:text(
<<"Registered Name"/utf8>>
)]
),
lustre@element@html:dd(
[],
[lustre@element@html:text(
erlang:atom_to_binary(
Name
)
)]
)]
)
end,
lustre@element@html:dl(
[],
[lustre@element@html:dt(
[],
[lustre@element@html:text(
<<"Status"/utf8>>
)]
),
lustre@element@html:dd(
[],
[lustre@element@html:text(
erlang:atom_to_binary(
erlang:element(
9,
erlang:element(
3,
Proc
)
)
)
)]
)]
),
lustre@element@html:dl(
[],
[lustre@element@html:dt(
[],
[lustre@element@html:text(
<<"Links"/utf8>>
)]
),
lustre@element@html:dd(
[],
render_primitive_list(
erlang:element(3, Details)
)
)]
),
lustre@element@html:dl(
[],
[lustre@element@html:dt(
[],
[lustre@element@html:text(
<<"Monitored By"/utf8>>
)]
),
lustre@element@html:dd(
[],
render_primitive_list(
erlang:element(4, Details)
)
)]
),
lustre@element@html:dl(
[],
[lustre@element@html:dt(
[],
[lustre@element@html:text(
<<"Monitors"/utf8>>
)]
),
lustre@element@html:dd(
[],
render_primitive_list(
erlang:element(5, Details)
)
)]
),
lustre@element@html:dl(
[],
[lustre@element@html:dt(
[],
[lustre@element@html:text(
<<"Parent"/utf8>>
)]
),
lustre@element@html:dd(
[],
[case erlang:element(7, Details) of
none ->
lustre@element@html:text(
<<"None"/utf8>>
);
{some, Pid} ->
spectator@internal@views@display:pid(
Pid
)
end]
)]
),
lustre@element@html:dl(
[],
[lustre@element@html:dt(
[],
[lustre@element@html:text(
<<"Trap Exit"/utf8>>
)]
),
lustre@element@html:dd(
[],
[spectator@internal@views@display:bool(
erlang:element(
6,
Details
)
)]
)]
),
lustre@element@html:dl(
[],
[lustre@element@html:dt(
[],
[lustre@element@html:text(
<<"Initial Call"/utf8>>
)]
),
lustre@element@html:dd(
[],
[spectator@internal@views@display:function(
erlang:element(
3,
erlang:element(
3,
Proc
)
)
)]
)]
),
lustre@element@html:dl(
[],
[lustre@element@html:dt(
[],
[lustre@element@html:text(
<<"Current Function"/utf8>>
)]
),
lustre@element@html:dd(
[],
[spectator@internal@views@display:function(
erlang:element(
2,
erlang:element(
3,
Proc
)
)
)]
)]
),
lustre@element@html:dl(
[],
[lustre@element@html:dt(
[],
[lustre@element@html:text(
<<"Reductions"/utf8>>
)]
),
lustre@element@html:dd(
[],
[spectator@internal@views@display:number(
erlang:element(
7,
erlang:element(
3,
Proc
)
)
)]
)]
),
lustre@element@html:dl(
[],
[lustre@element@html:dt(
[],
[lustre@element@html:text(
<<"Memory"/utf8>>
)]
),
lustre@element@html:dd(
[],
[spectator@internal@views@display:storage_detailed(
erlang:element(
5,
erlang:element(
3,
Proc
)
)
)]
)]
),
lustre@element@html:dl(
[],
[lustre@element@html:dt(
[],
[lustre@element@html:text(
<<"Message Queue Length"/utf8>>
)]
),
lustre@element@html:dd(
[],
[spectator@internal@views@display:number(
erlang:element(
6,
erlang:element(
3,
Proc
)
)
)]
)]
)]
)]
),
lustre@element@html:'div'(
[lustre@attribute:class(<<"otp"/utf8>>)],
case {Status, State} of
{{some, Status@1}, {some, State@1}} ->
[lustre@element@html:'div'(
[lustre@attribute:class(
<<"panel-heading"/utf8>>
)],
[lustre@element@html:strong(
[],
[lustre@element@html:text(
<<"☎️ OTP Process: "/utf8>>
)]
),
spectator@internal@views@display:atom(
erlang:element(3, Status@1)
),
case erlang:element(5, Status@1) of
suspended ->
lustre@element@html:button(
[lustre@event:on_click(
Handle_otp_state_click(
Proc,
erlang:element(
5,
Status@1
)
)
),
lustre@attribute:class(
<<"otp-toggle resume"/utf8>>
)],
[lustre@element@html:text(
<<"🏃‍♀️‍➡️ Resume"/utf8>>
)]
);
running ->
lustre@element@html:button(
[lustre@event:on_click(
Handle_otp_state_click(
Proc,
erlang:element(
5,
Status@1
)
)
),
lustre@attribute:class(
<<"otp-toggle suspend"/utf8>>
)],
[lustre@element@html:text(
<<"✋ Suspend"/utf8>>
)]
)
end]
),
lustre@element@html:'div'(
[lustre@attribute:class(
<<"panel-content"/utf8>>
)],
[lustre@element@html:pre(
[],
[lustre@element@html:text(
pprint:format(State@1)
)]
)]
)];
{_, _} when erlang:element(
6,
erlang:element(3, Proc)
) >= 10 ->
[lustre@element@html:'div'(
[lustre@attribute:class(
<<"panel-content"/utf8>>
)],
[lustre@element@html:strong(
[],
[lustre@element@html:text(
<<"⚠️ Spectator has stopped trying to inspect the OTP state of this process"/utf8>>
)]
),
lustre@element@html:p(
[],
[lustre@element@html:text(
<<"The message queue length is above the threshold of "/utf8>>
),
spectator@internal@views@display:number(
10
),
lustre@element@html:text(
<<"."/utf8>>
),
lustre@element@html:br([]),
lustre@element@html:text(
<<"Spectator will not send it any more system messages to avoid filling the message queue."/utf8>>
)]
)]
)];
{_, _} ->
[lustre@element@html:'div'(
[lustre@attribute:class(
<<"panel-content"/utf8>>
)],
[lustre@element@html:text(
<<"This process does not appear to be OTP-compliant"/utf8>>
)]
)]
end
)]
);
{_, _} ->
lustre@element@html:'div'(
[lustre@attribute:class(<<"otp-placeholder"/utf8>>)],
[lustre@element@html:text(
<<"Click a process to see details"/utf8>>
)]
)
end.
-file("/Users/jonas/Projects/spectator/src/spectator/internal/components/processes_live.gleam", 408).
-spec classify_selected(
spectator@internal@api:process_item(),
gleam@option:option(spectator@internal@api:process_item())
) -> lustre@internals@vdom:attribute(any()).
classify_selected(Process, Active) ->
Selection_status = case Active of
{some, Active@1} when erlang:element(2, Active@1) =:= erlang:element(
2,
Process
) ->
<<"selected"/utf8>>;
_ ->
<<""/utf8>>
end,
case erlang:element(8, erlang:element(3, Process)) of
none ->
lustre@attribute:class(Selection_status);
{some, <<"__spectator_internal "/utf8, _/binary>>} ->
lustre@attribute:class(
<<Selection_status/binary, " spectator-tagged"/utf8>>
);
{some, _} ->
lustre@attribute:class(<<Selection_status/binary, " tagged"/utf8>>)
end.
-file("/Users/jonas/Projects/spectator/src/spectator/internal/components/processes_live.gleam", 421).
-spec render(
list(spectator@internal@api:process_item()),
fun((spectator@internal@api:process_item()) -> AQFX),
gleam@option:option(spectator@internal@api:process_item()),
gleam@option:option(spectator@internal@api:process_details()),
gleam@option:option(spectator@internal@api:status()),
gleam@option:option(gleam@dynamic:dynamic_()),
spectator@internal@api:info_sort_criteria(),
spectator@internal@api:sort_direction(),
fun((spectator@internal@api:info_sort_criteria()) -> AQFX),
fun((spectator@internal@api:process_item(), spectator@internal@api:sys_state()) -> AQFX)
) -> lustre@internals@vdom:element(AQFX).
render(
Processes,
Handle_process_click,
Active,
Details,
Status,
State,
Sort_criteria,
Sort_direction,
Handle_heading_click,
Handle_otp_state_click
) ->
lustre@element@html:table(
[],
[lustre@element@html:thead(
[],
[lustre@element@html:tr(
[],
[spectator@internal@views@table:heading(
<<"Name"/utf8>>,
<<"Process PID or registered name"/utf8>>,
name,
Sort_criteria,
Sort_direction,
Handle_heading_click,
false
),
spectator@internal@views@table:heading(
<<"Tag"/utf8>>,
<<"Spectator tag or initial call"/utf8>>,
tag,
Sort_criteria,
Sort_direction,
Handle_heading_click,
false
),
spectator@internal@views@table:heading(
<<"Current"/utf8>>,
<<"Current function"/utf8>>,
current_function,
Sort_criteria,
Sort_direction,
Handle_heading_click,
false
),
spectator@internal@views@table:heading(
<<"Reductions"/utf8>>,
<<"Number of reductions"/utf8>>,
reductions,
Sort_criteria,
Sort_direction,
Handle_heading_click,
false
),
spectator@internal@views@table:heading(
<<"Memory"/utf8>>,
<<"Memory usage"/utf8>>,
memory,
Sort_criteria,
Sort_direction,
Handle_heading_click,
true
),
spectator@internal@views@table:heading(
<<"Msgs"/utf8>>,
<<"Message queue size"/utf8>>,
message_queue,
Sort_criteria,
Sort_direction,
Handle_heading_click,
true
),
spectator@internal@views@table:heading(
<<"Status"/utf8>>,
<<"Process Status"/utf8>>,
process_status,
Sort_criteria,
Sort_direction,
Handle_heading_click,
false
)]
)]
),
lustre@element@html:tbody(
[],
spectator@internal@views@table:map_rows(
Processes,
fun(Process) ->
lustre@element@html:tr(
[lustre@attribute:role(<<"button"/utf8>>),
classify_selected(Process, Active),
lustre@event:on_click(
Handle_process_click(Process)
)],
[lustre@element@html:td([], [render_name(Process)]),
lustre@element@html:td(
[],
[render_tag(Process)]
),
lustre@element@html:td(
[],
[spectator@internal@views@display:function(
erlang:element(
2,
erlang:element(3, Process)
)
)]
),
lustre@element@html:td(
[],
[spectator@internal@views@display:number(
erlang:element(
7,
erlang:element(3, Process)
)
)]
),
lustre@element@html:td(
[lustre@attribute:class(
<<"cell-right"/utf8>>
)],
[spectator@internal@views@display:storage(
erlang:element(
5,
erlang:element(3, Process)
)
)]
),
lustre@element@html:td(
[lustre@attribute:class(
<<"cell-right"/utf8>>
)],
[spectator@internal@views@display:number(
erlang:element(
6,
erlang:element(3, Process)
)
)]
),
lustre@element@html:td(
[],
[spectator@internal@views@display:atom(
erlang:element(
9,
erlang:element(3, Process)
)
)]
)]
)
end
)
),
lustre@element@html:tfoot(
[],
[lustre@element@html:tr(
[],
[lustre@element@html:td(
[lustre@attribute:attribute(
<<"colspan"/utf8>>,
<<"7"/utf8>>
)],
[render_details(
Active,
Details,
Status,
State,
Handle_otp_state_click
)]
)]
)]
)]
).
-file("/Users/jonas/Projects/spectator/src/spectator/internal/components/processes_live.gleam", 212).
-spec view(model()) -> lustre@internals@vdom:element(msg()).
view(Model) ->
render(
erlang:element(3, Model),
fun(Field@0) -> {process_clicked, Field@0} end,
erlang:element(6, Model),
erlang:element(7, Model),
erlang:element(8, Model),
erlang:element(9, Model),
erlang:element(4, Model),
erlang:element(5, Model),
fun(Field@0) -> {heading_clicked, Field@0} end,
fun(Field@0, Field@1) -> {otp_state_clicked, Field@0, Field@1} end
).
-file("/Users/jonas/Projects/spectator/src/spectator/internal/components/processes_live.gleam", 21).
-spec app() -> lustre:app(any(), model(), msg()).
app() ->
lustre:application(fun init/1, fun update/2, fun view/1).