Skip to content

Commit

Permalink
feat: inspector
Browse files Browse the repository at this point in the history
  • Loading branch information
mhanberg committed Nov 29, 2023
1 parent e871f34 commit fe9f9d0
Show file tree
Hide file tree
Showing 20 changed files with 780 additions and 9 deletions.
2 changes: 1 addition & 1 deletion .formatter.exs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
request: 2
],
line_length: 120,
import_deps: [:gen_lsp],
import_deps: [:gen_lsp, :plug, :temple],
plugins: [Styler],
inputs: [
".formatter.exs",
Expand Down
14 changes: 14 additions & 0 deletions assets/css/app.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";

@import url('https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&family=Rubik:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap');

pre {
@apply bg-zinc-500 text-white rounded px-2 text-sm;
}

h1, h2, h3, h4, h5 {
@apply font-fancy font-semibold;
}
17 changes: 17 additions & 0 deletions assets/tailwind.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// See the Tailwind configuration guide for advanced usage
// https://tailwindcss.com/docs/configuration

const defaultTheme = require("tailwindcss/defaultTheme");

module.exports = {
content: ["./js/**/*.js", "./lib/**/*.ex"],
theme: {
extend: {
fontFamily: {
sans: ['"Inter"', ...defaultTheme.fontFamily.sans],
fancy: ['"Rubik"', ...defaultTheme.fontFamily.sans],
},
},
},
plugins: [],
};
23 changes: 23 additions & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@ import Config

config :next_ls, :indexing_timeout, 100

config :temple,
engine: EEx.SmartEngine,
attributes: {Temple, :attributes}

config :tailwind,
version: "3.3.2",
default: [
args: ~w(
--config=assets/tailwind.config.js
--input=assets/css/app.css
--output=priv/css/site.css
)
]

# config :logger, :default_handler, config: [type: :standard_error]
config :logger, :default_handler,
config: [
file: ~c".elixir-tools/next-ls.log",
Expand All @@ -14,4 +29,12 @@ config :logger, :default_handler,

config :logger, :default_formatter, format: "\n$time $metadata[$level] $message\n", metadata: [:id]

config :next_ls, :logger, [
{:handler, :ui_logger, NextLS.UI.Logger,
%{
config: %{},
formatter: Logger.Formatter.new()
}}
]

import_config "#{config_env()}.exs"
5 changes: 5 additions & 0 deletions config/dev.exs
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
import Config

config :next_ls, :assets, tailwind: {Tailwind, :install_and_run, [:default, ~w(--watch)]}

config :web_dev_utils, :reload_url, "'wss://' + location.host + '/ws'"
config :web_dev_utils, :reload_log, true
34 changes: 31 additions & 3 deletions lib/next_ls.ex
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,22 @@ defmodule NextLS do
task_supervisor = Keyword.fetch!(args, :task_supervisor)
runtime_task_supervisor = Keyword.fetch!(args, :runtime_task_supervisor)
dynamic_supervisor = Keyword.fetch!(args, :dynamic_supervisor)

registry = Keyword.fetch!(args, :registry)

extensions = Keyword.get(args, :extensions, elixir: NextLS.ElixirExtension, credo: NextLS.CredoExtension)
cache = Keyword.fetch!(args, :cache)

{:ok, logger} = DynamicSupervisor.start_child(dynamic_supervisor, {NextLS.Logger, lsp: lsp})

{:ok, ui} =
DynamicSupervisor.start_child(
dynamic_supervisor,
{Bandit,
[
plug: {NextLS.UI.Router, registry: registry},
port: "NEXTLS_UI_PORT" |> System.get_env("0") |> String.to_integer()
]}
)

{:ok,
assign(lsp,
auto_update: Keyword.get(args, :auto_update, false),
Expand All @@ -85,7 +94,8 @@ defmodule NextLS do
registry: registry,
extensions: extensions,
ready: false,
client_capabilities: nil
client_capabilities: nil,
ui: ui
)}
end

Expand Down Expand Up @@ -127,6 +137,7 @@ defmodule NextLS do
nil
end,
document_formatting_provider: true,
execute_command_provider: %GenLSP.Structures.ExecuteCommandOptions{commands: ["open-ui"]},
hover_provider: true,
workspace_symbol_provider: true,
document_symbol_provider: true,
Expand Down Expand Up @@ -572,6 +583,23 @@ defmodule NextLS do
{:reply, [], lsp}
end

def handle_request(
%GenLSP.Requests.WorkspaceExecuteCommand{params: %GenLSP.Structures.ExecuteCommandParams{command: command}},
lsp
) do
{:ok, {_, port}} = ThousandIsland.listener_info(lsp.assigns.ui)

case command do
"open-ui" ->
System.cmd("open", ["http://localhost:#{port}"])

_ ->
NextLS.Logger.warning(lsp.logger, "[Next LS] Unknown workspace command: #{command}")
end

{:reply, nil, lsp}
end

def handle_request(%Shutdown{}, lsp) do
{:reply, nil, assign(lsp, exit_code: 0)}
end
Expand Down
15 changes: 13 additions & 2 deletions lib/next_ls/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,22 @@ defmodule NextLS.Application do

Node.start(:"next-ls-#{System.system_time()}", :shortnames)

children = [NextLS.LSPSupervisor]
children = [
{Registry, name: NextLS.UI.Registry, keys: :duplicate},
WebDevUtils.FileSystem,
WebDevUtils.CodeReloader,
NextLS.LSPSupervisor
]

# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: NextLS.Supervisor]
Supervisor.start_link(children, opts)
Supervisor.start_link(children ++ asset_children(), opts)
end

def asset_children do
for conf <- Application.get_env(:next_ls, :assets, []) do
{WebDevUtils.Assets, conf}
end
end
end
20 changes: 19 additions & 1 deletion lib/next_ls/db/activity.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@ defmodule NextLS.DB.Activity do
:gen_statem.start_link({:local, Keyword.get(args, :name)}, __MODULE__, Keyword.drop(args, [:name]), [])
end

def update(statem, count), do: :gen_statem.cast(statem, count)
def update(statem, count, time \\ DateTime.utc_now() |> DateTime.to_unix(:millisecond)) do
Registry.dispatch(NextLS.UI.Registry, :activity_socket, fn entries ->
for {pid, _} <- entries, do: send(pid, {:activity, count, time})
end)

:gen_statem.cast(statem, count)
end

@impl :gen_statem
def callback_mode, do: :state_functions
Expand Down Expand Up @@ -44,6 +50,10 @@ defmodule NextLS.DB.Activity do
{:next_state, :waiting, %{data | token: nil}}
end

# def active(event, msg, data) do
# handle_event(event, msg, data)
# end

def waiting(:cast, 0, _data) do
:keep_state_and_data
end
Expand All @@ -53,4 +63,12 @@ defmodule NextLS.DB.Activity do
NextLS.Progress.start(data.lsp, token, "Indexing!")
{:next_state, :active, %{data | count: mailbox_count, token: token}}
end

# def waiting(event, msg, data) do
# handle_event(event, msg, data)
# end

# defp handle_event({:call, from}, :get, data) do
# {:keep_state, data, [{:reply, from, data.count}]}
# end
end
4 changes: 3 additions & 1 deletion lib/next_ls/runtime/supervisor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ defmodule NextLS.Runtime.Supervisor do
children = [
{NextLS.Runtime.Sidecar, name: sidecar_name, db: db_name},
{NextLS.DB.Activity,
logger: logger, name: db_activity, lsp: lsp, timeout: Application.get_env(:next_ls, :indexing_timeout)},
logger: logger,
registry: registry,
name: db_activity, lsp: lsp, timeout: Application.get_env(:next_ls, :indexing_timeout)},
{NextLS.DB,
logger: logger,
file: "#{hidden_folder}/nextls.db",
Expand Down
111 changes: 111 additions & 0 deletions lib/next_ls_ui/components.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
defmodule NextLS.UI.Component do
@moduledoc false
use Temple.Component

defmacro __using__(_) do
quote do
import Temple
import unquote(__MODULE__)
end
end
end

defmodule NextLS.UI.Components do
@moduledoc false
use NextLS.UI.Component

@env Mix.env()

def root(assigns) do
assigns = Map.put(assigns, :env, @env)

temple do
"<!DOCTYPE html>"

html lang: "en" do
head do
meta charset: "utf-8"
meta http_equiv: "X-UA-Compatible", content: "IE=edge"
meta name: "viewport", content: "width=device-width, initial-scale=1.0"

title do
"Next LS Inspector"
end

script src: "https://unpkg.com/htmx.org@1.9.6"
script src: "https://unpkg.com/htmx.org/dist/ext/ws.js"
script src: "https://unpkg.com/htmx.org/dist/ext/morphdom-swap.js"

script src: "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.0/chart.umd.min.js"

script src: "https://cdn.jsdelivr.net/npm/luxon@^2"
script src: "https://cdn.jsdelivr.net/npm/chartjs-adapter-luxon@^1"


link rel: "stylesheet", href: "/css/site.css"
end

body class: "bg-zinc-200 dark:bg-zinc-900 font-sans", hx_ext: "morphdom-swap" do
main class: "container mx-auto" do
header class: "mb-8 py-2" do
div class: "flex items-center space-x-2" do
a href: "/", class: "hover:underline" do
img src: "/nextls-logo-no-background.png", class: "h-8 w-8"
end

h2 class: "text-xl dark:text-white" do
a href: "/" do
"Next LS Inspector"
end
end
end
end

slot @inner_block

footer class: "flex justify-between dark:text-white mt-8 py-4" do
div do
a class: "underline",
href: "https://github.com/elixir-tools/next-ls",
do: "Source Code"
end

div class: "italic" do
span do: "Built with"

a class: "underline",
href: "https://github.com/mhanberg/temple",
do: "Temple,"

a class: "underline",
href: "https://tailwindcss.com",
do: "TailwindCSS,"

a class: "underline",
href: "https://htmx.org",
do: "HTMX,"

" and"

span class: "text-red-500", do: "♥"
end
end
end

if @env == :dev do
c &WebDevUtils.Components.live_reload/1
end
end
end
end
end

def card(assigns) do
temple do
div class: "#{assigns[:class]} bg-zinc-50 dark:bg-zinc-700 dark:text-white rounded shadow-xl p-2",
rest!: Map.take(assigns, [:id]) do
slot @inner_block
end
end
end
end
12 changes: 12 additions & 0 deletions lib/next_ls_ui/logger.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
defmodule NextLS.UI.Logger do
@moduledoc false
def log(event, _config) do
if Process.alive?(Process.whereis(NextLS.UI.Registry)) do
Registry.dispatch(NextLS.UI.Registry, :log_socket, fn entries ->
for {pid, _} <- entries do
send(pid, {:log, event})
end
end)
end
end
end
Loading

0 comments on commit fe9f9d0

Please sign in to comment.