Skip to content

Commit f5b5706

Browse files
authored
Merge pull request #119 from okayno14/main_less
Added state formating with erlfmt and rendering with less
2 parents b24f9b7 + af2fa22 commit f5b5706

13 files changed

+705
-33
lines changed

docs.exs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
extras: [
33
{"README.md", title: "Home"},
44
"./docs/plugin.md",
5+
"./docs/formatter.md",
56
"./docs/CHANGELOG.md",
67
"LICENSE"
78
],

docs/formatter.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# How use your own formatter?
2+
3+
Observer CLI before rendering does formatting for:
4+
5+
* Proccess State;
6+
* Process Messages;
7+
* Process Dictionary.
8+
9+
By default it uses `io:format("~p", [])`.
10+
11+
User can change this. Observer CLI exposes behaviour `observer_cli_formatter`
12+
with callback:
13+
14+
```erlang
15+
-callback format(Pid :: pid(), Term :: term()) ->
16+
string().
17+
```
18+
So, for changing `Term` formatting we need:
19+
20+
* Write our own implementation of `observer_cli_formatter`;
21+
* Add implementation to Observer CLI configuration.
22+
23+
Default formatter config:
24+
25+
```erlang
26+
{formatter, #{application => observer_cli, mod => observer_cli_formatter_default}}
27+
```
28+
29+
**Option reference**
30+
31+
- `application` - Formatter application. Observer CLI loads all it's modules to
32+
remote node.
33+
- `mod` - Implementation of observer_cli_formatter behaviour.
34+

rebar.config

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
rebar3_ex_doc
2828
]}.
2929

30+
{shell, [
31+
{apps, [observer_cli]}
32+
]}.
33+
3034
{escript_main_app, observer_cli}.
3135
{escript_emu_args,
3236
"%%! -escript main observer_cli_escriptize -hidden +sbtu +A0 -elixir ansi_enabled true\n"}.

src/less_client.erl

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
-module(less_client).
2+
3+
-export([init/1, main/1]).
4+
5+
-include("observer_cli.hrl").
6+
7+
%%--------------------------------------------------------------------
8+
%% @doc
9+
-spec init(Input :: string()) ->
10+
LessServer :: pid().
11+
%%--------------------------------------------------------------------
12+
init(Input) ->
13+
%% We must save 1 line for status render
14+
{ok, LessServer} = less_server:start_link(Input, less_server:lines() - 1),
15+
LessServer.
16+
%%--------------------------------------------------------------------
17+
18+
%%--------------------------------------------------------------------
19+
%% @doc
20+
-spec main(LessServer :: pid()) ->
21+
ok.
22+
%%--------------------------------------------------------------------
23+
main(LessServer) ->
24+
handle_current_page(LessServer),
25+
loop(LessServer).
26+
%%--------------------------------------------------------------------
27+
28+
loop(LessServer) ->
29+
case io:get_line("") of
30+
"j\n" ->
31+
handle_next_page(LessServer),
32+
loop(LessServer);
33+
"F\n" ->
34+
handle_next_page(LessServer),
35+
loop(LessServer);
36+
"k\n" ->
37+
handle_prev_page(LessServer),
38+
loop(LessServer);
39+
"B\n" ->
40+
handle_prev_page(LessServer),
41+
loop(LessServer);
42+
"q\n" ->
43+
handle_quit(LessServer),
44+
ok;
45+
_ ->
46+
handle_current_page(LessServer),
47+
loop(LessServer)
48+
end.
49+
50+
handle_current_page(LessServer) ->
51+
handle_page(less_server:page(LessServer)).
52+
53+
handle_next_page(LessServer) ->
54+
handle_page(less_server:next(LessServer)).
55+
56+
handle_prev_page(LessServer) ->
57+
handle_page(less_server:prev(LessServer)).
58+
59+
handle_quit(LessServer) ->
60+
less_server:stop(LessServer),
61+
?output(?CLEAR).
62+
63+
handle_page(Page) ->
64+
?output(?CLEAR),
65+
?output([Page]),
66+
?output([render_last_line()]).
67+
68+
render_last_line() ->
69+
unicode:characters_to_binary([
70+
<<"|">>,
71+
?GRAY_BG,
72+
<<"q(quit) F/j(next page) B/k(previous page)">>,
73+
?RESET
74+
]).

