LSP-server-example
152 строки · 3.3 Кб
1package server
2
3import (
4"errors"
5"io"
6"log/slog"
7"net"
8"os"
9"os/signal"
10"strings"
11"sync"
12"syscall"
13
14"lsp-server/internal/protocol"
15"lsp-server/internal/server/handlers"
16)
17
18type Server struct {
19listener net.Listener
20quit chan interface{}
21wg sync.WaitGroup
22mux *Mux
23fileURIToContents *map[string][]string
24}
25
26func NewServer() *Server {
27s := &Server{
28quit: make(chan interface{}),
29mux: NewMux(),
30fileURIToContents: &map[string][]string{},
31}
32return s
33}
34
35const docChangesBuffSize = 10
36
37func (s *Server) serve() {
38defer s.wg.Done()
39
40documentUpdates := make(chan protocol.TextDocumentItem, docChangesBuffSize)
41s.registerHandlers(documentUpdates)
42go s.runDiagnostic(documentUpdates)
43
44for {
45conn, err := s.listener.Accept()
46if err != nil {
47select {
48case <-s.quit:
49return
50default:
51slog.Error("accept error", err)
52}
53} else {
54s.updateConn(&conn)
55s.wg.Add(1)
56go func() {
57s.handleConnection(conn)
58s.wg.Done()
59}()
60}
61}
62}
63
64func (s *Server) updateConn(conn *net.Conn) {
65s.mux.NewConn(conn)
66}
67
68func (s *Server) Stop() {
69close(s.quit)
70s.listener.Close()
71s.wg.Wait()
72}
73
74func (s *Server) handleConnection(conn net.Conn) {
75defer conn.Close()
76
77slog.Info("received from %v", conn.RemoteAddr())
78
79err := s.mux.Process()
80if err != nil && errors.Is(err, io.EOF) {
81slog.Error("read error, err: %v", err)
82return
83}
84}
85
86func (s *Server) Run(port string) {
87l, err := net.Listen("tcp", ":"+port)
88if err != nil {
89slog.Error(err.Error())
90return
91}
92s.listener = l
93s.wg.Add(1)
94go s.serve()
95
96quit := make(chan os.Signal, 1)
97signal.Notify(quit, os.Interrupt, syscall.SIGTERM, syscall.SIGTSTP)
98
99<-quit
100
101s.Stop()
102}
103
104func (s *Server) registerHandlers(documentUpdates chan protocol.TextDocumentItem) {
105s.mux.HandleRequest(handlers.InitializeMethod, handlers.Initialize{})
106s.mux.HandleNotification(handlers.InitializedNotification, handlers.Initialized{})
107
108s.mux.HandleRequest(handlers.HoverMethod, handlers.NewHover(s.fileURIToContents))
109s.mux.HandleNotification(handlers.DidOpenTextDocumentNotification, handlers.NewDidOpen(documentUpdates))
110s.mux.HandleNotification(handlers.DidChangeTextDocumentNotification, handlers.NewDidChange(documentUpdates))
111s.mux.HandleRequest(handlers.CompletionRequestMethod, handlers.NewCompletion(s.fileURIToContents))
112}
113
114func (s *Server) runDiagnostic(documentUpdates chan protocol.TextDocumentItem) {
115for doc := range documentUpdates {
116diagnostics := s.createDiagnostics(doc)
117
118err := s.mux.Notify(
119handlers.PublishDiagnosticsMethod,
120protocol.PublishDiagnosticsParams{
121URI: doc.URI,
122Version: doc.Version,
123Diagnostics: diagnostics,
124})
125if err != nil {
126slog.Error("error to send diagnostic notify", slog.Any("err", err))
127return
128}
129}
130}
131
132func (s *Server) createDiagnostics(doc protocol.TextDocumentItem) []protocol.Diagnostic {
133docSplit := strings.Split(doc.Text, "\n")
134(*s.fileURIToContents)[doc.URI.Path()] = docSplit
135diagnostics := []protocol.Diagnostic{}
136diagnostics = append(diagnostics, protocol.Diagnostic{
137Severity: protocol.SeverityError,
138Message: "some diagnostic",
139Source: "lsp-server",
140Range: protocol.Range{
141Start: protocol.Position{
142Line: 0,
143Character: 0,
144},
145End: protocol.Position{
146Line: 0,
147Character: 0,
148},
149},
150})
151return diagnostics
152}
153