Skip to content

Commit 942c7ac

Browse files
authored
Merge pull request #2 from EmmyLuaLs/dap
Support Dap
2 parents e6108ae + d22b10f commit 942c7ac

File tree

5 files changed

+290
-6
lines changed

5 files changed

+290
-6
lines changed

.zed/debug.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"configurations": [
3+
{
4+
"type": "emmylua_new",
5+
"request": "launch",
6+
"name": "EmmyLua Debug",
7+
"host": "localhost",
8+
"port": 9966,
9+
"sourcePaths": [
10+
"${workspaceFolder}"
11+
],
12+
"ext": [
13+
".lua",
14+
".lua.txt",
15+
".lua.bytes"
16+
],
17+
"ideConnectDebugger": true
18+
}
19+
]
20+
}

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@
4242
- **Text Objects**: Navigate and select Lua-specific code structures (functions, loops, conditionals, tables)
4343
- **Symbol Search**: Quick navigation to any symbol in your workspace
4444

45+
### Debugging Support
46+
- **Debug Adapter Protocol**: Integration with [emmylua_dap](https://github.com/EmmyLuaLs/emmylua_dap)
47+
4548
### 🛠️ Development Tools
4649
- **Task Runner**: Pre-configured tasks for running Lua scripts
4750
- **Code Actions**: Quick fixes and refactoring suggestions
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"title": "EmmyLua Debug Adapter Configuration",
4+
"description": "Configuration schema for EmmyLua debug adapter (emmylua_dap)",
5+
"type": "object",
6+
"required": ["type"],
7+
"properties": {
8+
"type": {
9+
"type": "string",
10+
"enum": ["emmylua_new"],
11+
"description": "Debug adapter type"
12+
},
13+
"request": {
14+
"type": "string",
15+
"enum": ["launch", "attach"],
16+
"description": "Request type for this debug configuration",
17+
"default": "launch"
18+
},
19+
"name": {
20+
"type": "string",
21+
"description": "Name of the debug configuration",
22+
"default": "EmmyLua Debug"
23+
},
24+
"host": {
25+
"type": "string",
26+
"description": "Debug server host address",
27+
"default": "localhost"
28+
},
29+
"port": {
30+
"type": "number",
31+
"description": "Debug server port number",
32+
"default": 9966
33+
},
34+
"sourcePaths": {
35+
"type": "array",
36+
"items": {
37+
"type": "string"
38+
},
39+
"description": "Source code directories to search for files",
40+
"default": ["${workspaceFolder}"]
41+
},
42+
"ext": {
43+
"type": "array",
44+
"items": {
45+
"type": "string"
46+
},
47+
"description": "Supported Lua file extensions",
48+
"default": [".lua", ".lua.txt", ".lua.bytes"]
49+
},
50+
"ideConnectDebugger": {
51+
"type": "boolean",
52+
"description": "Whether the IDE should initiate the connection to the debugger",
53+
"default": true
54+
},
55+
"stopOnEntry": {
56+
"type": "boolean",
57+
"description": "Automatically stop after connecting",
58+
"default": false
59+
}
60+
}
61+
}

extension.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ repository = "https://github.com/EmmyLuaLs/Zed-EmmyLua"
1010
name = "EmmyLua Language Server"
1111
languages = ["EmmyLua", "Lua", "EmmyLuadoc"]
1212

13+
[debug_adapters.emmylua_new]
14+
# EmmyLua Debug Adapter (emmylua_dap)
15+
schema_path = "debug_adapter_schemas/emmylua_new.json"
16+
17+
[debug_locators.lua]
18+
# Lua debug locator for converting run tasks to debug scenarios
19+
1320
[grammars.lua]
1421
repository = "https://github.com/tree-sitter-grammars/tree-sitter-lua"
1522
commit = "d76023017f7485eae629cb60d406c7a1ca0f40c9"

src/emmylua.rs

Lines changed: 199 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1-
use std::fs;
2-
use zed::{CodeLabel, CodeLabelSpan, LanguageServerId, Result, lsp::CompletionKind};
3-
use zed_extension_api::{self as zed};
1+
use std::path::PathBuf;
2+
use std::str::FromStr;
3+
use std::{env, fs};
4+
use zed::lsp::CompletionKind;
5+
use zed::{CodeLabel, CodeLabelSpan, LanguageServerId};
6+
use zed_extension_api::{self as zed, Result, serde_json::Value};
47

