首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >golang源码分析 :gopls(5)

golang源码分析 :gopls(5)

作者头像
golangLeetcode
发布2026-03-18 18:26:55
发布2026-03-18 18:26:55
870
举报

介绍完rpc方式实现后我们看看stdio方式的实现,首先是初始化Stream

代码语言:javascript
复制
func NewStreamServer(cache *cache.Cache, daemon bool, optionsFunc func(*settings.Options)) jsonrpc2.StreamServer {
    return &streamServer{cache: cache, daemon: daemon, optionsOverrides: optionsFunc}
}
代码语言:javascript
复制
type streamServer struct {
    cache *cache.Cache
    // daemon controls whether or not to log new connections.
    daemon bool
    // optionsOverrides is passed to newly created sessions.
    optionsOverrides func(*settings.Options)
    // serverForTest may be set to a test fake for testing.
    serverForTest protocol.Server
}

它的ServeStream方法定义如下

代码语言:javascript
复制
func (s *streamServer) ServeStream(ctx context.Context, conn jsonrpc2.Conn) error {
    client := protocol.ClientDispatcher(conn)
    session := cache.NewSession(ctx, s.cache)
    svr := s.serverForTest
    if svr == nil {
        options := settings.DefaultOptions(s.optionsOverrides)
        svr = server.New(session, client, options)
        if instance := debug.GetInstance(ctx); instance != nil {
            instance.AddService(svr, session)
        }
    }
    // Clients may or may not send a shutdown message. Make sure the server is
    // shut down.
    // TODO(rFindley): this shutdown should perhaps be on a disconnected context.
    defer func() {
        if err := svr.Shutdown(ctx); err != nil {
            event.Error(ctx, "error shutting down", err)
        }
    }()
    executable, err := os.Executable()
    if err != nil {
        log.Printf("error getting gopls path: %v", err)
        executable = ""
    }
    ctx = protocol.WithClient(ctx, client)
    conn.Go(ctx,
        protocol.Handlers(
            handshaker(session, executable, s.daemon,
                protocol.ServerHandler(svr,
                    jsonrpc2.MethodNotFound))))
    if s.daemon {
        log.Printf("Session %s: connected", session.ID())
        defer log.Printf("Session %s: exited", session.ID())
    }
    <-conn.Done()
    return conn.Err()
}
代码语言:javascript
复制
func ClientDispatcher(conn jsonrpc2.Conn) ClientCloser {
    return &clientDispatcher{sender: clientConn{conn}}
}
代码语言:javascript
复制
type clientDispatcher struct {
    sender connSender
}

首先通过os.Executable()获取可以执行的命令行工具,然后通过

svr=server.New(session, client, options)获取server,最后调用conn.Go进行转发

代码语言:javascript
复制
    conn.Go(ctx,
        protocol.Handlers(
            handshaker(session, executable, s.daemon,
                protocol.ServerHandler(svr,
                    jsonrpc2.MethodNotFound))))

其中middleware handshaker 完成了握手和会话的功能

代码语言:javascript
复制
func handshaker(session *cache.Session, goplsPath string, logHandshakes bool, handler jsonrpc2.Handler) jsonrpc2.Handler {
    return func(ctx context.Context, reply jsonrpc2.Replier, r jsonrpc2.Request) error {
        switch r.Method() {
        case handshakeMethod:
            // We log.Printf in this handler, rather than event.Log when we want logs
            // to go to the daemon log rather than being reflected back to the
            // client.
            var req handshakeRequest
            if err := json.Unmarshal(r.Params(), &req); err != nil {
                if logHandshakes {
                    log.Printf("Error processing handshake for session %s: %v", session.ID(), err)
                }
                sendError(ctx, reply, err)
                return nil
            }
            if logHandshakes {
                log.Printf("Session %s: got handshake. Logfile: %q, Debug addr: %q", session.ID(), req.Logfile, req.DebugAddr)
            }
            event.Log(ctx, "Handshake session update",
                cache.KeyUpdateSession.Of(session),
                label.DebugAddress.Of(req.DebugAddr),
                label.Logfile.Of(req.Logfile),
                label.ServerID.Of(req.ServerID),
                label.GoplsPath.Of(req.GoplsPath),
            )
            resp := handshakeResponse{
                SessionID: session.ID(),
                GoplsPath: goplsPath,
            }
            if di := debug.GetInstance(ctx); di != nil {
                resp.Logfile = di.Logfile
                resp.DebugAddr = di.ListenedDebugAddress()
            }
            return reply(ctx, resp, nil)
        case sessionsMethod:
            resp := serverState{
                GoplsPath:       goplsPath,
                CurrentClientID: session.ID(),
            }
            if di := debug.GetInstance(ctx); di != nil {
                resp.Logfile = di.Logfile
                resp.DebugAddr = di.ListenedDebugAddress()
                for _, c := range di.State.Clients() {
                    resp.Clients = append(resp.Clients, clientSession{
                        SessionID: c.Session.ID(),
                        Logfile:   c.Logfile,
                        DebugAddr: c.DebugAddress,
                    })
                }
            }
            return reply(ctx, resp, nil)
        }
        return handler(ctx, reply, r)
    }
}

对应的请求路径如下

代码语言:javascript
复制
const (
    handshakeMethod = "gopls/handshake"
    sessionsMethod  = "gopls/sessions"
)
代码语言:javascript
复制
func New(session *cache.Session, client protocol.ClientCloser, options *settings.Options) protocol.Server {
    const concurrentAnalyses = 1
    // If this assignment fails to compile after a protocol
    // upgrade, it means that one or more new methods need new
    // stub declarations in unimplemented.go.
    return &server{
        diagnostics:         make(map[protocol.DocumentURI]*fileDiagnostics),
        watchedGlobPatterns: nil, // empty
        changedFiles:        make(map[protocol.DocumentURI]unit),
        session:             session,
        client:              client,
        diagnosticsSema:     make(chan unit, concurrentAnalyses),
        progress:            progress.NewTracker(client),
        options:             options,
        viewsToDiagnose:     make(map[*cache.View]uint64),
    }
}

对应的server就返回了pls 的接口具体实现。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-07-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 golang算法架构leetcode技术php 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档