Skip to content

Commit d896f3e

Browse files
committed
feat: inspector
1 parent 53c655e commit d896f3e

22 files changed

+868
-19
lines changed

.formatter.exs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
request: 2
99
],
1010
line_length: 120,
11-
import_deps: [:gen_lsp],
11+
import_deps: [:gen_lsp, :plug, :temple],
1212
plugins: [Styler],
1313
inputs: [
1414
".formatter.exs",

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,5 @@ result
3030
# Ignore dialyzer plt files
3131
/priv/plts/*.plt
3232
/priv/plts/*.plt.hash
33+
34+
/priv/css/*

assets/css/app.css

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
@import "tailwindcss/base";
2+
@import "tailwindcss/components";
3+
@import "tailwindcss/utilities";
4+
5+
@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');
6+
@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');
7+
8+
pre {
9+
@apply bg-zinc-500 text-white rounded px-2 text-sm;
10+
}
11+
12+
h1, h2, h3, h4, h5 {
13+
@apply font-fancy font-semibold;
14+
}

assets/tailwind.config.js

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// See the Tailwind configuration guide for advanced usage
2+
// https://tailwindcss.com/docs/configuration
3+
4+
const defaultTheme = require("tailwindcss/defaultTheme");
5+
6+
module.exports = {
7+
content: ["./js/**/*.js", "./lib/**/*.ex"],
8+
theme: {
9+
extend: {
10+
fontFamily: {
11+
sans: ['"Inter"', ...defaultTheme.fontFamily.sans],
12+
fancy: ['"Rubik"', ...defaultTheme.fontFamily.sans],
13+
},
14+
},
15+
},
16+
plugins: [],
17+
};

bin/start

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33
cd "$(dirname "$0")"/.. || exit 1
44

5-
elixir -S mix run --no-halt -e "Application.ensure_all_started(:next_ls)" -- "$@"
5+
mix run --no-halt -e "Application.ensure_all_started(:next_ls)" -- "$@"

config/config.exs

+36-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,41 @@ import Config
22

33
config :next_ls, :indexing_timeout, 100
44

5-
config :logger, :default_handler, config: [type: :standard_error]
5+
config :temple,
6+
engine: EEx.SmartEngine,
7+
attributes: {Temple, :attributes}
8+
9+
config :tailwind,
10+
version: "3.3.2",
11+
default: [
12+
args: ~w(
13+
--config=assets/tailwind.config.js
14+
--input=assets/css/app.css
15+
--output=priv/css/site.css
16+
)
17+
]
18+
19+
# config :logger, :default_handler, config: [type: :standard_error]
20+
config :logger, :default_handler,
21+
config: [
22+
file: ~c".elixir-tools/next-ls.log",
23+
filesync_repeat_interval: 5000,
24+
file_check: 5000,
25+
max_no_bytes: 10_000_000,
26+
max_no_files: 5,
27+
compress_on_rotate: true
28+
]
29+
30+
config :logger, :default_formatter,
31+
format: "\n$time $metadata[$level] $message\n"
32+
33+
config :next_ls, :logger, [
34+
{:handler, :ui_logger, NextLS.UI.Logger,
35+
%{
36+
config: %{},
37+
formatter: Logger.Formatter.new()
38+
}}
39+
]
40+
641

742
import_config "#{config_env()}.exs"

config/dev.exs

+5
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
11
import Config
2+
3+
config :next_ls, :assets, tailwind: {Tailwind, :install_and_run, [:default, ~w(--watch)]}
4+
5+
config :web_dev_utils, :reload_url, "'wss://' + location.host + '/ws'"
6+
config :web_dev_utils, :reload_log, true

lib/next_ls.ex

+27-1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ defmodule NextLS do
7171
cache = Keyword.fetch!(args, :cache)
7272
{:ok, logger} = DynamicSupervisor.start_child(dynamic_supervisor, {NextLS.Logger, lsp: lsp})
7373

74+
{:ok, ui} =
75+
DynamicSupervisor.start_child(
76+
dynamic_supervisor,
77+
{Bandit, [plug: NextLS.UI.Router, port: "NEXTLS_UI_PORT" |> System.get_env("0") |> String.to_integer()]}
78+
)
79+
7480
{:ok,
7581
assign(lsp,
7682
auto_update: Keyword.get(args, :auto_update, false),
@@ -85,7 +91,8 @@ defmodule NextLS do
8591
registry: registry,
8692
extensions: extensions,
8793
ready: false,
88-
client_capabilities: nil
94+
client_capabilities: nil,
95+
ui: ui
8996
)}
9097
end
9198

