Current section
Files
Jump to
Current section
Files
src/less_server.erl
-module(less_server).
-behaviour(gen_server).
%% utils
-export([lines/0]).
%% api
-export([
start_link/1,
start_link/2,
stop/1,
page/1,
next/1,
prev/1
]).
%% gen_server
-export([
init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2
]).
-record(state, {
lines :: pos_integer(),
buf :: list(string()),
position :: non_neg_integer()
}).
-type state() :: #state{}.
%%%===================================================================
%%% api
%%%===================================================================
%%--------------------------------------------------------------------
%% @doc
-spec start_link(String :: string()) ->
{ok, pid()} | {error, {already_started, pid()}} | {error, Reason :: any()}.
%%--------------------------------------------------------------------
start_link(String) ->
gen_server:start_link(?MODULE, {String}, []).
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
%% @doc
-spec start_link(String :: string(), Lines :: pos_integer()) ->
{ok, pid()} | {error, {already_started, pid()}} | {error, Reason :: any()}.
%%--------------------------------------------------------------------
start_link(String, Lines) ->
gen_server:start_link(?MODULE, {String, Lines}, []).
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
%% @doc
-spec stop(LessServer :: pid()) ->
ok.
%%--------------------------------------------------------------------
stop(LessServer) ->
gen_server:stop(LessServer).
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
%% @doc
-spec page(LessServer :: pid()) ->
String :: string().
%%--------------------------------------------------------------------
page(LessServer) ->
gen_server:call(LessServer, {page}, infinity).
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
%% @doc
-spec next(LessServer :: pid()) ->
String :: string().
%%--------------------------------------------------------------------
next(LessServer) ->
gen_server:call(LessServer, {next}, infinity).
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
%% @doc
-spec prev(LessServer :: pid()) ->
String :: string().
%%--------------------------------------------------------------------
prev(LessServer) ->
gen_server:call(LessServer, {prev}, infinity).
%%--------------------------------------------------------------------
%%%===================================================================
%%% gen_server
%%%===================================================================
%%--------------------------------------------------------------------
%% @doc
-spec init({String :: string()} | {String :: string(), Lines :: pos_integer()}) ->
{ok, state()}.
%%--------------------------------------------------------------------
init({String}) ->
{ok, state(String)};
init({String, Lines}) ->
{ok, state(String, Lines)}.
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
%% @doc
-spec handle_call(Request :: term(), From :: {pid(), _Tag}, State :: state()) ->
{reply, Result :: ok, State2 :: state()}.
%%--------------------------------------------------------------------
handle_call({page}, _From, State) ->
{reply, page_2(State), State};
handle_call({next}, _From, State) ->
State2 = next_2(State),
{reply, page_2(State2), State2};
handle_call({prev}, _From, State) ->
State2 = prev_2(State),
{reply, page_2(State2), State2};
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
%% @doc
-spec handle_cast(Request :: term(), State :: state()) ->
{noreply, State2 :: state()}.
%%--------------------------------------------------------------------
handle_cast(_Request, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
%% @doc
-spec handle_info(Info :: term(), State :: state()) ->
{noreply, State :: state()}.
%%--------------------------------------------------------------------
handle_info(_Info, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
%% @doc
-spec terminate(Reason :: term(), State :: state()) ->
ok.
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
ok.
%%--------------------------------------------------------------------
%%%===================================================================
%%% state api
%%%===================================================================
%%--------------------------------------------------------------------
%% @doc
-spec state(String :: string()) ->
State :: state().
%%--------------------------------------------------------------------
state(String) ->
state_2(string_to_buf(String), lines()).
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
%% @doc
-spec state(String :: string(), Lines :: pos_integer()) ->
state().
%%--------------------------------------------------------------------
state(String, Lines) ->
state_2(string_to_buf(String), Lines).
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
%% @doc
-spec state_2(Buf :: list(string()), Lines :: pos_integer()) ->
state().
%%--------------------------------------------------------------------
state_2(Buf, Lines) ->
SafeLines = erlang:max(1, Lines),
#state{buf = Buf, lines = SafeLines, position = 0}.
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
%% @doc
-spec page_2(State :: state()) ->
String :: string().
%%--------------------------------------------------------------------
page_2(State) ->
observer_cli_lib:pipe(State#state.buf, [
fun(X) -> lists:sublist(X, State#state.position + 1, State#state.lines) end,
fun buf_to_string/1
]).
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
%% @doc
-spec next_2(State :: state()) ->
State2 :: state().
%%--------------------------------------------------------------------
next_2(State) ->
#state{position = Position, lines = Lines, buf = Buf} = State,
NewPosition = Position + Lines,
case NewPosition >= length(Buf) of
true -> State;
false -> State#state{position = NewPosition}
end.
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
%% @doc
-spec prev_2(State :: state()) ->
State2 :: state().
%%--------------------------------------------------------------------
prev_2(State) ->
#state{position = Position, lines = Lines} = State,
case Position =< 0 of
true ->
State;
false ->
NewPosition = Position - Lines,
case NewPosition =< 0 of
true -> State#state{position = 0};
false -> State#state{position = NewPosition}
end
end.
%%--------------------------------------------------------------------
%%%===================================================================
%%% buf
%%%===================================================================
string_to_buf(String) ->
string:split(String, "\n", all).
buf_to_string(Buf) ->
observer_cli_lib:pipe(Buf, [
fun(X) -> string:join(X, "\n") end,
fun(X) -> string:concat(X, "\n") end
]).
%%%===================================================================
%%% utils
%%%===================================================================
-spec lines() -> pos_integer().
lines() ->
case io:rows() of
{ok, Rows} -> Rows;
_ -> 43
end.