Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 63 additions & 51 deletions lsp/angularls.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,72 +14,84 @@

-- Angular requires a node_modules directory to probe for @angular/language-service and typescript
-- in order to use your projects configured versions.
local root_dir = vim.fn.getcwd()
local node_modules_dir = vim.fs.find('node_modules', { path = root_dir, upward = true })[1]
local project_root = node_modules_dir and vim.fs.dirname(node_modules_dir) or '?'
local fs, fn, uv = vim.fs, vim.fn, vim.uv

local function get_probe_dir()
return project_root and (project_root .. '/node_modules') or ''
end
local function collect_node_modules(root_dir)
local results = {}

local function get_angular_core_version()
if not project_root then
return ''
local project_node = fs.joinpath(root_dir, 'node_modules')
if uv.fs_stat(project_node) then
table.insert(results, project_node)
end

local package_json = project_root .. '/package.json'
if not vim.uv.fs_stat(package_json) then
return ''
local ngserver_exe = fn.exepath('ngserver')
if ngserver_exe and #ngserver_exe > 0 then
local realpath = uv.fs_realpath(ngserver_exe) or ngserver_exe
local candidate = fs.normalize(fs.joinpath(fs.dirname(realpath), '../../node_modules'))
if uv.fs_stat(candidate) then
table.insert(results, candidate)
end
end

local contents = io.open(package_json):read '*a'
local json = vim.json.decode(contents)
if not json.dependencies then
return ''
local internal_servers = fn.globpath(fn.stdpath('data'), '**/node_modules/.bin/ngserver', true, true)
for _, exe in ipairs(internal_servers) do
local realpath = uv.fs_realpath(exe) or exe
local candidate = fs.normalize(fs.joinpath(fs.dirname(realpath), '../../node_modules'))
if uv.fs_stat(candidate) then
table.insert(results, candidate)
end
end

local angular_core_version = json.dependencies['@angular/core']

angular_core_version = angular_core_version and angular_core_version:match('%d+%.%d+%.%d+')

return angular_core_version
return results
end

local default_probe_dir = get_probe_dir()
local default_angular_core_version = get_angular_core_version()
local function get_angular_core_version(root_dir)
local package_json = fs.joinpath(root_dir, 'package.json')
if not uv.fs_stat(package_json) then
return ''
end

local ok, f = pcall(io.open, package_json, 'r')
if not ok or not f then
return ''
end

-- structure should be like
-- - $EXTENSION_PATH
-- - @angular
-- - language-server
-- - bin
-- - ngserver
-- - typescript
local ngserver_exe = vim.fn.exepath('ngserver')
local ngserver_path = #(ngserver_exe or '') > 0 and vim.fs.dirname(vim.uv.fs_realpath(ngserver_exe)) or '?'
local extension_path = vim.fs.normalize(vim.fs.joinpath(ngserver_path, '../../../'))
local json = vim.json.decode(f:read('*a')) or {}
f:close()

-- angularls will get module by `require.resolve(PROBE_PATH, MODULE_NAME)` of nodejs
local ts_probe_dirs = vim.iter({ extension_path, default_probe_dir }):join(',')
local ng_probe_dirs = vim
.iter({ extension_path, default_probe_dir })
:map(function(p)
return vim.fs.joinpath(p, '/@angular/language-server/node_modules')
end)
:join(',')
local version = (json.dependencies or {})['@angular/core'] or ''
return version:match('%d+%.%d+%.%d+') or ''
end

---@type vim.lsp.Config
return {
cmd = {
'ngserver',
'--stdio',
'--tsProbeLocations',
ts_probe_dirs,
'--ngProbeLocations',
ng_probe_dirs,
'--angularCoreVersion',
default_angular_core_version,
},
cmd = function(dispatchers, config)
local root_dir = config.root or fn.getcwd()
local node_paths = collect_node_modules(root_dir)

local ts_probe = table.concat(node_paths, ',')
local ng_probe = table.concat(
vim
.iter(node_paths)
:map(function(p)
return fs.joinpath(p, '@angular/language-server/node_modules')
end)
:totable(),
','
)
local cmd = {
'ngserver',
'--stdio',
'--tsProbeLocations',
ts_probe,
'--ngProbeLocations',
ng_probe,
'--angularCoreVersion',
get_angular_core_version(root_dir),
}
return vim.lsp.rpc.start(cmd, dispatchers)
end,

filetypes = { 'typescript', 'html', 'typescriptreact', 'typescript.tsx', 'htmlangular' },
root_markers = { 'angular.json', 'nx.json' },
}