Skip to content

Commit 05d01a4

Browse files
authored
Merge #4304 from isaacs/isaacs/deno-ts_ls-inference
2 parents f4e9d36 + 7701661 commit 05d01a4

4 files changed

Lines changed: 136 additions & 24 deletions

File tree

lsp/denols.lua

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,37 @@
1212
--- "ts=typescript"
1313
--- }
1414
--- ```
15+
---
16+
--- Some care must be taken here to correctly infer whether a file is part of a Deno program, or a TS program that
17+
--- expects to run in Node or Web Browsers. This supports having a Deno module that is a part of a mostly-not-Deno
18+
--- monorepo. We do this by finding the nearest package manager lock file, and the nearest deno.json or deno.jsonc.
19+
--- Note that this means that without a deno.json, deno.jsonc, or deno.lock file, this LSP client will not attach.
20+
---
21+
--- Example:
22+
---
23+
--- ```
24+
--- project-root
25+
--- +-- node_modules/...
26+
--- +-- package-lock.json
27+
--- +-- package.json
28+
--- +-- packages
29+
--- +-- deno-module
30+
--- | +-- deno.json
31+
--- | +-- package.json <-- It's normal for Deno projects to have package.json files!
32+
--- | +-- src
33+
--- | +-- index.ts <-- this is a Deno file
34+
--- +-- node-module
35+
--- +-- package.json
36+
--- +-- src
37+
--- +-- index.ts <-- a non-Deno file (ie, should use ts_ls or tsgo)
38+
--- ```
39+
---
40+
--- From the file being edited, we walk up to find the nearest package manager lockfile. This is PROJECT ROOT.
41+
--- From the file being edited, find the nearest deno.json or deno.jsonc. This is DENO ROOT.
42+
--- From the file being edited, find the nearest deno.lock. This is DENO LOCK ROOT
43+
--- If DENO LOCK ROOT is found, and PROJECT ROOT is missing or shorter, then this is a deno file, and we attach.
44+
--- If DENO ROOT is found, and it's longer than or equal to PROJECT ROOT, then this is a Deno file, and we attach.
45+
--- Otherwise, we abort, because this is a non-Deno TS file.
1546

1647
local lsp = vim.lsp
1748

@@ -77,21 +108,23 @@ return {
77108
},
78109
root_dir = function(bufnr, on_dir)
79110
-- The project root is where the LSP can be started from
80-
local root_markers = { 'deno.lock' }
111+
local root_markers = { 'deno.lock', 'deno.json', 'deno.jsonc' }
81112
-- Give the root markers equal priority by wrapping them in a table
82113
root_markers = vim.fn.has('nvim-0.11.3') == 1 and { root_markers, { '.git' } }
83114
or vim.list_extend(root_markers, { '.git' })
84-
-- exclude non-deno projects (npm, yarn, pnpm, bun)
85-
local non_deno_path = vim.fs.root(
86-
bufnr,
87-
{ 'package.json', 'package-lock.json', 'yarn.lock', 'pnpm-lock.yaml', 'bun.lockb', 'bun.lock' }
88-
)
115+
-- only include deno projects
116+
local deno_root = vim.fs.root(bufnr, { 'deno.json', 'deno.jsonc' })
117+
local deno_lock_root = vim.fs.root(bufnr, { 'deno.lock' })
89118
local project_root = vim.fs.root(bufnr, root_markers)
90-
if non_deno_path and (not project_root or #non_deno_path >= #project_root) then
91-
return
119+
if
120+
(deno_lock_root and (not project_root or #deno_lock_root > #project_root))
121+
or (deno_root and (not project_root or #deno_root >= #project_root))
122+
then
123+
-- deno config is closer than or equal to package manager lock,
124+
-- or deno lock is closer than package manager lock. Attach at the project root,
125+
-- or deno lock or deno config path. At least one of these is always set at this point.
126+
on_dir(project_root or deno_lock_root or deno_root)
92127
end
93-
-- We fallback to the current working directory if no project root is found
94-
on_dir(project_root or vim.fn.getcwd())
95128
end,
96129
settings = {
97130
deno = {

lsp/ts_ls.lua

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,36 @@
4242
---
4343
--- It is recommended to use the same version of TypeScript in all packages, and therefore have it available in your workspace root. The location of the TypeScript binary will be determined automatically, but only once.
4444
---
45+
--- Some care must be taken here to correctly infer whether a file is part of a Deno program, or a TS program that
46+
--- expects to run in Node or Web Browsers. This supports having a Deno module using the denols LSP as a part of a
47+
--- mostly-not-Deno monorepo. We do this by finding the nearest package manager lock file, and the nearest deno.json
48+
--- or deno.jsonc.
49+
---
50+
--- Example:
51+
---
52+
--- ```
53+
--- project-root
54+
--- +-- node_modules/...
55+
--- +-- package-lock.json
56+
--- +-- package.json
57+
--- +-- packages
58+
--- +-- deno-module
59+
--- | +-- deno.json
60+
--- | +-- package.json <-- It's normal for Deno projects to have package.json files!
61+
--- | +-- src
62+
--- | +-- index.ts <-- this is a Deno file
63+
--- +-- node-module
64+
--- +-- package.json
65+
--- +-- src
66+
--- +-- index.ts <-- a non-Deno file (ie, should use ts_ls or tsgols)
67+
--- ```
68+
---
69+
--- From the file being edited, we walk up to find the nearest package manager lockfile. This is PROJECT ROOT.
70+
--- From the file being edited, find the nearest deno.json or deno.jsonc. This is DENO ROOT.
71+
--- From the file being edited, find the nearest deno.lock. This is DENO LOCK ROOT
72+
--- If DENO LOCK ROOT is found, and PROJECT ROOT is missing or shorter, then this is a deno file, and we abort.
73+
--- If DENO ROOT is found, and it's longer than or equal to PROJECT ROOT, then this is a Deno file, and we abort.
74+
--- Otherwise, attach at PROJECT ROOT, or the cwd if not found.
4575

4676
---@type vim.lsp.Config
4777
return {
@@ -65,11 +95,18 @@ return {
6595
root_markers = vim.fn.has('nvim-0.11.3') == 1 and { root_markers, { '.git' } }
6696
or vim.list_extend(root_markers, { '.git' })
6797
-- exclude deno
68-
local deno_path = vim.fs.root(bufnr, { 'deno.json', 'deno.jsonc', 'deno.lock' })
98+
local deno_root = vim.fs.root(bufnr, { 'deno.json', 'deno.jsonc' })
99+
local deno_lock_root = vim.fs.root(bufnr, { 'deno.lock' })
69100
local project_root = vim.fs.root(bufnr, root_markers)
70-
if deno_path and (not project_root or #deno_path >= #project_root) then
101+
if deno_lock_root and (not project_root or #deno_lock_root > #project_root) then
102+
-- deno lock is closer than package manager lock, abort
103+
return
104+
end
105+
if deno_root and (not project_root or #deno_root >= #project_root) then
106+
-- deno config is closer than or equal to package manager lock, abort
71107
return
72108
end
109+
-- project is standard TS, not deno
73110
-- We fallback to the current working directory if no project root is found
74111
on_dir(project_root or vim.fn.getcwd())
75112
end,

lsp/tsgo.lua

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,36 @@
1313
---
1414
--- It is recommended to use the same version of TypeScript in all packages, and therefore have it available in your workspace root. The location of the TypeScript binary will be determined automatically, but only once.
1515
---
16+
--- Some care must be taken here to correctly infer whether a file is part of a Deno program, or a TS program that
17+
--- expects to run in Node or Web Browsers. This supports having a Deno module using the denols LSP as a part of a
18+
--- mostly-not-Deno monorepo. We do this by finding the nearest package manager lock file, and the nearest deno.json
19+
--- or deno.jsonc.
20+
---
21+
--- Example:
22+
---
23+
--- ```
24+
--- project-root
25+
--- +-- node_modules/...
26+
--- +-- package-lock.json
27+
--- +-- package.json
28+
--- +-- packages
29+
--- +-- deno-module
30+
--- | +-- deno.json
31+
--- | +-- package.json <-- It's normal for Deno projects to have package.json files!
32+
--- | +-- src
33+
--- | +-- index.ts <-- this is a Deno file
34+
--- +-- node-module
35+
--- +-- package.json
36+
--- +-- src
37+
--- +-- index.ts <-- a non-Deno file (ie, should use ts_ls or tsgols)
38+
--- ```
39+
---
40+
--- From the file being edited, we walk up to find the nearest package manager lockfile. This is PROJECT ROOT.
41+
--- From the file being edited, find the nearest deno.json or deno.jsonc. This is DENO ROOT.
42+
--- From the file being edited, find the nearest deno.lock. This is DENO LOCK ROOT
43+
--- If DENO LOCK ROOT is found, and PROJECT ROOT is missing or shorter, then this is a deno file, and we abort.
44+
--- If DENO ROOT is found, and it's longer than or equal to PROJECT ROOT, then this is a Deno file, and we abort.
45+
--- Otherwise, attach at PROJECT ROOT, or the cwd if not found.
1646

1747
---@type vim.lsp.Config
1848
return {
@@ -40,14 +70,19 @@ return {
4070
root_markers = vim.fn.has('nvim-0.11.3') == 1 and { root_markers, { '.git' } }
4171
or vim.list_extend(root_markers, { '.git' })
4272

43-
-- exclude deno
44-
if vim.fs.root(bufnr, { 'deno.json', 'deno.jsonc', 'deno.lock' }) then
73+
local deno_root = vim.fs.root(bufnr, { 'deno.json', 'deno.jsonc' })
74+
local deno_lock_root = vim.fs.root(bufnr, { 'deno.lock' })
75+
local project_root = vim.fs.root(bufnr, root_markers)
76+
if deno_lock_root and (not project_root or #deno_lock_root > #project_root) then
77+
-- deno lock is closer than package manager lock, abort
4578
return
4679
end
47-
80+
if deno_root and (not project_root or #deno_root >= #project_root) then
81+
-- deno config is closer than or equal to package manager lock, abort
82+
return
83+
end
84+
-- project is standard TS, not deno
4885
-- We fallback to the current working directory if no project root is found
49-
local project_root = vim.fs.root(bufnr, root_markers) or vim.fn.getcwd()
50-
51-
on_dir(project_root)
86+
on_dir(project_root or vim.fn.getcwd())
5287
end,
5388
}

lsp/vtsls.lua

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@
6464
--- This works without the need of spawning multiple instances of `vtsls`, saving memory.
6565
---
6666
--- It is recommended to use the same version of TypeScript in all packages, and therefore have it available in your workspace root. The location of the TypeScript binary will be determined automatically, but only once.
67+
---
68+
--- This includes the same Deno-excluding logic from `ts_ls`. It is not recommended to enable both `vtsls` and `ts_ls` at the same time!
6769

6870
---@type vim.lsp.Config
6971
return {
@@ -88,15 +90,20 @@ return {
8890
-- Give the root markers equal priority by wrapping them in a table
8991
root_markers = vim.fn.has('nvim-0.11.3') == 1 and { root_markers, { '.git' } }
9092
or vim.list_extend(root_markers, { '.git' })
91-
9293
-- exclude deno
93-
if vim.fs.root(bufnr, { 'deno.json', 'deno.jsonc', 'deno.lock' }) then
94+
local deno_root = vim.fs.root(bufnr, { 'deno.json', 'deno.jsonc' })
95+
local deno_lock_root = vim.fs.root(bufnr, { 'deno.lock' })
96+
local project_root = vim.fs.root(bufnr, root_markers)
97+
if deno_lock_root and (not project_root or #deno_lock_root > #project_root) then
98+
-- deno lock is closer than package manager lock, abort
9499
return
95100
end
96-
101+
if deno_root and (not project_root or #deno_root >= #project_root) then
102+
-- deno config is closer than or equal to package manager lock, abort
103+
return
104+
end
105+
-- project is standard TS, not deno
97106
-- We fallback to the current working directory if no project root is found
98-
local project_root = vim.fs.root(bufnr, root_markers) or vim.fn.getcwd()
99-
100-
on_dir(project_root)
107+
on_dir(project_root or vim.fn.getcwd())
101108
end,
102109
}

0 commit comments

Comments
 (0)