fake-lsp-server.lua 25 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061
  1. local protocol = require 'vim.lsp.protocol'
  2. -- Logs to $NVIM_LOG_FILE.
  3. --
  4. -- TODO(justinmk): remove after https://github.com/neovim/neovim/pull/7062
  5. local function log(loglevel, area, msg)
  6. vim.fn.writefile({ string.format('%s %s: %s', loglevel, area, msg) }, vim.env.NVIM_LOG_FILE, 'a')
  7. end
  8. local function message_parts(sep, ...)
  9. local parts = {}
  10. for i = 1, select('#', ...) do
  11. local arg = select(i, ...)
  12. if arg ~= nil then
  13. table.insert(parts, arg)
  14. end
  15. end
  16. return table.concat(parts, sep)
  17. end
  18. -- Assert utility methods
  19. local function assert_eq(a, b, ...)
  20. if not vim.deep_equal(a, b) then
  21. error(
  22. message_parts(
  23. ': ',
  24. ...,
  25. 'assert_eq failed',
  26. string.format(
  27. 'left == %q, right == %q',
  28. table.concat(vim.split(vim.inspect(a), '\n'), ''),
  29. table.concat(vim.split(vim.inspect(b), '\n'), '')
  30. )
  31. )
  32. )
  33. end
  34. end
  35. local function format_message_with_content_length(encoded_message)
  36. return table.concat {
  37. 'Content-Length: ',
  38. tostring(#encoded_message),
  39. '\r\n\r\n',
  40. encoded_message,
  41. }
  42. end
  43. local function read_message()
  44. local line = io.read('*l')
  45. local length = line:lower():match('content%-length:%s*(%d+)')
  46. return vim.json.decode(io.read(2 + length):sub(2))
  47. end
  48. local function send(payload)
  49. io.stdout:write(format_message_with_content_length(vim.json.encode(payload)))
  50. end
  51. local function respond(id, err, result)
  52. assert(type(id) == 'number', 'id must be a number')
  53. send { jsonrpc = '2.0', id = id, error = err, result = result }
  54. end
  55. local function notify(method, params)
  56. assert(type(method) == 'string', 'method must be a string')
  57. send { method = method, params = params or {} }
  58. end
  59. local function expect_notification(method, params, ...)
  60. local message = read_message()
  61. assert_eq(method, message.method, ..., 'expect_notification', 'method')
  62. if params then
  63. assert_eq(params, message.params, ..., 'expect_notification', method, 'params')
  64. assert_eq(
  65. { jsonrpc = '2.0', method = method, params = params },
  66. message,
  67. ...,
  68. 'expect_notification',
  69. 'message'
  70. )
  71. end
  72. end
  73. local function expect_request(method, handler, ...)
  74. local req = read_message()
  75. assert_eq(method, req.method, ..., 'expect_request', 'method')
  76. local err, result = handler(req.params)
  77. respond(req.id, err, result)
  78. end
  79. io.stderr:setvbuf('no')
  80. local function skeleton(config)
  81. local on_init = assert(config.on_init)
  82. local body = assert(config.body)
  83. expect_request('initialize', function(params)
  84. return nil, on_init(params)
  85. end)
  86. expect_notification('initialized', {})
  87. body()
  88. expect_request('shutdown', function()
  89. return nil, {}
  90. end)
  91. expect_notification('exit', nil)
  92. end
  93. -- The actual tests.
  94. local tests = {}
  95. function tests.basic_init()
  96. skeleton {
  97. on_init = function(params)
  98. assert_eq(params.workDoneToken, '1')
  99. return {
  100. capabilities = {
  101. textDocumentSync = protocol.TextDocumentSyncKind.None,
  102. },
  103. }
  104. end,
  105. body = function()
  106. notify('test')
  107. end,
  108. }
  109. end
  110. function tests.basic_init_did_change_configuration()
  111. skeleton({
  112. on_init = function(_)
  113. return {
  114. capabilities = {},
  115. }
  116. end,
  117. body = function()
  118. expect_notification('workspace/didChangeConfiguration', { settings = { dummy = 1 } })
  119. end,
  120. })
  121. end
  122. function tests.check_workspace_configuration()
  123. skeleton {
  124. on_init = function(_params)
  125. return { capabilities = {} }
  126. end,
  127. body = function()
  128. notify('start')
  129. notify('workspace/configuration', {
  130. items = {
  131. { section = 'testSetting1' },
  132. { section = 'testSetting2' },
  133. { section = 'test.Setting3' },
  134. { section = 'test.Setting4' },
  135. },
  136. })
  137. expect_notification('workspace/configuration', { true, false, 'nested', vim.NIL })
  138. notify('shutdown')
  139. end,
  140. }
  141. end
  142. function tests.prepare_rename_nil()
  143. skeleton {
  144. on_init = function()
  145. return {
  146. capabilities = {
  147. renameProvider = {
  148. prepareProvider = true,
  149. },
  150. },
  151. }
  152. end,
  153. body = function()
  154. notify('start')
  155. expect_request('textDocument/prepareRename', function()
  156. return nil, nil
  157. end)
  158. notify('shutdown')
  159. end,
  160. }
  161. end
  162. function tests.prepare_rename_placeholder()
  163. skeleton {
  164. on_init = function()
  165. return {
  166. capabilities = {
  167. renameProvider = {
  168. prepareProvider = true,
  169. },
  170. },
  171. }
  172. end,
  173. body = function()
  174. notify('start')
  175. expect_request('textDocument/prepareRename', function()
  176. return nil, { placeholder = 'placeholder' }
  177. end)
  178. expect_request('textDocument/rename', function(params)
  179. assert_eq(params.newName, 'renameto')
  180. return nil, nil
  181. end)
  182. notify('shutdown')
  183. end,
  184. }
  185. end
  186. function tests.prepare_rename_range()
  187. skeleton {
  188. on_init = function()
  189. return {
  190. capabilities = {
  191. renameProvider = {
  192. prepareProvider = true,
  193. },
  194. },
  195. }
  196. end,
  197. body = function()
  198. notify('start')
  199. expect_request('textDocument/prepareRename', function()
  200. return nil,
  201. {
  202. start = { line = 1, character = 8 },
  203. ['end'] = { line = 1, character = 12 },
  204. }
  205. end)
  206. expect_request('textDocument/rename', function(params)
  207. assert_eq(params.newName, 'renameto')
  208. return nil, nil
  209. end)
  210. notify('shutdown')
  211. end,
  212. }
  213. end
  214. function tests.prepare_rename_error()
  215. skeleton {
  216. on_init = function()
  217. return {
  218. capabilities = {
  219. renameProvider = {
  220. prepareProvider = true,
  221. },
  222. },
  223. }
  224. end,
  225. body = function()
  226. notify('start')
  227. expect_request('textDocument/prepareRename', function()
  228. return {}, nil
  229. end)
  230. notify('shutdown')
  231. end,
  232. }
  233. end
  234. function tests.basic_check_capabilities()
  235. skeleton {
  236. on_init = function(params)
  237. local expected_capabilities = protocol.make_client_capabilities()
  238. assert_eq(params.capabilities, expected_capabilities)
  239. return {
  240. capabilities = {
  241. textDocumentSync = protocol.TextDocumentSyncKind.Full,
  242. codeLensProvider = false,
  243. },
  244. }
  245. end,
  246. body = function() end,
  247. }
  248. end
  249. function tests.text_document_save_did_open()
  250. skeleton {
  251. on_init = function()
  252. return {
  253. capabilities = {
  254. textDocumentSync = {
  255. save = true,
  256. },
  257. },
  258. }
  259. end,
  260. body = function()
  261. notify('start')
  262. expect_notification('textDocument/didClose')
  263. expect_notification('textDocument/didOpen')
  264. expect_notification('textDocument/didSave')
  265. notify('shutdown')
  266. end,
  267. }
  268. end
  269. function tests.text_document_sync_save_bool()
  270. skeleton {
  271. on_init = function()
  272. return {
  273. capabilities = {
  274. textDocumentSync = {
  275. save = true,
  276. },
  277. },
  278. }
  279. end,
  280. body = function()
  281. notify('start')
  282. expect_notification('textDocument/didSave', { textDocument = { uri = 'file://' } })
  283. notify('shutdown')
  284. end,
  285. }
  286. end
  287. function tests.text_document_sync_save_includeText()
  288. skeleton {
  289. on_init = function()
  290. return {
  291. capabilities = {
  292. textDocumentSync = {
  293. save = {
  294. includeText = true,
  295. },
  296. },
  297. },
  298. }
  299. end,
  300. body = function()
  301. notify('start')
  302. expect_notification('textDocument/didSave', {
  303. textDocument = {
  304. uri = 'file://',
  305. },
  306. text = 'help me\n',
  307. })
  308. notify('shutdown')
  309. end,
  310. }
  311. end
  312. function tests.capabilities_for_client_supports_method()
  313. skeleton {
  314. on_init = function(params)
  315. local expected_capabilities = protocol.make_client_capabilities()
  316. assert_eq(params.capabilities, expected_capabilities)
  317. return {
  318. capabilities = {
  319. textDocumentSync = protocol.TextDocumentSyncKind.Full,
  320. completionProvider = true,
  321. hoverProvider = true,
  322. renameProvider = false,
  323. definitionProvider = false,
  324. referencesProvider = false,
  325. codeLensProvider = { resolveProvider = true },
  326. },
  327. }
  328. end,
  329. body = function() end,
  330. }
  331. end
  332. function tests.check_forward_request_cancelled()
  333. skeleton {
  334. on_init = function(_)
  335. return { capabilities = {} }
  336. end,
  337. body = function()
  338. expect_request('error_code_test', function()
  339. return { code = -32800 }, nil, { method = 'error_code_test', client_id = 1 }
  340. end)
  341. notify('finish')
  342. end,
  343. }
  344. end
  345. function tests.check_forward_content_modified()
  346. skeleton {
  347. on_init = function(_)
  348. return { capabilities = {} }
  349. end,
  350. body = function()
  351. expect_request('error_code_test', function()
  352. return { code = -32801 }, nil, { method = 'error_code_test', client_id = 1 }
  353. end)
  354. expect_notification('finish')
  355. notify('finish')
  356. end,
  357. }
  358. end
  359. function tests.check_forward_server_cancelled()
  360. skeleton {
  361. on_init = function()
  362. return { capabilities = {} }
  363. end,
  364. body = function()
  365. expect_request('error_code_test', function()
  366. return { code = -32802 }, nil, { method = 'error_code_test', client_id = 1 }
  367. end)
  368. expect_notification('finish')
  369. notify('finish')
  370. end,
  371. }
  372. end
  373. function tests.check_pending_request_tracked()
  374. skeleton {
  375. on_init = function(_)
  376. return { capabilities = {} }
  377. end,
  378. body = function()
  379. local msg = read_message()
  380. assert_eq('slow_request', msg.method)
  381. expect_notification('release')
  382. respond(msg.id, nil, {})
  383. expect_notification('finish')
  384. notify('finish')
  385. end,
  386. }
  387. end
  388. function tests.check_cancel_request_tracked()
  389. skeleton {
  390. on_init = function(_)
  391. return { capabilities = {} }
  392. end,
  393. body = function()
  394. local msg = read_message()
  395. assert_eq('slow_request', msg.method)
  396. expect_notification('$/cancelRequest', { id = msg.id })
  397. expect_notification('release')
  398. respond(msg.id, { code = -32800 }, nil)
  399. notify('finish')
  400. end,
  401. }
  402. end
  403. function tests.check_tracked_requests_cleared()
  404. skeleton {
  405. on_init = function(_)
  406. return { capabilities = {} }
  407. end,
  408. body = function()
  409. local msg = read_message()
  410. assert_eq('slow_request', msg.method)
  411. expect_notification('$/cancelRequest', { id = msg.id })
  412. expect_notification('release')
  413. respond(msg.id, nil, {})
  414. expect_notification('finish')
  415. notify('finish')
  416. end,
  417. }
  418. end
  419. function tests.basic_finish()
  420. skeleton {
  421. on_init = function(params)
  422. local expected_capabilities = protocol.make_client_capabilities()
  423. assert_eq(params.capabilities, expected_capabilities)
  424. return {
  425. capabilities = {
  426. textDocumentSync = protocol.TextDocumentSyncKind.Full,
  427. },
  428. }
  429. end,
  430. body = function()
  431. expect_notification('finish')
  432. notify('finish')
  433. end,
  434. }
  435. end
  436. function tests.basic_check_buffer_open()
  437. skeleton {
  438. on_init = function(params)
  439. local expected_capabilities = protocol.make_client_capabilities()
  440. assert_eq(params.capabilities, expected_capabilities)
  441. return {
  442. capabilities = {
  443. textDocumentSync = protocol.TextDocumentSyncKind.Full,
  444. },
  445. }
  446. end,
  447. body = function()
  448. notify('start')
  449. expect_notification('textDocument/didOpen', {
  450. textDocument = {
  451. languageId = '',
  452. text = table.concat({ 'testing', '123' }, '\n') .. '\n',
  453. uri = 'file://',
  454. version = 0,
  455. },
  456. })
  457. expect_notification('finish')
  458. notify('finish')
  459. end,
  460. }
  461. end
  462. function tests.basic_check_buffer_open_and_change()
  463. skeleton {
  464. on_init = function(params)
  465. local expected_capabilities = protocol.make_client_capabilities()
  466. assert_eq(params.capabilities, expected_capabilities)
  467. return {
  468. capabilities = {
  469. textDocumentSync = protocol.TextDocumentSyncKind.Full,
  470. },
  471. }
  472. end,
  473. body = function()
  474. notify('start')
  475. expect_notification('textDocument/didOpen', {
  476. textDocument = {
  477. languageId = '',
  478. text = table.concat({ 'testing', '123' }, '\n') .. '\n',
  479. uri = 'file://',
  480. version = 0,
  481. },
  482. })
  483. expect_notification('textDocument/didChange', {
  484. textDocument = {
  485. uri = 'file://',
  486. version = 3,
  487. },
  488. contentChanges = {
  489. { text = table.concat({ 'testing', 'boop' }, '\n') .. '\n' },
  490. },
  491. })
  492. expect_notification('finish')
  493. notify('finish')
  494. end,
  495. }
  496. end
  497. function tests.basic_check_buffer_open_and_change_noeol()
  498. skeleton {
  499. on_init = function(params)
  500. local expected_capabilities = protocol.make_client_capabilities()
  501. assert_eq(params.capabilities, expected_capabilities)
  502. return {
  503. capabilities = {
  504. textDocumentSync = protocol.TextDocumentSyncKind.Full,
  505. },
  506. }
  507. end,
  508. body = function()
  509. notify('start')
  510. expect_notification('textDocument/didOpen', {
  511. textDocument = {
  512. languageId = '',
  513. text = table.concat({ 'testing', '123' }, '\n'),
  514. uri = 'file://',
  515. version = 0,
  516. },
  517. })
  518. expect_notification('textDocument/didChange', {
  519. textDocument = {
  520. uri = 'file://',
  521. version = 3,
  522. },
  523. contentChanges = {
  524. { text = table.concat({ 'testing', 'boop' }, '\n') },
  525. },
  526. })
  527. expect_notification('finish')
  528. notify('finish')
  529. end,
  530. }
  531. end
  532. function tests.basic_check_buffer_open_and_change_multi()
  533. skeleton {
  534. on_init = function(params)
  535. local expected_capabilities = protocol.make_client_capabilities()
  536. assert_eq(params.capabilities, expected_capabilities)
  537. return {
  538. capabilities = {
  539. textDocumentSync = protocol.TextDocumentSyncKind.Full,
  540. },
  541. }
  542. end,
  543. body = function()
  544. notify('start')
  545. expect_notification('textDocument/didOpen', {
  546. textDocument = {
  547. languageId = '',
  548. text = table.concat({ 'testing', '123' }, '\n') .. '\n',
  549. uri = 'file://',
  550. version = 0,
  551. },
  552. })
  553. expect_notification('textDocument/didChange', {
  554. textDocument = {
  555. uri = 'file://',
  556. version = 3,
  557. },
  558. contentChanges = {
  559. { text = table.concat({ 'testing', '321' }, '\n') .. '\n' },
  560. },
  561. })
  562. expect_notification('textDocument/didChange', {
  563. textDocument = {
  564. uri = 'file://',
  565. version = 4,
  566. },
  567. contentChanges = {
  568. { text = table.concat({ 'testing', 'boop' }, '\n') .. '\n' },
  569. },
  570. })
  571. expect_notification('finish')
  572. notify('finish')
  573. end,
  574. }
  575. end
  576. function tests.basic_check_buffer_open_and_change_multi_and_close()
  577. skeleton {
  578. on_init = function(params)
  579. local expected_capabilities = protocol.make_client_capabilities()
  580. assert_eq(params.capabilities, expected_capabilities)
  581. return {
  582. capabilities = {
  583. textDocumentSync = protocol.TextDocumentSyncKind.Full,
  584. },
  585. }
  586. end,
  587. body = function()
  588. notify('start')
  589. expect_notification('textDocument/didOpen', {
  590. textDocument = {
  591. languageId = '',
  592. text = table.concat({ 'testing', '123' }, '\n') .. '\n',
  593. uri = 'file://',
  594. version = 0,
  595. },
  596. })
  597. expect_notification('textDocument/didChange', {
  598. textDocument = {
  599. uri = 'file://',
  600. version = 3,
  601. },
  602. contentChanges = {
  603. { text = table.concat({ 'testing', '321' }, '\n') .. '\n' },
  604. },
  605. })
  606. expect_notification('textDocument/didChange', {
  607. textDocument = {
  608. uri = 'file://',
  609. version = 4,
  610. },
  611. contentChanges = {
  612. { text = table.concat({ 'testing', 'boop' }, '\n') .. '\n' },
  613. },
  614. })
  615. expect_notification('textDocument/didClose', {
  616. textDocument = {
  617. uri = 'file://',
  618. },
  619. })
  620. expect_notification('finish')
  621. notify('finish')
  622. end,
  623. }
  624. end
  625. function tests.basic_check_buffer_open_and_change_incremental()
  626. skeleton {
  627. on_init = function(params)
  628. local expected_capabilities = protocol.make_client_capabilities()
  629. assert_eq(params.capabilities, expected_capabilities)
  630. return {
  631. capabilities = {
  632. textDocumentSync = {
  633. openClose = true,
  634. change = protocol.TextDocumentSyncKind.Incremental,
  635. willSave = true,
  636. willSaveWaitUntil = true,
  637. save = {
  638. includeText = true,
  639. },
  640. },
  641. },
  642. }
  643. end,
  644. body = function()
  645. notify('start')
  646. expect_notification('textDocument/didOpen', {
  647. textDocument = {
  648. languageId = '',
  649. text = table.concat({ 'testing', '123' }, '\n') .. '\n',
  650. uri = 'file://',
  651. version = 0,
  652. },
  653. })
  654. expect_notification('textDocument/didChange', {
  655. textDocument = {
  656. uri = 'file://',
  657. version = 3,
  658. },
  659. contentChanges = {
  660. {
  661. range = {
  662. start = { line = 1, character = 3 },
  663. ['end'] = { line = 1, character = 3 },
  664. },
  665. rangeLength = 0,
  666. text = 'boop',
  667. },
  668. },
  669. })
  670. expect_notification('finish')
  671. notify('finish')
  672. end,
  673. }
  674. end
  675. function tests.basic_check_buffer_open_and_change_incremental_editing()
  676. skeleton {
  677. on_init = function(params)
  678. local expected_capabilities = protocol.make_client_capabilities()
  679. assert_eq(params.capabilities, expected_capabilities)
  680. return {
  681. capabilities = {
  682. textDocumentSync = protocol.TextDocumentSyncKind.Incremental,
  683. },
  684. }
  685. end,
  686. body = function()
  687. notify('start')
  688. expect_notification('textDocument/didOpen', {
  689. textDocument = {
  690. languageId = '',
  691. text = table.concat({ 'testing', '123' }, '\n'),
  692. uri = 'file://',
  693. version = 0,
  694. },
  695. })
  696. expect_notification('textDocument/didChange', {
  697. textDocument = {
  698. uri = 'file://',
  699. version = 3,
  700. },
  701. contentChanges = {
  702. {
  703. range = {
  704. start = { line = 0, character = 0 },
  705. ['end'] = { line = 1, character = 0 },
  706. },
  707. rangeLength = 4,
  708. text = 'testing\n\n',
  709. },
  710. },
  711. })
  712. expect_notification('finish')
  713. notify('finish')
  714. end,
  715. }
  716. end
  717. function tests.invalid_header()
  718. io.stdout:write('Content-length: \r\n')
  719. end
  720. function tests.decode_nil()
  721. skeleton {
  722. on_init = function(_)
  723. return { capabilities = {} }
  724. end,
  725. body = function()
  726. notify('start')
  727. notify('workspace/executeCommand', {
  728. arguments = { 'EXTRACT_METHOD', { metadata = { field = vim.NIL } }, 3, 0, 6123, vim.NIL },
  729. command = 'refactor.perform',
  730. title = 'EXTRACT_METHOD',
  731. })
  732. notify('finish')
  733. end,
  734. }
  735. end
  736. function tests.code_action_with_resolve()
  737. skeleton {
  738. on_init = function()
  739. return {
  740. capabilities = {
  741. codeActionProvider = {
  742. resolveProvider = true,
  743. },
  744. },
  745. }
  746. end,
  747. body = function()
  748. notify('start')
  749. local cmd = {
  750. title = 'Command 1',
  751. command = 'dummy1',
  752. }
  753. expect_request('textDocument/codeAction', function()
  754. return nil, { cmd }
  755. end)
  756. expect_request('codeAction/resolve', function()
  757. return nil, cmd
  758. end)
  759. notify('shutdown')
  760. end,
  761. }
  762. end
  763. function tests.code_action_server_side_command()
  764. skeleton({
  765. on_init = function()
  766. return {
  767. capabilities = {
  768. codeActionProvider = {
  769. resolveProvider = false,
  770. },
  771. executeCommandProvider = {
  772. commands = { 'dummy1' },
  773. },
  774. },
  775. }
  776. end,
  777. body = function()
  778. notify('start')
  779. local cmd = {
  780. title = 'Command 1',
  781. command = 'dummy1',
  782. }
  783. expect_request('textDocument/codeAction', function()
  784. return nil, { cmd }
  785. end)
  786. expect_request('workspace/executeCommand', function()
  787. return nil, cmd
  788. end)
  789. notify('shutdown')
  790. end,
  791. })
  792. end
  793. function tests.code_action_filter()
  794. skeleton {
  795. on_init = function()
  796. return {
  797. capabilities = {
  798. codeActionProvider = {
  799. resolveProvider = false,
  800. },
  801. },
  802. }
  803. end,
  804. body = function()
  805. notify('start')
  806. local action = {
  807. title = 'Action 1',
  808. command = 'command',
  809. }
  810. local preferred_action = {
  811. title = 'Action 2',
  812. isPreferred = true,
  813. command = 'preferred_command',
  814. }
  815. local type_annotate_action = {
  816. title = 'Action 3',
  817. kind = 'type-annotate',
  818. command = 'type_annotate_command',
  819. }
  820. local type_annotate_foo_action = {
  821. title = 'Action 4',
  822. kind = 'type-annotate.foo',
  823. command = 'type_annotate_foo_command',
  824. }
  825. expect_request('textDocument/codeAction', function()
  826. return nil, { action, preferred_action, type_annotate_action, type_annotate_foo_action }
  827. end)
  828. expect_request('textDocument/codeAction', function()
  829. return nil, { action, preferred_action, type_annotate_action, type_annotate_foo_action }
  830. end)
  831. notify('shutdown')
  832. end,
  833. }
  834. end
  835. function tests.clientside_commands()
  836. skeleton {
  837. on_init = function()
  838. return {
  839. capabilities = {},
  840. }
  841. end,
  842. body = function()
  843. notify('start')
  844. notify('shutdown')
  845. end,
  846. }
  847. end
  848. function tests.codelens_refresh_lock()
  849. skeleton {
  850. on_init = function()
  851. return {
  852. capabilities = {
  853. codeLensProvider = { resolveProvider = true },
  854. },
  855. }
  856. end,
  857. body = function()
  858. notify('start')
  859. expect_request('textDocument/codeLens', function()
  860. return { code = -32002, message = 'ServerNotInitialized' }, nil
  861. end)
  862. expect_request('textDocument/codeLens', function()
  863. local lenses = {
  864. {
  865. range = {
  866. start = { line = 0, character = 0 },
  867. ['end'] = { line = 0, character = 3 },
  868. },
  869. command = { title = 'Lens1', command = 'Dummy' },
  870. },
  871. }
  872. return nil, lenses
  873. end)
  874. expect_request('textDocument/codeLens', function()
  875. local lenses = {
  876. {
  877. range = {
  878. start = { line = 0, character = 0 },
  879. ['end'] = { line = 0, character = 3 },
  880. },
  881. command = { title = 'Lens2', command = 'Dummy' },
  882. },
  883. }
  884. return nil, lenses
  885. end)
  886. notify('shutdown')
  887. end,
  888. }
  889. end
  890. function tests.basic_formatting()
  891. skeleton {
  892. on_init = function()
  893. return {
  894. capabilities = {
  895. documentFormattingProvider = true,
  896. },
  897. }
  898. end,
  899. body = function()
  900. notify('start')
  901. expect_request('textDocument/formatting', function()
  902. return nil, {}
  903. end)
  904. notify('shutdown')
  905. end,
  906. }
  907. end
  908. function tests.range_formatting()
  909. skeleton {
  910. on_init = function()
  911. return {
  912. capabilities = {
  913. documentFormattingProvider = true,
  914. documentRangeFormattingProvider = true,
  915. },
  916. }
  917. end,
  918. body = function()
  919. notify('start')
  920. expect_request('textDocument/rangeFormatting', function()
  921. return nil, {}
  922. end)
  923. notify('shutdown')
  924. end,
  925. }
  926. end
  927. function tests.ranges_formatting()
  928. skeleton {
  929. on_init = function()
  930. return {
  931. capabilities = {
  932. documentFormattingProvider = true,
  933. documentRangeFormattingProvider = {
  934. rangesSupport = true,
  935. },
  936. },
  937. }
  938. end,
  939. body = function()
  940. notify('start')
  941. expect_request('textDocument/rangesFormatting', function()
  942. return nil, {}
  943. end)
  944. notify('shutdown')
  945. end,
  946. }
  947. end
  948. function tests.set_defaults_all_capabilities()
  949. skeleton {
  950. on_init = function(_)
  951. return {
  952. capabilities = {
  953. definitionProvider = true,
  954. completionProvider = true,
  955. documentRangeFormattingProvider = true,
  956. hoverProvider = true,
  957. },
  958. }
  959. end,
  960. body = function()
  961. notify('test')
  962. end,
  963. }
  964. end
  965. function tests.inlay_hint()
  966. skeleton {
  967. on_init = function(params)
  968. local expected_capabilities = protocol.make_client_capabilities()
  969. assert_eq(params.capabilities, expected_capabilities)
  970. return {
  971. capabilities = {
  972. inlayHintProvider = true,
  973. },
  974. }
  975. end,
  976. body = function()
  977. notify('start')
  978. expect_request('textDocument/inlayHint', function()
  979. return nil, {}
  980. end)
  981. expect_notification('finish')
  982. notify('finish')
  983. end,
  984. }
  985. end
  986. -- Tests will be indexed by test_name
  987. local test_name = arg[1]
  988. local timeout = arg[2]
  989. assert(type(test_name) == 'string', 'test_name must be specified as first arg.')
  990. local kill_timer = assert(vim.uv.new_timer())
  991. kill_timer:start(timeout or 1e3, 0, function()
  992. kill_timer:stop()
  993. kill_timer:close()
  994. log('ERROR', 'LSP', 'TIMEOUT')
  995. io.stderr:write('TIMEOUT')
  996. os.exit(100)
  997. end)
  998. local status, err = pcall(assert(tests[test_name], 'Test not found'))
  999. kill_timer:stop()
  1000. kill_timer:close()
  1001. if not status then
  1002. log('ERROR', 'LSP', tostring(err))
  1003. io.stderr:write(err)
  1004. vim.cmd [[101cquit]]
  1005. end