@@ -121,12 +128,14 @@ defmodule NextLS do
121128
completion_provider:
122129
if init_opts.experimental.completions.enable do
123130
%GenLSP.Structures.CompletionOptions{
131+
124132
trigger_characters: [".", "@", "&", "%", "^", ":", "!", "-", "~", "/", "{"]
125133
}
126134
else
127135
nil
128136
end,
129137
document_formatting_provider: true,
138+
execute_command_provider: %GenLSP.Structures.ExecuteCommandOptions{commands: ["open-ui"]},
130139
hover_provider: true,
131140
workspace_symbol_provider: true,
132141
document_symbol_provider: true,
@@ -572,6 +581,23 @@ defmodule NextLS do
572581
{:reply, [], lsp}
573582
end
574583

584+
def handle_request(
585+
%GenLSP.Requests.WorkspaceExecuteCommand{params: %GenLSP.Structures.ExecuteCommandParams{command: command}},
586+
lsp
587+
) do
588+
{:ok, {_, port}} = ThousandIsland.listener_info(lsp.assigns.ui)
589+
590+
case command do
591+
"open-ui" ->
592+
System.cmd("open", ["http://localhost:#{port}"])
593+
594+
_ ->
595+
NextLS.Logger.warning(lsp.logger, "[Next LS] Unknown workspace command: #{command}")
596+
end
597+
598+
{:reply, nil, lsp}
599+
end
600+
575601
def handle_request(%Shutdown{}, lsp) do
576602
{:reply, nil, assign(lsp, exit_code: 0)}
577603
end

lib/next_ls/application.ex

+129-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,116 @@
1+
# defmodule NextLS.OpentelemetrySchematic do
2+
# @moduledoc false
3+
# require Logger
4+
5+
# @tracer_id __MODULE__
6+
7+
# def setup do
8+
# :ok =
9+
# :telemetry.attach_many(
10+
# "schematic-handler",
11+
# [
12+
# [:schematic, :unify, :start],
13+
# [:schematic, :unify, :stop]
14+
# ],
15+
# &__MODULE__.process/4,
16+
# nil
17+
# )
18+
# end
19+
20+
# def process([:schematic, :unify, :start], _measurements, metadata, _config) do
21+
# OpentelemetryTelemetry.start_telemetry_span(
22+
# @tracer_id,
23+
# :"schematic.unify.#{metadata.kind} #{metadata.dir}",
24+
# metadata,
25+
# %{kind: :server, attributes: metadata}
26+
# )
27+
# end
28+
29+
# def process([:schematic, :unify, :stop], _measurements, metadata, _config) do
30+
# OpentelemetryTelemetry.set_current_telemetry_span(@tracer_id, metadata)
31+
# OpentelemetryTelemetry.end_telemetry_span(@tracer_id, metadata)
32+
# end
33+
# end
34+
35+
# defmodule NextLS.OpentelemetryGenLSP do
36+
# @moduledoc false
37+
# require Logger
38+
# require OpenTelemetry.Tracer, as: Tracer
39+
40+
# @tracer_id __MODULE__
41+
42+
# def setup do
43+
# :ok =
44+
# :telemetry.attach_many(
45+
# "gen-lsp-handler",
46+
# [
47+
# [:gen_lsp, :loop, :start],
48+
# [:gen_lsp, :loop, :stop],
49+
# [:gen_lsp, :notification, :emit],
50+
# [:gen_lsp, :info, :start],
51+
# [:gen_lsp, :info, :stop],
52+
# [:gen_lsp, :buffer, :read],
53+
# [:gen_lsp, :buffer, :write]
54+
# ],
55+
# &__MODULE__.process/4,
56+
# nil
57+
# )
58+
# end
59+
60+
# def process([:gen_lsp, :buffer, :read], _measurements, metadata, _config) do
61+
# OpenTelemetry.Ctx.clear()
62+
63+
# OpentelemetryTelemetry.start_telemetry_span(@tracer_id, :"gen_lsp.read", metadata, %{
64+
# kind: :server,
65+
# attributes: metadata
66+
# })
67+
# end
68+
69+
# def process([:gen_lsp, :loop, :start], _measurements, metadata, _config) do
70+
# parent_context = OpentelemetryProcessPropagator.fetch_parent_ctx(1, :"$callers")
71+
72+
# if parent_context != :undefined do
73+
# OpenTelemetry.Ctx.attach(parent_context)
74+
# end
75+
76+
# Tracer.update_name(:"gen_lsp.receive.#{metadata.type} #{metadata.method}")
77+
# end
78+
79+
# def process([:gen_lsp, :loop, :stop], _measurements, %{type: :request, reply: true} = _metadata, _config) do
80+
# OpenTelemetry.Ctx.clear()
81+
# end
82+
83+
# def process([:gen_lsp, :loop, :stop], _measurements, metadata, _config) do
84+
# OpentelemetryTelemetry.set_current_telemetry_span(@tracer_id, metadata)
85+
# OpentelemetryTelemetry.end_telemetry_span(@tracer_id, metadata)
86+
# OpenTelemetry.Ctx.clear()
87+
# end
88+
89+
# def process([:gen_lsp, :notification, :emit], _measurements, metadata, _config) do
90+
# OpentelemetryTelemetry.start_telemetry_span(
91+
# @tracer_id,
92+
# :"gen_lsp.send.notification #{metadata.method}",
93+
# metadata,
94+
# %{
95+
# kind: :server,
96+
# attributes: metadata
97+
# }
98+
# )
99+
# end
100+
101+
# def process([:gen_lsp, :buffer, :write], _measurements, metadata, _config) do
102+
# parent_context = OpentelemetryProcessPropagator.fetch_parent_ctx(1, :"$callers")
103+
104+
# if parent_context != :undefined do
105+
# OpenTelemetry.Ctx.attach(parent_context)
106+
# end
107+
108+
# OpentelemetryTelemetry.set_current_telemetry_span(@tracer_id, metadata)
109+
# OpentelemetryTelemetry.end_telemetry_span(@tracer_id, metadata)
110+
# OpenTelemetry.Ctx.clear()
111+
# end
112+
# end
113+
1114
defmodule NextLS.Application do
2115
# See https://hexdocs.pm/elixir/Application.html
3116
# for more information on OTP Applications
@@ -7,6 +120,9 @@ defmodule NextLS.Application do
7120

