browsable-lsp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +12 -0
- data/README.md +142 -0
- data/exe/browsable-lsp +9 -0
- data/lib/browsable/lsp/diagnostics.rb +91 -0
- data/lib/browsable/lsp/handlers/did_change.rb +26 -0
- data/lib/browsable/lsp/handlers/did_open.rb +25 -0
- data/lib/browsable/lsp/handlers/initialize.rb +30 -0
- data/lib/browsable/lsp/server.rb +98 -0
- data/lib/browsable/lsp/version.rb +7 -0
- data/lib/browsable-lsp.rb +18 -0
- metadata +118 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 87c10b50ca49f864d9d2b0793c69ecf77db137a97eccac37c9ab21ba76490006
|
|
4
|
+
data.tar.gz: 44ff7ce5d57863ee5d6b2b9fd914bff2e16cf18b1b8292c7eb32400d39336af6
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 36f4c19a6bb502bf67a31260a6a96496a5fec457c6b282c9452ec7e8a627d1fbc45390866fa8661c6d8da7203c0411a52300bc7b8df15972108408a5b72f8bc4
|
|
7
|
+
data.tar.gz: c0a514689ca863de6dc31d5eb53141a26f466021b9a170bd1dd3649cc1d1fea8c010ce736e7156872fdc2c36b8afacdc010dc4a9831494821553e2fe212a81e6
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the `browsable-lsp` gem are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0]
|
|
11
|
+
|
|
12
|
+
- Initial release.
|
data/README.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# browsable-lsp
|
|
2
|
+
|
|
3
|
+
**A Language Server Protocol server for [`browsable`](../browsable).**
|
|
4
|
+
|
|
5
|
+
`browsable-lsp` exposes browsable's browser-compatibility audit to your editor.
|
|
6
|
+
As you open and edit CSS, ERB, HTML, and JavaScript files, it reports — inline —
|
|
7
|
+
which features your code uses that fall outside your project's `allow_browser`
|
|
8
|
+
target.
|
|
9
|
+
|
|
10
|
+
> Part of the [`browsable` monorepo](https://github.com/romanhood/browsable).
|
|
11
|
+
> Neovim users want [`browsable.nvim`](../browsable.nvim) instead — it bundles
|
|
12
|
+
> this server's wiring.
|
|
13
|
+
|
|
14
|
+
## What is a language server?
|
|
15
|
+
|
|
16
|
+
A *language server* is a background program your editor talks to over a small
|
|
17
|
+
JSON-RPC protocol (the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/)).
|
|
18
|
+
The editor tells the server which files you open and edit; the server tells the
|
|
19
|
+
editor what to underline. One server works in every LSP-capable editor, so
|
|
20
|
+
browsable's analysis is written once and reused everywhere.
|
|
21
|
+
|
|
22
|
+
`browsable-lsp` communicates over stdio and pushes **diagnostics** — the
|
|
23
|
+
squiggly underlines — for each document you touch.
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
gem install browsable-lsp
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
This installs the `browsable-lsp` executable. Confirm it is on your `PATH`:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
which browsable-lsp
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
ERB and HTML are audited in-process and need nothing else. For CSS and
|
|
38
|
+
JavaScript diagnostics, install stylelint and eslint as described in the
|
|
39
|
+
[`browsable` README](../browsable/README.md#system-dependencies--the-doctor-workflow)
|
|
40
|
+
(`browsable doctor` will guide you).
|
|
41
|
+
|
|
42
|
+
## Severity mapping
|
|
43
|
+
|
|
44
|
+
| browsable category | LSP severity |
|
|
45
|
+
| --------------------------- | -------------- |
|
|
46
|
+
| `below_target` | Error |
|
|
47
|
+
| `baseline_newly_available` | Warning |
|
|
48
|
+
| `baseline_widely_available` | Information |
|
|
49
|
+
|
|
50
|
+
A diagnostic reads, for example:
|
|
51
|
+
|
|
52
|
+
> The `popover` attribute requires Firefox 125+, but your `:modern`
|
|
53
|
+
> `allow_browser` policy permits Firefox 121.
|
|
54
|
+
|
|
55
|
+
## Editor setup
|
|
56
|
+
|
|
57
|
+
Configuration is **inherited from the browsable gem** — `browsable-lsp` discovers
|
|
58
|
+
`config/browsable.yml` / `.browsable.yml` from the workspace root exactly as the
|
|
59
|
+
CLI does. There is nothing to configure in the server itself.
|
|
60
|
+
|
|
61
|
+
### VS Code
|
|
62
|
+
|
|
63
|
+
There is no dedicated extension yet. Use a generic LSP bridge such as
|
|
64
|
+
[`vscode-glspc`](https://marketplace.visualstudio.com/items?itemName=qugu.glspc)
|
|
65
|
+
and point it at the executable:
|
|
66
|
+
|
|
67
|
+
```jsonc
|
|
68
|
+
// settings.json
|
|
69
|
+
{
|
|
70
|
+
"glspc.languageServerPath": "browsable-lsp",
|
|
71
|
+
"glspc.languageId": ["css", "html", "erb", "javascript"]
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Helix
|
|
76
|
+
|
|
77
|
+
Add to `~/.config/helix/languages.toml`:
|
|
78
|
+
|
|
79
|
+
```toml
|
|
80
|
+
[language-server.browsable]
|
|
81
|
+
command = "browsable-lsp"
|
|
82
|
+
|
|
83
|
+
[[language]]
|
|
84
|
+
name = "erb"
|
|
85
|
+
language-servers = ["browsable"]
|
|
86
|
+
|
|
87
|
+
[[language]]
|
|
88
|
+
name = "css"
|
|
89
|
+
language-servers = ["browsable"]
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Zed
|
|
93
|
+
|
|
94
|
+
In your Zed settings, register the server (Zed discovers stdio servers via an
|
|
95
|
+
extension or the `lsp` settings block):
|
|
96
|
+
|
|
97
|
+
```jsonc
|
|
98
|
+
{
|
|
99
|
+
"lsp": {
|
|
100
|
+
"browsable-lsp": {
|
|
101
|
+
"binary": { "path": "browsable-lsp" }
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Neovim
|
|
108
|
+
|
|
109
|
+
Don't wire this up by hand — install [`browsable.nvim`](../browsable.nvim),
|
|
110
|
+
which configures the client, the filetypes, and the root-directory detection for
|
|
111
|
+
you.
|
|
112
|
+
|
|
113
|
+
## How it works
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
editor ──(LSP/JSON-RPC over stdio)──▶ browsable-lsp
|
|
117
|
+
│
|
|
118
|
+
▼
|
|
119
|
+
Browsable::Analyzers (the core gem)
|
|
120
|
+
│
|
|
121
|
+
▼
|
|
122
|
+
Findings ──▶ LSP diagnostics
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
On `textDocument/didOpen` and `textDocument/didChange`, the server runs
|
|
126
|
+
browsable's analyzers against the buffer's *in-memory* contents (no need to
|
|
127
|
+
save), converts the Findings to LSP diagnostics, and publishes them.
|
|
128
|
+
|
|
129
|
+
## Contributing
|
|
130
|
+
|
|
131
|
+
This gem lives in the `browsable-lsp/` subdirectory of the
|
|
132
|
+
[monorepo](https://github.com/romanhood/browsable):
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
cd browsable-lsp
|
|
136
|
+
bundle install
|
|
137
|
+
bundle exec rspec
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## License
|
|
141
|
+
|
|
142
|
+
MIT — see the [LICENSE](../LICENSE) at the monorepo root.
|
data/exe/browsable-lsp
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "tmpdir"
|
|
4
|
+
|
|
5
|
+
module Browsable
|
|
6
|
+
module LSP
|
|
7
|
+
# Audits a single document's contents and converts the resulting browsable
|
|
8
|
+
# Findings into LSP diagnostic hashes ready for textDocument/publishDiagnostics.
|
|
9
|
+
class Diagnostics
|
|
10
|
+
# browsable severity -> LSP DiagnosticSeverity
|
|
11
|
+
# below_target -> Error (1)
|
|
12
|
+
# baseline_newly_available -> Warning (2)
|
|
13
|
+
# baseline_widely_available -> Information (3)
|
|
14
|
+
SEVERITY = { error: 1, warning: 2, info: 3 }.freeze
|
|
15
|
+
|
|
16
|
+
def self.for(uri:, content:, root: Dir.pwd)
|
|
17
|
+
new(uri: uri, content: content, root: root).call
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def initialize(uri:, content:, root:)
|
|
21
|
+
@uri = uri
|
|
22
|
+
@content = content.to_s
|
|
23
|
+
@root = root
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def call
|
|
27
|
+
findings.map { |finding| to_diagnostic(finding) }
|
|
28
|
+
rescue StandardError
|
|
29
|
+
# A diagnostics pass must never crash the editor session.
|
|
30
|
+
[]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def findings
|
|
36
|
+
config = Browsable::Config.load(root: @root)
|
|
37
|
+
analyzer_class = analyzer_for(path)
|
|
38
|
+
return [] unless analyzer_class
|
|
39
|
+
|
|
40
|
+
analyzer = analyzer_class.new(target: config.target, config: config)
|
|
41
|
+
|
|
42
|
+
if analyzer.is_a?(Browsable::Analyzers::ERB)
|
|
43
|
+
# ERB/HTML analysis is in-process — audit the buffer contents directly.
|
|
44
|
+
analyzer.analyze_source(@content, file: path)
|
|
45
|
+
else
|
|
46
|
+
# CSS/JS need a real file for stylelint/eslint.
|
|
47
|
+
# TODO(v0.2): debounce changes and reuse a per-document temp file.
|
|
48
|
+
audit_via_tempfile(analyzer)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def audit_via_tempfile(analyzer)
|
|
53
|
+
Dir.mktmpdir("browsable-lsp") do |dir|
|
|
54
|
+
tmp = File.join(dir, File.basename(path))
|
|
55
|
+
File.write(tmp, @content)
|
|
56
|
+
analyzer.analyze([tmp])
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def analyzer_for(file)
|
|
61
|
+
case File.extname(file).downcase
|
|
62
|
+
when ".erb" then Browsable::Analyzers::ERB
|
|
63
|
+
when ".html", ".htm" then Browsable::Analyzers::HTML
|
|
64
|
+
when ".css", ".scss" then Browsable::Analyzers::CSS
|
|
65
|
+
when ".js", ".mjs" then Browsable::Analyzers::Javascript
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def path
|
|
70
|
+
@path ||= @uri.to_s.sub(%r{\Afile://}, "")
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def to_diagnostic(finding)
|
|
74
|
+
line = [finding.line.to_i - 1, 0].max
|
|
75
|
+
character = [finding.column.to_i - 1, 0].max
|
|
76
|
+
span = finding.feature_name.to_s.length.clamp(1, 200)
|
|
77
|
+
|
|
78
|
+
{
|
|
79
|
+
range: {
|
|
80
|
+
start: { line: line, character: character },
|
|
81
|
+
end: { line: line, character: character + span }
|
|
82
|
+
},
|
|
83
|
+
severity: SEVERITY.fetch(finding.severity, 2),
|
|
84
|
+
source: "browsable",
|
|
85
|
+
code: finding.feature_id,
|
|
86
|
+
message: finding.message
|
|
87
|
+
}
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Browsable
|
|
4
|
+
module LSP
|
|
5
|
+
module Handlers
|
|
6
|
+
# Handles textDocument/didChange — re-audits a document as it is edited.
|
|
7
|
+
class DidChange
|
|
8
|
+
def initialize(server)
|
|
9
|
+
@server = server
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def call(params)
|
|
13
|
+
uri = params.dig("textDocument", "uri")
|
|
14
|
+
return unless uri
|
|
15
|
+
|
|
16
|
+
# Full-sync mode: the final content change carries the whole document.
|
|
17
|
+
text = Array(params["contentChanges"]).last&.fetch("text", nil)
|
|
18
|
+
return if text.nil?
|
|
19
|
+
|
|
20
|
+
@server.store(uri, text)
|
|
21
|
+
@server.publish_diagnostics(uri, text)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Browsable
|
|
4
|
+
module LSP
|
|
5
|
+
module Handlers
|
|
6
|
+
# Handles textDocument/didOpen — audits a freshly-opened document and
|
|
7
|
+
# publishes its diagnostics.
|
|
8
|
+
class DidOpen
|
|
9
|
+
def initialize(server)
|
|
10
|
+
@server = server
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call(params)
|
|
14
|
+
document = params["textDocument"] || {}
|
|
15
|
+
uri = document["uri"]
|
|
16
|
+
return unless uri
|
|
17
|
+
|
|
18
|
+
text = document["text"].to_s
|
|
19
|
+
@server.store(uri, text)
|
|
20
|
+
@server.publish_diagnostics(uri, text)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Browsable
|
|
4
|
+
module LSP
|
|
5
|
+
module Handlers
|
|
6
|
+
# Handles the `initialize` request: advertises the server's capabilities.
|
|
7
|
+
class Initialize
|
|
8
|
+
def call(_params)
|
|
9
|
+
{
|
|
10
|
+
capabilities: {
|
|
11
|
+
# 1 = Full document sync — the client resends the whole buffer on
|
|
12
|
+
# every change. Simple and fine for the file sizes browsable sees.
|
|
13
|
+
textDocumentSync: { openClose: true, change: 1 }
|
|
14
|
+
},
|
|
15
|
+
serverInfo: { name: "browsable-lsp", version: Browsable::LSP::VERSION }
|
|
16
|
+
}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Best-effort extraction of the workspace root from initialize params.
|
|
20
|
+
# browsable's Config is then loaded relative to it.
|
|
21
|
+
def self.workspace_root(params)
|
|
22
|
+
uri = params["rootUri"] || params.dig("workspaceFolders", 0, "uri")
|
|
23
|
+
return params["rootPath"] if uri.nil?
|
|
24
|
+
|
|
25
|
+
uri.to_s.sub(%r{\Afile://}, "")
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Browsable
|
|
4
|
+
module LSP
|
|
5
|
+
# The JSON-RPC server loop.
|
|
6
|
+
#
|
|
7
|
+
# Reads LSP messages from stdin, dispatches them to handlers, and writes
|
|
8
|
+
# responses and diagnostics back to stdout — the standard stdio LSP
|
|
9
|
+
# convention. All compatibility analysis is delegated to the browsable gem;
|
|
10
|
+
# this class only speaks the protocol.
|
|
11
|
+
class Server
|
|
12
|
+
def initialize(input: $stdin, output: $stdout)
|
|
13
|
+
# Io::Reader/Writer take an explicit IO (the Stdio:: subclasses hard-code
|
|
14
|
+
# STDIN/STDOUT); passing $stdin/$stdout keeps the default behaviour while
|
|
15
|
+
# letting tests drive the server over StringIO pipes.
|
|
16
|
+
@reader = ::LanguageServer::Protocol::Transport::Io::Reader.new(input)
|
|
17
|
+
@writer = ::LanguageServer::Protocol::Transport::Io::Writer.new(output)
|
|
18
|
+
@documents = {}
|
|
19
|
+
@workspace_root = Dir.pwd
|
|
20
|
+
@shutdown_requested = false
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Block reading messages until the client disconnects or sends `exit`.
|
|
24
|
+
def start
|
|
25
|
+
@reader.read { |message| dispatch(normalize(message)) }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Cache the latest known contents of a document.
|
|
29
|
+
def store(uri, content)
|
|
30
|
+
@documents[uri] = content
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Audit `content` and push its diagnostics to the client.
|
|
34
|
+
def publish_diagnostics(uri, content)
|
|
35
|
+
diagnostics = Diagnostics.for(uri: uri, content: content, root: @workspace_root)
|
|
36
|
+
notify("textDocument/publishDiagnostics", { uri: uri, diagnostics: diagnostics })
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def dispatch(message)
|
|
42
|
+
method = message["method"]
|
|
43
|
+
id = message["id"]
|
|
44
|
+
params = message["params"] || {}
|
|
45
|
+
|
|
46
|
+
case method
|
|
47
|
+
when "initialize"
|
|
48
|
+
@workspace_root = Handlers::Initialize.workspace_root(params) || @workspace_root
|
|
49
|
+
respond(id, Handlers::Initialize.new.call(params))
|
|
50
|
+
when "initialized"
|
|
51
|
+
nil # nothing to do — diagnostics are pushed on open/change
|
|
52
|
+
when "textDocument/didOpen"
|
|
53
|
+
Handlers::DidOpen.new(self).call(params)
|
|
54
|
+
when "textDocument/didChange"
|
|
55
|
+
Handlers::DidChange.new(self).call(params)
|
|
56
|
+
when "textDocument/didClose"
|
|
57
|
+
@documents.delete(params.dig("textDocument", "uri"))
|
|
58
|
+
when "shutdown"
|
|
59
|
+
@shutdown_requested = true
|
|
60
|
+
respond(id, nil)
|
|
61
|
+
when "exit"
|
|
62
|
+
exit(@shutdown_requested ? 0 : 1)
|
|
63
|
+
# TODO(v0.2): textDocument/codeAction — offer "Add @supports fallback"
|
|
64
|
+
# and "Tighten allow_browser to require Safari 15.4+" as quick fixes.
|
|
65
|
+
end
|
|
66
|
+
rescue StandardError => e
|
|
67
|
+
log("error handling #{method}: #{e.class}: #{e.message}")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def respond(id, result)
|
|
71
|
+
write(jsonrpc: "2.0", id: id, result: result)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def notify(method, params)
|
|
75
|
+
write(jsonrpc: "2.0", method: method, params: params)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def write(message)
|
|
79
|
+
@writer.write(message)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Diagnostics go to stderr — stdout is reserved for the JSON-RPC channel.
|
|
83
|
+
def log(text)
|
|
84
|
+
warn("[browsable-lsp] #{text}")
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Normalize every hash key to a String so handlers need not care whether
|
|
88
|
+
# the transport produced symbol or string keys.
|
|
89
|
+
def normalize(object)
|
|
90
|
+
case object
|
|
91
|
+
when Hash then object.each_with_object({}) { |(k, v), h| h[k.to_s] = normalize(v) }
|
|
92
|
+
when Array then object.map { |element| normalize(element) }
|
|
93
|
+
else object
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# browsable-lsp — a Language Server Protocol server built on the browsable gem.
|
|
4
|
+
#
|
|
5
|
+
# The core gem already sets up Zeitwerk for the Browsable namespace; this small
|
|
6
|
+
# companion gem uses an explicit require manifest instead. With only a handful
|
|
7
|
+
# of files, a flat manifest is clearer than a second autoloader co-managing the
|
|
8
|
+
# shared Browsable:: namespace.
|
|
9
|
+
|
|
10
|
+
require "browsable"
|
|
11
|
+
require "language_server-protocol"
|
|
12
|
+
|
|
13
|
+
require_relative "browsable/lsp/version"
|
|
14
|
+
require_relative "browsable/lsp/diagnostics"
|
|
15
|
+
require_relative "browsable/lsp/handlers/initialize"
|
|
16
|
+
require_relative "browsable/lsp/handlers/did_open"
|
|
17
|
+
require_relative "browsable/lsp/handlers/did_change"
|
|
18
|
+
require_relative "browsable/lsp/server"
|
metadata
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: browsable-lsp
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Roman Hood
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-05-25 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: browsable
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0.1'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0.1'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: language_server-protocol
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '3.17'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '3.17'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rake
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '13.0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '13.0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rspec
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '3.13'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '3.13'
|
|
69
|
+
description: |
|
|
70
|
+
browsable-lsp exposes browsable's browser-compatibility audit as a Language
|
|
71
|
+
Server Protocol server, so editors can show inline diagnostics as you type.
|
|
72
|
+
It wraps the browsable gem's analyzers and speaks LSP over stdio.
|
|
73
|
+
email:
|
|
74
|
+
- roman.hood@aigility.com
|
|
75
|
+
executables:
|
|
76
|
+
- browsable-lsp
|
|
77
|
+
extensions: []
|
|
78
|
+
extra_rdoc_files: []
|
|
79
|
+
files:
|
|
80
|
+
- CHANGELOG.md
|
|
81
|
+
- README.md
|
|
82
|
+
- exe/browsable-lsp
|
|
83
|
+
- lib/browsable-lsp.rb
|
|
84
|
+
- lib/browsable/lsp/diagnostics.rb
|
|
85
|
+
- lib/browsable/lsp/handlers/did_change.rb
|
|
86
|
+
- lib/browsable/lsp/handlers/did_open.rb
|
|
87
|
+
- lib/browsable/lsp/handlers/initialize.rb
|
|
88
|
+
- lib/browsable/lsp/server.rb
|
|
89
|
+
- lib/browsable/lsp/version.rb
|
|
90
|
+
homepage: https://github.com/romanhood/browsable
|
|
91
|
+
licenses:
|
|
92
|
+
- MIT
|
|
93
|
+
metadata:
|
|
94
|
+
homepage_uri: https://github.com/romanhood/browsable
|
|
95
|
+
source_code_uri: https://github.com/romanhood/browsable/tree/main/browsable-lsp
|
|
96
|
+
changelog_uri: https://github.com/romanhood/browsable/blob/main/browsable-lsp/CHANGELOG.md
|
|
97
|
+
bug_tracker_uri: https://github.com/romanhood/browsable/issues
|
|
98
|
+
rubygems_mfa_required: 'true'
|
|
99
|
+
post_install_message:
|
|
100
|
+
rdoc_options: []
|
|
101
|
+
require_paths:
|
|
102
|
+
- lib
|
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
104
|
+
requirements:
|
|
105
|
+
- - ">="
|
|
106
|
+
- !ruby/object:Gem::Version
|
|
107
|
+
version: '3.2'
|
|
108
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
109
|
+
requirements:
|
|
110
|
+
- - ">="
|
|
111
|
+
- !ruby/object:Gem::Version
|
|
112
|
+
version: '0'
|
|
113
|
+
requirements: []
|
|
114
|
+
rubygems_version: 3.5.22
|
|
115
|
+
signing_key:
|
|
116
|
+
specification_version: 4
|
|
117
|
+
summary: Language Server Protocol server for browsable.
|
|
118
|
+
test_files: []
|