src/less_server.erl

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
-module(less_server).
2+
-behaviour(gen_server).
3+
4+
%% utils
5+
-export([lines/0]).
6+
7+
%% api
8+
-export([
9+
start_link/1,
10+
start_link/2,
11+
stop/1,
12+
13+
page/1,
14+
next/1,
15+
prev/1
16+
]).
17+
18+
%% gen_server
19+
-export([
20+
init/1,
21+
handle_call/3,
22+
handle_cast/2,
23+
handle_info/2,
24+
terminate/2
25+
]).
26+
27+
-record(state, {
28+
lines :: pos_integer(),
29+
buf :: list(string()),
30+
position :: non_neg_integer()
31+
}).
32+
33+
-type state() :: #state{}.
34+
35+
%%%===================================================================
36+
%%% api
37+
%%%===================================================================
38+
39+
%%--------------------------------------------------------------------
40+
%% @doc
41+
-spec start_link(String :: string()) ->
42+
{ok, pid()} | {error, {already_started, pid()}} | {error, Reason :: any()}.
43+
%%--------------------------------------------------------------------
44+
start_link(String) ->
45+
gen_server:start_link(?MODULE, {String}, []).
46+
%%--------------------------------------------------------------------
47+
48+
%%--------------------------------------------------------------------
49+
%% @doc
50+
-spec start_link(String :: string(), Lines :: pos_integer()) ->
51+
{ok, pid()} | {error, {already_started, pid()}} | {error, Reason :: any()}.
52+
%%--------------------------------------------------------------------
53+
start_link(String, Lines) ->
54+
gen_server:start_link(?MODULE, {String, Lines}, []).
55+
%%--------------------------------------------------------------------
56+
57+
%%--------------------------------------------------------------------
58+
%% @doc
59+
-spec stop(LessServer :: pid()) ->
60+
ok.
61+
%%--------------------------------------------------------------------
62+
stop(LessServer) ->
63+
gen_server:stop(LessServer).
64+
%%--------------------------------------------------------------------
65+
66+
%%--------------------------------------------------------------------
67+
%% @doc
68+
-spec page(LessServer :: pid()) ->
69+
String :: string().
70+
%%--------------------------------------------------------------------
71+
page(LessServer) ->
72+
gen_server:call(LessServer, {page}, infinity).
73+
%%--------------------------------------------------------------------
74+
75+
%%--------------------------------------------------------------------
76+
%% @doc
77+
-spec next(LessServer :: pid()) ->
78+
String :: string().
79+
%%--------------------------------------------------------------------
80+
next(LessServer) ->
81+
gen_server:call(LessServer, {next}, infinity).
82+
%%--------------------------------------------------------------------
83+
84+
%%--------------------------------------------------------------------
85+
%% @doc
86+
-spec prev(LessServer :: pid()) ->
87+
String :: string().
88+
%%--------------------------------------------------------------------
89+
prev(LessServer) ->
90+
gen_server:call(LessServer, {prev}, infinity).
91+
%%--------------------------------------------------------------------
92+
93+
%%%===================================================================
94+
%%% gen_server
95+
%%%===================================================================
96+
97+
%%--------------------------------------------------------------------
98+
%% @doc
99+
-spec init({String :: string()} | {String :: string(), Lines :: pos_integer()}) ->
100+
{ok, state()}.
101+
%%--------------------------------------------------------------------
102+
init({String}) ->
103+
{ok, state(String)};
104+
init({String, Lines}) ->
105+
{ok, state(String, Lines)}.
106+
%%--------------------------------------------------------------------
107+
108+
%%--------------------------------------------------------------------
109+
%% @doc
110+
-spec handle_call(Request :: term(), From :: {pid(), _Tag}, State :: state()) ->
111+
{reply, Result :: ok, State2 :: state()}.
112+
%%--------------------------------------------------------------------
113+
handle_call({page}, _From, State) ->
114+
{reply, page_2(State), State};
115+
handle_call({next}, _From, State) ->
116+
State2 = next_2(State),
117+
{reply, page_2(State2), State2};
118+
handle_call({prev}, _From, State) ->
119+
State2 = prev_2(State),
120+
{reply, page_2(State2), State2};
121+
handle_call(_Request, _From, State) ->
122+
Reply = ok,
123+
{reply, Reply, State}.
124+
%%--------------------------------------------------------------------
125+
126+
%%--------------------------------------------------------------------
127+
%% @doc
128+
-spec handle_cast(Request :: term(), State :: state()) ->
129+
{noreply, State2 :: state()}.
130+
%%--------------------------------------------------------------------
131+
handle_cast(_Request, State) ->
132+
{noreply, State}.
133+
%%--------------------------------------------------------------------
134+
135+
%%--------------------------------------------------------------------
136+
%% @doc
137+
-spec handle_info(Info :: term(), State :: state()) ->
138+
{noreply, State :: state()}.
139+
%%--------------------------------------------------------------------
140+
handle_info(_Info, State) ->
141+
{noreply, State}.
142+
%%--------------------------------------------------------------------
143+
144+
%%--------------------------------------------------------------------
145+
%% @doc
146+
-spec terminate(Reason :: term(), State :: state()) ->
147+
ok.
148+
%%--------------------------------------------------------------------
149+
terminate(_Reason, _State) ->
150+
ok.
151+
%%--------------------------------------------------------------------
152+
153+
%%%===================================================================
154+
%%% state api
155+
%%%===================================================================
156+
157+
%%--------------------------------------------------------------------
158+
%% @doc
159+
-spec state(String :: string()) ->
160+
State :: state().
161+
%%--------------------------------------------------------------------
162+
state(String) ->
163+
state_2(string_to_buf(String), lines()).
164+
%%--------------------------------------------------------------------
165+
166+
%%--------------------------------------------------------------------
167+
%% @doc
168+
-spec state(String :: string(), Lines :: pos_integer()) ->
169+
state().
170+
%%--------------------------------------------------------------------
171+
state(String, Lines) ->
172+
state_2(string_to_buf(String), Lines).
173+
%%--------------------------------------------------------------------
174+
175+
%%--------------------------------------------------------------------
176+
%% @doc
177+
-spec state_2(Buf :: list(string()), Lines :: pos_integer()) ->
178+
state().
179+
%%--------------------------------------------------------------------
180+
state_2(Buf, Lines) ->
181+
#state{buf = Buf, lines = Lines, position = 0}.
182+
%%--------------------------------------------------------------------
183+
184+
%%--------------------------------------------------------------------
185+
%% @doc
186+
-spec page_2(State :: state()) ->
187+
String :: string().
188+
%%--------------------------------------------------------------------
189+
page_2(State) ->
190+
observer_cli_lib:pipe(State#state.buf, [
191+
fun(X) -> lists:sublist(X, State#state.position + 1, State#state.lines) end,
192+
fun buf_to_string/1
193+
]).
194+
%%--------------------------------------------------------------------
195+
196+
%%--------------------------------------------------------------------
197+
%% @doc
198+
-spec next_2(State :: state()) ->
199+
State2 :: state().
200+
%%--------------------------------------------------------------------
201+
next_2(State) ->
202+
case State#state.position + 1 of
203+
Position when Position > length(State#state.buf) - State#state.lines ->
204+
State;
205+
Position ->
206+
State#state{position = Position}
207+
end.
208+
%%--------------------------------------------------------------------
209+
210+
%%--------------------------------------------------------------------
211+
%% @doc
212+
-spec prev_2(State :: state()) ->
213+
State2 :: state().
214+
%%--------------------------------------------------------------------
215+
prev_2(State) ->
216+
case State#state.position of
217+
0 ->
218+
State;
219+
Position ->
220+
State#state{position = Position - 1}
221+
end.
222+
%%--------------------------------------------------------------------
223+
224+
%%%===================================================================
225+
%%% buf
226+
%%%===================================================================
227+
228+
string_to_buf(String) ->
229+
string:split(String, "\n", all).
230+
231+
buf_to_string(Buf) ->
232+
observer_cli_lib:pipe(Buf, [
233+
fun(X) -> string:join(X, "\n") end,
234+
fun(X) -> string:concat(X, "\n") end
235+
]).
236+
237+
%%%===================================================================
238+
%%% utils
239+
%%%===================================================================
240+
241+
lines() ->
242+
case io:rows() of
243+
{ok, Rows} -> Rows;
244+
_ -> 43
245+
end.

src/observer_cli.app.src

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
observer_cli
66
]},
77
{registered, []},
8+
{included_applications, [
9+
recon
10+
]},
811
{applications, [
912
kernel,
1013
stdlib,
@@ -23,6 +26,11 @@
2326
]},
2427
{build_tools, ["mix", "rebar3"]},
2528
{env, [
29+
% {formatter, #{
30+
% application := atom(), - Formatter application. observer_cli loads all it's modules to remote node.
31+
% mod := module() - observer_cli_formatter implementation.
32+
% }}
33+
{formatter, #{application => observer_cli, mod => observer_cli_formatter_default}}
2634
%% {scheduler_usage, enable} %% default value is 'disable', uncomment to enable by default
2735
%% {plugins, [
2836
%% module - Specific module implements plugin behavior. It's mandatory.

0 commit comments

Comments
 (0)