@@ -14,6 +14,7 @@ import path = require('path');
1414import semver = require( 'semver' ) ;
1515import util = require( 'util' ) ;
1616import vscode = require( 'vscode' ) ;
17+ import { InitializeParams , LSPAny , LSPObject } from 'vscode-languageserver-protocol' ;
1718import {
1819 CancellationToken ,
1920 CloseAction ,
@@ -59,7 +60,8 @@ import { maybePromptForDeveloperSurvey } from '../goDeveloperSurvey';
5960import { CommandFactory } from '../commands' ;
6061import { updateLanguageServerIconGoStatusBar } from '../goStatus' ;
6162import { 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' ;
6365import { createHash } from 'crypto' ;
6466import { GoExtensionContext } from '../context' ;
6567import { GoDocumentSelector } from '../goMode' ;
@@ -379,9 +381,23 @@ export class GoLanguageClient extends LanguageClient implements vscode.Disposabl
379381 this . onDidChangeVulncheckResultEmitter . dispose ( ) ;
380382 return super . dispose ( timeout ) ;
381383 }
384+
382385 public get onDidChangeVulncheckResult ( ) : vscode . Event < VulncheckEvent > {
383386 return this . onDidChangeVulncheckResultEmitter . event ;
384387 }
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+ }
385401}
386402
387403type VulncheckEvent = {
@@ -402,10 +418,12 @@ export async function buildLanguageClient(
402418 // we want to handle the connection close error case specially. Capture the error
403419 // in initializationFailedHandler and handle it in the connectionCloseHandler.
404420 let initializationError : ResponseError < InitializeError > | undefined = undefined ;
405- let govulncheckTerminal : IVulncheckTerminal | undefined ;
406421
422+ // TODO(hxjiang): deprecate special handling for async call gopls.run_govulncheck.
423+ let govulncheckTerminal : IProgressTerminal | undefined ;
407424 const pendingVulncheckProgressToken = new Map < ProgressToken , any > ( ) ;
408425 const onDidChangeVulncheckResultEmitter = new vscode . EventEmitter < VulncheckEvent > ( ) ;
426+
409427 // cfg is captured by closures for later use during error report.
410428 const c = new GoLanguageClient (
411429 'go' , // id
@@ -489,13 +507,34 @@ export async function buildLanguageClient(
489507 handleWorkDoneProgress : async ( token , params , next ) => {
490508 switch ( params . kind ) {
491509 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+ }
492525 break ;
493526 case 'report' :
527+ if ( params . message ) {
528+ ActiveProgressTerminals . get ( token ) ?. appendLine ( params . message ) ;
529+ }
494530 if ( pendingVulncheckProgressToken . has ( token ) && params . message ) {
495531 govulncheckTerminal ?. appendLine ( params . message ) ;
496532 }
497533 break ;
498534 case 'end' :
535+ if ( params . message ) {
536+ ActiveProgressTerminals . get ( token ) ?. appendLine ( params . message ) ;
537+ }
499538 if ( pendingVulncheckProgressToken . has ( token ) ) {
500539 const out = pendingVulncheckProgressToken . get ( token ) ;
501540 pendingVulncheckProgressToken . delete ( token ) ;
@@ -507,7 +546,7 @@ export async function buildLanguageClient(
507546 } ,
508547 executeCommand : async ( command : string , args : any [ ] , next : ExecuteCommandSignature ) => {
509548 try {
510- if ( command === 'gopls.tidy' ) {
549+ if ( command === 'gopls.tidy' || command === 'gopls.vulncheck' ) {
511550 await vscode . workspace . saveAll ( false ) ;
512551 }
513552 if ( command === 'gopls.run_govulncheck' && args . length && args [ 0 ] . URI ) {
@@ -520,17 +559,35 @@ export async function buildLanguageClient(
520559 await vscode . workspace . saveAll ( false ) ;
521560 const uri = args [ 0 ] . URI ? URI . parse ( args [ 0 ] . URI ) : undefined ;
522561 const dir = uri ?. fsPath ?. endsWith ( '.mod' ) ? path . dirname ( uri . fsPath ) : uri ?. fsPath ;
523- govulncheckTerminal = VulncheckTerminal . Open ( ) ;
562+ govulncheckTerminal = ProgressTerminal . Open ( 'govulncheck' ) ;
524563 govulncheckTerminal . appendLine ( `⚡ govulncheck -C ${ dir } ./...\n\n` ) ;
525564 govulncheckTerminal . show ( ) ;
526565 }
527566 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 ) ;
532588 }
533589 }
590+
534591 return res ;
535592 } catch ( e ) {
536593 // TODO: how to print ${e} reliably???
0 commit comments