@@ -47,6 +47,12 @@ Set this if you have the binary installed or have it built yourself."
47
47
:group 'lsp-csharp
48
48
:type '(string :tag " Single string value or nil" ))
49
49
50
+ (defcustom lsp-csharp-test-run-buffer-name
51
+ " *lsp-csharp test run*"
52
+ " The name of buffer used for outputing lsp-csharp test run results."
53
+ :group 'lsp-csharp
54
+ :type 'string )
55
+
50
56
(defun lsp-csharp--version-list-latest (lst )
51
57
(->> lst
52
58
(-sort (lambda (a b ) (not (version<= (substring a 1 )
@@ -244,6 +250,184 @@ tarball or a zip file (based on a current platform) to TARGET-DIR."
244
250
((&omnisharp:MsBuildProject :path ) ms-build-project))
245
251
(find-file path)))
246
252
253
+ (defun lsp-csharp--get-buffer-code-elements ()
254
+ " Retrieve code structure by calling into the /v2/codestructure endpoint.
255
+ Returns :elements from omnisharp:CodeStructureResponse."
256
+ (-let* ((code-structure (lsp-request " o#/v2/codestructure"
257
+ (lsp-make-omnisharp-code-structure-request :file-name (buffer-file-name ))))
258
+ ((&omnisharp:CodeStructureResponse :elements ) code-structure))
259
+ elements))
260
+
261
+ (defun lsp-csharp--inspect-code-elements-recursively (fn elements )
262
+ " Invoke FN for every omnisharp:CodeElement found recursively in ELEMENTS."
263
+ (seq-each
264
+ (lambda (el )
265
+ (funcall fn el)
266
+ (-let (((&omnisharp:CodeElement :children ) el))
267
+ (lsp-csharp--inspect-code-elements-recursively fn children)))
268
+ elements))
269
+
270
+ (defun lsp-csharp--collect-code-elements-recursively (predicate elements )
271
+ " Flatten the omnisharp:CodeElement tree in ELEMENTS matching PREDICATE."
272
+ (let ((results nil ))
273
+ (lsp-csharp--inspect-code-elements-recursively (lambda (el )
274
+ (when (funcall predicate el)
275
+ (setq results (cons el results))))
276
+ elements)
277
+ results))
278
+
279
+ (lsp-defun lsp-csharp--l-c-within-range (l c (&omnisharp:Range :start :end ))
280
+ " Determine if L (line) and C (column) are within RANGE."
281
+ (-let* (((&omnisharp:Point :line start-l :column start-c) start)
282
+ ((&omnisharp:Point :line end-l :column end-c) end))
283
+ (or (and (= l start-l) (>= c start-c) (or (> end-l start-l) (<= c end-c)))
284
+ (and (> l start-l) (< l end-l))
285
+ (and (= l end-l) (<= c end-c)))))
286
+
287
+ (defun lsp-csharp--code-element-stack-on-l-c (l c elements )
288
+ " Return omnisharp:CodeElement stack at L (line) and C (column) in ELEMENTS tree."
289
+ (when-let ((matching-element (seq-find (lambda (el )
290
+ (-when-let* (((&omnisharp:CodeElement :ranges ) el)
291
+ ((&omnisharp:RangeList :full? ) ranges))
292
+ (lsp-csharp--l-c-within-range l c full?) ))
293
+ elements)))
294
+ (-let (((&omnisharp:CodeElement :children ) matching-element))
295
+ (cons matching-element (lsp-csharp--code-element-stack-on-l-c l c children)))))
296
+
297
+ (defun lsp-csharp--code-element-stack-at-point ()
298
+ " Return omnisharp:CodeElement stack at point as a list."
299
+ (let ((pos-line (plist-get (lsp--cur-position) :line ))
300
+ (pos-col (plist-get (lsp--cur-position) :character )))
301
+ (lsp-csharp--code-element-stack-on-l-c pos-line
302
+ pos-col
303
+ (lsp-csharp--get-buffer-code-elements))))
304
+
305
+ (lsp-defun lsp-csharp--code-element-test-method-p (element)
306
+ " Return test method name and test framework for a given ELEMENT."
307
+ (when element
308
+ (-when-let* (((&omnisharp:CodeElement :properties ) element)
309
+ ((&omnisharp:CodeElementProperties :test-method-name? :test-framework? ) properties))
310
+ (list test-method-name? test-framework?) )))
311
+
312
+ (defun lsp-csharp--reset-test-buffer (present-buffer )
313
+ " Create new or reuse an existing test result output buffer.
314
+ PRESENT-BUFFER will make the buffer be presented to the user."
315
+ (with-current-buffer (get-buffer-create lsp-csharp-test-run-buffer-name)
316
+ (compilation-mode )
317
+ (read-only-mode )
318
+ (let ((inhibit-read-only t ))
319
+ (erase-buffer )))
320
+
321
+ (when present-buffer
322
+ (display-buffer lsp-csharp-test-run-buffer-name)))
323
+
324
+ (defun lsp-csharp--start-tests (test-method-framework test-method-names )
325
+ " Run test(s) identified by TEST-METHOD-NAMES using TEST-METHOD-FRAMEWORK."
326
+ (if (and test-method-framework test-method-names)
327
+ (let ((request-message (lsp-make-omnisharp-run-tests-in-class-request
328
+ :file-name (buffer-file-name )
329
+ :test-frameworkname test-method-framework
330
+ :method-names (vconcat test-method-names))))
331
+ (lsp-csharp--reset-test-buffer t )
332
+ (lsp-session-set-metadata " last-test-method-framework" test-method-framework)
333
+ (lsp-session-set-metadata " last-test-method-names" test-method-names)
334
+ (lsp-request-async " o#/v2/runtestsinclass"
335
+ request-message
336
+ (-lambda ((&omnisharp:RunTestResponse))
337
+ (message " lsp-csharp: Test run has started " ))))
338
+ (message " lsp-csharp: No test methods to run " )))
339
+
340
+ (defun lsp-csharp--test-message (message )
341
+ " Emit a MESSAGE to lsp-csharp test run buffer."
342
+ (when-let ((existing-buffer (get-buffer lsp-csharp-test-run-buffer-name))
343
+ (inhibit-read-only t ))
344
+ (with-current-buffer existing-buffer
345
+ (save-excursion
346
+ (goto-char (point-max ))
347
+ (insert message " \n " )))))
348
+
349
+ (defun lsp-csharp-run-test-at-point ()
350
+ " Start test run at current point (if any)."
351
+ (interactive )
352
+ (let* ((stack (lsp-csharp--code-element-stack-at-point))
353
+ (element-on-point (car (last stack)))
354
+ (test-method (lsp-csharp--code-element-test-method-p element-on-point))
355
+ (test-method-name (car test-method))
356
+ (test-method-framework (car (cdr test-method))))
357
+ (lsp-csharp--start-tests test-method-framework (list test-method-name))))
358
+
359
+ (defun lsp-csharp-run-all-tests-in-buffer ()
360
+ " Run all test methods in the current buffer."
361
+ (interactive )
362
+ (let* ((elements (lsp-csharp--get-buffer-code-elements))
363
+ (test-methods (lsp-csharp--collect-code-elements-recursively 'lsp-csharp--code-element-test-method-p elements))
364
+ (test-method-framework (car (cdr (lsp-csharp--code-element-test-method-p (car test-methods)))))
365
+ (test-method-names (mapcar (lambda (method )
366
+ (car (lsp-csharp--code-element-test-method-p method)))
367
+ test-methods)))
368
+ (lsp-csharp--start-tests test-method-framework test-method-names)))
369
+
370
+ (defun lsp-csharp-run-test-in-buffer ()
371
+ " Run selected test in current buffer."
372
+ (interactive )
373
+ (when-let* ((elements (lsp-csharp--get-buffer-code-elements))
374
+ (test-methods (lsp-csharp--collect-code-elements-recursively 'lsp-csharp--code-element-test-method-p elements))
375
+ (test-method-framework (car (cdr (lsp-csharp--code-element-test-method-p (car test-methods)))))
376
+ (test-method-names (mapcar (lambda (method )
377
+ (car (lsp-csharp--code-element-test-method-p method)))
378
+ test-methods))
379
+ (selected-test-method-name (lsp--completing-read " Select test:" test-method-names 'identity )))
380
+ (lsp-csharp--start-tests test-method-framework (list selected-test-method-name))))
381
+
382
+ (defun lsp-csharp-run-last-tests ()
383
+ " Re-run test(s) that were run last time."
384
+ (interactive )
385
+ (if-let ((last-test-method-framework (lsp-session-get-metadata " last-test-method-framework" ))
386
+ (last-test-method-names (lsp-session-get-metadata " last-test-method-names" )))
387
+ (lsp-csharp--start-tests last-test-method-framework last-test-method-names)
388
+ (message " lsp-csharp: No test method(s) found to be ran previously on this workspace " )))
389
+
390
+ (lsp-defun lsp-csharp--handle-os-error (_workspace (&omnisharp:ErrorMessage :file-name :text ))
391
+ " Handle the 'o#/error' (interop) notification by displaying a message with lsp-warn."
392
+ (lsp-warn " %s: %s" file-name text))
393
+
394
+ (lsp-defun lsp-csharp--handle-os-testmessage (_workspace (&omnisharp:TestMessageEvent :message ))
395
+ " Handle the 'o#/testmessage and display test message on lsp-csharp
396
+ test output buffer."
397
+ (lsp-csharp--test-message message))
398
+
399
+ (lsp-defun lsp-csharp--handle-os-testcompleted (_workspace (&omnisharp:DotNetTestResult
400
+ :method-name
401
+ :outcome
402
+ :error-message
403
+ :error-stack-trace
404
+ :standard-output
405
+ :standard-error ))
406
+ " Handle the 'o#/testcompleted' message from the server.
407
+
408
+ Will display the results of the test on the lsp-csharp test output buffer."
409
+ (let ((passed (string-equal " passed" outcome)))
410
+ (lsp-csharp--test-message
411
+ (format " [%s ] %s "
412
+ (propertize (upcase outcome) 'font-lock-face (if passed 'success 'error ))
413
+ method-name))
414
+
415
+ (unless passed
416
+ (lsp-csharp--test-message error-message)
417
+
418
+ (when error-stack-trace
419
+ (lsp-csharp--test-message error-stack-trace))
420
+
421
+ (unless (seq-empty-p standard-output)
422
+ (lsp-csharp--test-message " STANDARD OUTPUT:" )
423
+ (seq-doseq (stdout-line standard-output)
424
+ (lsp-csharp--test-message stdout-line)))
425
+
426
+ (unless (seq-empty-p standard-error)
427
+ (lsp-csharp--test-message " STANDARD ERROR:" )
428
+ (seq-doseq (stderr-line standard-error)
429
+ (lsp-csharp--test-message stderr-line))))))
430
+
247
431
(lsp-defun lsp-csharp--action-client-find-references ((&Command :arguments? ))
248
432
" Read first argument from ACTION as Location and display xrefs for that location
249
433
using the `textDocument/references' request."
@@ -255,10 +439,6 @@ using the `textDocument/references' request."
255
439
(lsp-show-xrefs (lsp--locations-to-xref-items locations-found) nil t )
256
440
(message " No references found " )))
257
441
258
- (lsp-defun lsp-csharp--handle-os-error (_workspace (&omnisharp:ErrorMessage :file-name :text ))
259
- " Handle the 'o#/error' (interop) notification by displaying a message with lsp-warn."
260
- (lsp-warn " %s: %s" file-name text))
261
-
262
442
(lsp-register-client
263
443
(make-lsp-client :new-connection (lsp-stdio-connection
264
444
#'lsp-csharp--language-server-command
@@ -277,6 +457,8 @@ using the `textDocument/references' request."
277
457
(" o#/packagerestorefinished" 'ignore )
278
458
(" o#/unresolveddependencies" 'ignore )
279
459
(" o#/error" 'lsp-csharp--handle-os-error )
460
+ (" o#/testmessage" 'lsp-csharp--handle-os-testmessage )
461
+ (" o#/testcompleted" 'lsp-csharp--handle-os-testcompleted )
280
462
(" o#/projectconfiguration" 'ignore )
281
463
(" o#/projectdiagnosticstatus" 'ignore ))
282
464
:download-server-fn
0 commit comments