8121
@impl true
9122
def start(_type, _args) do
123+
124+
Logger.add_handlers(:next_ls)
125+
10126
case System.cmd("epmd", ["-daemon"], stderr_to_stdout: true) do
11127
{_, 0} ->
12128
:ok
@@ -19,11 +135,22 @@ defmodule NextLS.Application do
19135

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

22-
children = [NextLS.LSPSupervisor]
138+
children = [
139+
{Registry, name: NextLS.UI.Registry, keys: :duplicate},
140+
WebDevUtils.FileSystem,
141+
WebDevUtils.CodeReloader,
142+
NextLS.LSPSupervisor
143+
]
23144

24145
# See https://hexdocs.pm/elixir/Supervisor.html
25146
# for other strategies and supported options
26147
opts = [strategy: :one_for_one, name: NextLS.Supervisor]
27-
Supervisor.start_link(children, opts)
148+
Supervisor.start_link(children ++ asset_children(), opts)
149+
end
150+
151+
def asset_children do
152+
for conf <- Application.get_env(:next_ls, :assets, []) do
153+
{WebDevUtils.Assets, conf}
154+
end
28155
end
29156
end

lib/next_ls/db/activity.ex

+8-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,14 @@ defmodule NextLS.DB.Activity do
1313
:gen_statem.start_link({:local, Keyword.get(args, :name)}, __MODULE__, Keyword.drop(args, [:name]), [])
1414
end
1515

16-
def update(statem, count), do: :gen_statem.cast(statem, count)
16+
def update(statem, count) do
17+
Registry.dispatch(NextLS.UI.Registry, :activity_socket, fn entries ->
18+
for {pid, _} <- entries, do: send(pid, {:activity, count})
19+
20+
end)
21+
22+
:gen_statem.cast(statem, count)
23+
end
1724

1825
@impl :gen_statem
1926
def callback_mode, do: :state_functions

lib/next_ls/logger.ex

+10
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ defmodule NextLS.Logger do
22
@moduledoc false
33
use GenServer
44

5+
require Logger
6+
57
def start_link(arg) do
68
GenServer.start_link(__MODULE__, arg, Keyword.take(arg, [:name]))
79
end
@@ -22,6 +24,14 @@ defmodule NextLS.Logger do
2224

2325
def handle_cast({:log, type, msg}, state) do
2426
apply(GenLSP, type, [state.lsp, String.trim("[NextLS] #{msg}")])
27+
28+
case type do
29+
:log -> Logger.debug(msg)
30+
:warning -> Logger.warning(msg)
31+
:error -> Logger.error(msg)
32+
:info -> Logger.info(msg)
33+
end
34+
2535
{:noreply, state}
2636
end
2737

0 commit comments

Comments
 (0)