暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

golang源码分析 :gopls(5)

        介绍完rpc方式实现后我们看看stdio方式的实现,首先是初始化Stream
    func NewStreamServer(cache *cache.Cache, daemon bool, optionsFunc func(*settings.Options)) jsonrpc2.StreamServer {
        return &streamServer{cache: cache, daemon: daemon, optionsOverrides: optionsFunc}
    }
      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方法定义如下
        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()
        }
          func ClientDispatcher(conn jsonrpc2.ConnClientCloser {
              return &clientDispatcher{sender: clientConn{conn}}
          }
            type clientDispatcher struct {
                sender connSender
            }
                    首先通过os.Executable()获取可以执行的命令行工具,然后通过
            svr=server.New(session, client, options)获取server,最后调用conn.Go进行转发
                  conn.Go(ctx,
                      protocol.Handlers(
                          handshaker(session, executable, s.daemon,
                              protocol.ServerHandler(svr,
                                  jsonrpc2.MethodNotFound))))
              其中middleware handshaker 完成了握手和会话的功能
                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)
                    }
                }
                对应的请求路径如下
                  const (
                      handshakeMethod = "gopls/handshake"
                      sessionsMethod  = "gopls/sessions"
                  )
                    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 的接口具体实现。

                    文章转载自golang算法架构leetcode技术php,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                    评论