@@ -14,6 +14,7 @@ import path = require('path');
14
14
import semver = require( 'semver' ) ;
15
15
import util = require( 'util' ) ;
16
16
import vscode = require( 'vscode' ) ;
17
+ import { InitializeParams , LSPAny , LSPObject } from 'vscode-languageserver-protocol' ;
17
18
import {
18
19
CancellationToken ,
19
20
CloseAction ,
@@ -59,7 +60,8 @@ import { maybePromptForDeveloperSurvey } from '../goDeveloperSurvey';
59
60
import { CommandFactory } from '../commands' ;
60
61
import { updateLanguageServerIconGoStatusBar } from '../goStatus' ;
61
62
import { URI } from 'vscode-uri' ;
62
- import { IVulncheckTerminal , VulncheckReport , VulncheckTerminal , writeVulns } from '../goVulncheck' ;
63
+ import { VulncheckReport , writeVulns } from '../goVulncheck' ;
64
+ import { ActiveProgressTerminals , IProgressTerminal , ProgressTerminal } from '../progressTerminal' ;
63
65
import { createHash } from 'crypto' ;
64
66
import { GoExtensionContext } from '../context' ;
65
67
import { GoDocumentSelector } from '../goMode' ;
@@ -379,9 +381,23 @@ export class GoLanguageClient extends LanguageClient implements vscode.Disposabl
379
381
this . onDidChangeVulncheckResultEmitter . dispose ( ) ;
380
382
return super . dispose ( timeout ) ;
381
383
}
384
+
382
385
public get onDidChangeVulncheckResult ( ) : vscode . Event < VulncheckEvent > {
383
386
return this . onDidChangeVulncheckResultEmitter . event ;
384
387
}
388
+
389
+ protected fillInitializeParams ( params : InitializeParams ) : void {
390
+ super . fillInitializeParams ( params ) ;
391
+
392
+ // VSCode-Go honors most client capabilities from the vscode-languageserver-node
393
+ // library. Experimental capabilities not used by vscode-languageserver-node
394
+ // can be used for custom communication between vscode-go and gopls.
395
+ // See https://github.com/microsoft/vscode-languageserver-node/issues/1607
396
+ const experimental : LSPObject = {
397
+ progressMessageStyles : [ 'log' ]
398
+ } ;
399
+ params . capabilities . experimental = experimental ;
400
+ }
385
401
}
386
402
387
403
type VulncheckEvent = {
@@ -402,10 +418,12 @@ export async function buildLanguageClient(
402
418
// we want to handle the connection close error case specially. Capture the error
403
419
// in initializationFailedHandler and handle it in the connectionCloseHandler.
404
420
let initializationError : ResponseError < InitializeError > | undefined = undefined ;
405
- let govulncheckTerminal : IVulncheckTerminal | undefined ;
406
421
422
+ // TODO(hxjiang): deprecate special handling for async call gopls.run_govulncheck.
423
+ let govulncheckTerminal : IProgressTerminal | undefined ;
407
424
const pendingVulncheckProgressToken = new Map < ProgressToken , any > ( ) ;
408
425
const onDidChangeVulncheckResultEmitter = new vscode . EventEmitter < VulncheckEvent > ( ) ;
426
+
409
427
// cfg is captured by closures for later use during error report.
410
428
const c = new GoLanguageClient (
411
429
'go' , // id
@@ -489,13 +507,34 @@ export async function buildLanguageClient(
489
507
handleWorkDoneProgress : async ( token , params , next ) => {
490
508
switch ( params . kind ) {
491
509
case 'begin' :
510
+ if ( typeof params . message === 'string' ) {
511
+ const paragraphs = params . message . split ( '\n\n' , 2 ) ;
512
+ const metadata = paragraphs [ 0 ] . trim ( ) ;
513
+ if ( ! metadata . startsWith ( 'style: ' ) ) {
514
+ break ;
515
+ }
516
+ const style = metadata . substring ( 'style: ' . length ) ;
517
+ if ( style === 'log' ) {
518
+ const term = ProgressTerminal . Open ( params . title , token ) ;
519
+ if ( paragraphs . length > 1 ) {
520
+ term . appendLine ( paragraphs [ 1 ] ) ;
521
+ }
522
+ term . show ( ) ;
523
+ }
524
+ }
492
525
break ;
493
526
case 'report' :
527
+ if ( params . message ) {
528
+ ActiveProgressTerminals . get ( token ) ?. appendLine ( params . message ) ;
529
+ }
494
530
if ( pendingVulncheckProgressToken . has ( token ) && params . message ) {
495
531
govulncheckTerminal ?. appendLine ( params . message ) ;
496
532
}
497
533
break ;
498
534
case 'end' :
535
+ if ( params . message ) {
536
+ ActiveProgressTerminals . get ( token ) ?. appendLine ( params . message ) ;
537
+ }
499
538
if ( pendingVulncheckProgressToken . has ( token ) ) {
500
539
const out = pendingVulncheckProgressToken . get ( token ) ;
501
540
pendingVulncheckProgressToken . delete ( token ) ;
@@ -507,7 +546,7 @@ export async function buildLanguageClient(
507
546
} ,
508
547
executeCommand : async ( command : string , args : any [ ] , next : ExecuteCommandSignature ) => {
509
548
try {
510
- if ( command === 'gopls.tidy' ) {
549
+ if ( command === 'gopls.tidy' || command === 'gopls.vulncheck' ) {
511
550
await vscode . workspace . saveAll ( false ) ;
512
551
}
513
552
if ( command === 'gopls.run_govulncheck' && args . length && args [ 0 ] . URI ) {
@@ -520,17 +559,35 @@ export async function buildLanguageClient(
520
559
await vscode . workspace . saveAll ( false ) ;
521
560
const uri = args [ 0 ] . URI ? URI . parse ( args [ 0 ] . URI ) : undefined ;
522
561
const dir = uri ?. fsPath ?. endsWith ( '.mod' ) ? path . dirname ( uri . fsPath ) : uri ?. fsPath ;
523
- govulncheckTerminal = VulncheckTerminal . Open ( ) ;
562
+ govulncheckTerminal = ProgressTerminal . Open ( 'govulncheck' ) ;
524
563
govulncheckTerminal . appendLine ( `⚡ govulncheck -C ${ dir } ./...\n\n` ) ;
525
564
govulncheckTerminal . show ( ) ;
526
565
}
527
566
const res = await next ( command , args ) ;
528
- if ( command === 'gopls.run_govulncheck' ) {
529
- const progressToken = res . Token ;
530
- if ( progressToken ) {
531
- pendingVulncheckProgressToken . set ( progressToken , args [ 0 ] ) ;
567
+
568
+ const progressToken = < ProgressToken > res . Token ;
569
+ // The progressToken from executeCommand indicates that
570
+ // gopls may trigger a related workDoneProgress
571
+ // notification, either before or after the command
572
+ // completes.
573
+ // https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#serverInitiatedProgress
574
+ if ( progressToken !== undefined ) {
575
+ switch ( command ) {
576
+ case 'gopls.run_govulncheck' :
577
+ pendingVulncheckProgressToken . set ( progressToken , args [ 0 ] ) ;
578
+ break ;
579
+ case 'gopls.vulncheck' :
580
+ // Write the vulncheck report to the terminal.
581
+ if ( ActiveProgressTerminals . has ( progressToken ) ) {
582
+ writeVulns ( res . Result , ActiveProgressTerminals . get ( progressToken ) , cfg . path ) ;
583
+ }
584
+ break ;
585
+ default :
586
+ // By default, dump the result to the terminal.
587
+ ActiveProgressTerminals . get ( progressToken ) ?. appendLine ( res . Result ) ;
532
588
}
533
589
}
590
+
534
591
return res ;
535
592
} catch ( e ) {
536
593
// TODO: how to print ${e} reliably???
0 commit comments