58
struct EmmyLuaExtension {
69
cached_binary_path: Option<String>,
10+
cached_dap_binary_path: Option<String>,
711
}
812

913
impl EmmyLuaExtension {
@@ -15,11 +19,14 @@ impl EmmyLuaExtension {
1519
if let Ok(lsp_settings) = zed::settings::LspSettings::for_worktree("emmylua", worktree) {
1620
if let Some(binary) = lsp_settings.binary {
1721
if let Some(path) = binary.path {
18-
return Ok(path);
22+
if !fs::metadata(&path).map_or(false, |stat| stat.is_file()) {
23+
return Ok(path);
24+
}
1925
}
2026
}
2127
}
2228

29+
// First check if emmylua is in PATH
2330
if let Some(path) = worktree.which("emmylua_ls") {
2431
return Ok(path);
2532
}
@@ -109,8 +116,12 @@ impl EmmyLuaExtension {
109116
fs::read_dir(".").map_err(|e| format!("failed to list working directory {e}"))?;
110117
for entry in entries {
111118
let entry = entry.map_err(|e| format!("failed to load directory entry {e}"))?;
112-
if entry.file_name().to_str() != Some(&version_dir) {
113-
fs::remove_dir_all(entry.path()).ok();
119+
if let Some(file_name) = entry.file_name().to_str() {
120+
if file_name.starts_with("emmylua-") {
121+
if entry.file_name().to_str() != Some(&version_dir) {
122+
fs::remove_dir_all(entry.path()).ok();
123+
}
124+
}
114125
}
115126
}
116127
}
@@ -124,6 +135,7 @@ impl zed::Extension for EmmyLuaExtension {
124135
fn new() -> Self {
125136
Self {
126137
cached_binary_path: None,
138+
cached_dap_binary_path: None,
127139
}
128140
}
129141

@@ -267,6 +279,187 @@ impl zed::Extension for EmmyLuaExtension {
267279

268280
Ok(Some(settings))
269281
}
282+
283+
// DAP Support for EmmyLua debugger
284+
fn get_dap_binary(
285+
&mut self,
286+
adapter_name: String,
287+
config: zed::DebugTaskDefinition,
288+
user_provided_debug_adapter_path: Option<String>,
289+
worktree: &zed::Worktree,
290+
) -> std::result::Result<zed::DebugAdapterBinary, String> {
291+
if adapter_name != "emmylua_new" {
292+
return Err(format!("Unknown debug adapter: {}", adapter_name));
293+
}
294+
295+
let default_request_args = zed::StartDebuggingRequestArguments {
296+
configuration: config.config.clone(),
297+
request: self.dap_request_kind(
298+
adapter_name,
299+
Value::from_str(config.config.as_str())
300+
.map_err(|e| format!("Invalid JSON configuration: {e}"))?,
301+
)?,
302+
};
303+
304+
let cwd = Some(worktree.root_path());
305+
306+
// Check if user provided a custom path
307+
if let Some(path) = user_provided_debug_adapter_path {
308+
if !fs::metadata(&path).map_or(false, |stat| stat.is_file()) {
309+
self.cached_dap_binary_path = Some(path.clone());
310+
return Ok(zed::DebugAdapterBinary {
311+
command: Some(path),
312+
arguments: vec![],
313+
envs: vec![],
314+
cwd,
315+
connection: None,
316+
request_args: default_request_args,
317+
});
318+
}
319+
}
320+
321+
// Try to find emmylua_dap in PATH
322+
if let Some(path) = worktree.which("emmylua_dap") {
323+
return Ok(zed::DebugAdapterBinary {
324+
command: Some(path),
325+
arguments: vec![],
326+
envs: vec![],
327+
cwd,
328+
connection: None,
329+
request_args: default_request_args,
330+
});
331+
}
332+
333+
// Check cached path
334+
if let Some(path) = &self.cached_dap_binary_path {
335+
if fs::metadata(path).map_or(false, |stat| stat.is_file()) {
336+
return Ok(zed::DebugAdapterBinary {
337+
command: Some(path.clone()),
338+
arguments: vec![],
339+
envs: vec![],
340+
cwd,
341+
connection: None,
342+
request_args: default_request_args,
343+
});
344+
}
345+
}
346+
347+
// Try to download from GitHub releases
348+
let release = zed::latest_github_release(
349+
"EmmyLuaLs/emmylua_dap",
350+
zed::GithubReleaseOptions {
351+
require_assets: true,
352+
pre_release: false,
353+
},
354+
)
355+
.map_err(|e| format!("Failed to get latest release: {}", e))?;
356+
357+
let (platform, arch) = zed::current_platform();
358+
let asset_name = format!(
359+
"emmylua_dap-{os}-{arch}{glibc}.{extension}",
360+
os = match platform {
361+
zed::Os::Mac => "darwin",
362+
zed::Os::Linux => "linux",
363+
zed::Os::Windows => "win32",
364+
},
365+
arch = match arch {
366+
zed::Architecture::Aarch64 => match platform {
367+
zed::Os::Linux => "aarch64",
368+
zed::Os::Windows | zed::Os::Mac => "arm64",
369+
},
370+
zed::Architecture::X8664 => "x64",
371+
zed::Architecture::X86 => return Err("unsupported platform x86".into()),
372+
},
373+
glibc = match platform {
374+
zed::Os::Linux => "-glibc.2.17",
375+
zed::Os::Windows | zed::Os::Mac => "",
376+
},
377+
extension = match platform {
378+
zed::Os::Mac | zed::Os::Linux => "tar.gz",
379+
zed::Os::Windows => "zip",
380+
},
381+
);
382+
383+
let asset = release
384+
.assets
385+
.iter()
386+
.find(|asset| asset.name.contains(&asset_name))
387+
.ok_or_else(|| format!("No asset found matching pattern: {}", asset_name))?;
388+
389+
let version_dir = format!("emmylua_dap-{}", release.version);
390+
let binary_name = format!(
391+
"emmylua_dap{}",
392+
match platform {
393+
zed::Os::Mac | zed::Os::Linux => "",
394+
zed::Os::Windows => ".exe",
395+
}
396+
);
397+
let binary_path = format!("{}/{}", version_dir, binary_name);
398+
399+
if !fs::metadata(&binary_path).map_or(false, |stat| stat.is_file()) {
400+
zed::download_file(
401+
&asset.download_url,
402+
&version_dir,
403+
match platform {
404+
zed::Os::Mac | zed::Os::Linux => zed::DownloadedFileType::GzipTar,
405+
zed::Os::Windows => zed::DownloadedFileType::Zip,
406+
},
407+
)
408+
.map_err(|e| format!("Failed to download DAP binary: {}", e))?;
409+
410+
let entries =
411+
fs::read_dir(".").map_err(|e| format!("failed to list working directory {e}"))?;
412+
for entry in entries {
413+
let entry = entry.map_err(|e| format!("failed to load directory entry {e}"))?;
414+
if let Some(file_name) = entry.file_name().to_str() {
415+
if file_name.starts_with("emmylua_dap-") {
416+
if entry.file_name().to_str() != Some(&version_dir) {
417+
fs::remove_dir_all(entry.path()).ok();
418+
}
419+
}
420+
}
421+
}
422+
}
423+
424+
let path = match env::current_dir() {
425+
Ok(current_dir) => current_dir.join(binary_path).to_string_lossy().to_string(),
426+
Err(e) => {
427+
eprintln!("Error: {}", e);
428+
PathBuf::from(binary_path).to_string_lossy().to_string()
429+
}
430+
};
431+
432+
self.cached_dap_binary_path = Some(path.clone());
433+
Ok(zed::DebugAdapterBinary {
434+
command: Some(path),
435+
arguments: vec![],
436+
envs: vec![],
437+
cwd,
438+
connection: None,
439+
request_args: default_request_args,
440+
})
441+
}
442+
443+
fn dap_request_kind(
444+
&mut self,
445+
adapter_name: String,
446+
config: Value,
447+
) -> std::result::Result<zed::StartDebuggingRequestArgumentsRequest, String> {
448+
if adapter_name != "emmylua_new" {
449+
return Err(format!("Unknown debug adapter: {}", adapter_name));
450+
}
451+
452+
let request_type = config
453+
.get("request")
454+
.and_then(|v| v.as_str())
455+
.unwrap_or("launch");
456+
457+
match request_type {
458+
"launch" => Ok(zed::StartDebuggingRequestArgumentsRequest::Launch),
459+
"attach" => Ok(zed::StartDebuggingRequestArgumentsRequest::Attach),
460+
_ => Err(format!("Unknown request type: {}", request_type)),
461+
}
462+
}
270463
}
271464

272465
zed::register_extension!(EmmyLuaExtension);

0 commit comments

Comments
 (0)