LSP-server-example

Форк
0
152 строки · 3.3 Кб
1
package server
2

3
import (
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

18
type Server struct {
19
	listener          net.Listener
20
	quit              chan interface{}
21
	wg                sync.WaitGroup
22
	mux               *Mux
23
	fileURIToContents *map[string][]string
24
}
25

26
func NewServer() *Server {
27
	s := &Server{
28
		quit:              make(chan interface{}),
29
		mux:               NewMux(),
30
		fileURIToContents: &map[string][]string{},
31
	}
32
	return s
33
}
34

35
const docChangesBuffSize = 10
36

37
func (s *Server) serve() {
38
	defer s.wg.Done()
39

40
	documentUpdates := make(chan protocol.TextDocumentItem, docChangesBuffSize)
41
	s.registerHandlers(documentUpdates)
42
	go s.runDiagnostic(documentUpdates)
43

44
	for {
45
		conn, err := s.listener.Accept()
46
		if err != nil {
47
			select {
48
			case <-s.quit:
49
				return
50
			default:
51
				slog.Error("accept error", err)
52
			}
53
		} else {
54
			s.updateConn(&conn)
55
			s.wg.Add(1)
56
			go func() {
57
				s.handleConnection(conn)
58
				s.wg.Done()
59
			}()
60
		}
61
	}
62
}
63

64
func (s *Server) updateConn(conn *net.Conn) {
65
	s.mux.NewConn(conn)
66
}
67

68
func (s *Server) Stop() {
69
	close(s.quit)
70
	s.listener.Close()
71
	s.wg.Wait()
72
}
73

74
func (s *Server) handleConnection(conn net.Conn) {
75
	defer conn.Close()
76

77
	slog.Info("received from %v", conn.RemoteAddr())
78

79
	err := s.mux.Process()
80
	if err != nil && errors.Is(err, io.EOF) {
81
		slog.Error("read error, err: %v", err)
82
		return
83
	}
84
}
85

86
func (s *Server) Run(port string) {
87
	l, err := net.Listen("tcp", ":"+port)
88
	if err != nil {
89
		slog.Error(err.Error())
90
		return
91
	}
92
	s.listener = l
93
	s.wg.Add(1)
94
	go s.serve()
95

96
	quit := make(chan os.Signal, 1)
97
	signal.Notify(quit, os.Interrupt, syscall.SIGTERM, syscall.SIGTSTP)
98

99
	<-quit
100

101
	s.Stop()
102
}
103

104
func (s *Server) registerHandlers(documentUpdates chan protocol.TextDocumentItem) {
105
	s.mux.HandleRequest(handlers.InitializeMethod, handlers.Initialize{})
106
	s.mux.HandleNotification(handlers.InitializedNotification, handlers.Initialized{})
107

108
	s.mux.HandleRequest(handlers.HoverMethod, handlers.NewHover(s.fileURIToContents))
109
	s.mux.HandleNotification(handlers.DidOpenTextDocumentNotification, handlers.NewDidOpen(documentUpdates))
110
	s.mux.HandleNotification(handlers.DidChangeTextDocumentNotification, handlers.NewDidChange(documentUpdates))
111
	s.mux.HandleRequest(handlers.CompletionRequestMethod, handlers.NewCompletion(s.fileURIToContents))
112
}
113

114
func (s *Server) runDiagnostic(documentUpdates chan protocol.TextDocumentItem) {
115
	for doc := range documentUpdates {
116
		diagnostics := s.createDiagnostics(doc)
117

118
		err := s.mux.Notify(
119
			handlers.PublishDiagnosticsMethod,
120
			protocol.PublishDiagnosticsParams{
121
				URI:         doc.URI,
122
				Version:     doc.Version,
123
				Diagnostics: diagnostics,
124
			})
125
		if err != nil {
126
			slog.Error("error to send diagnostic notify", slog.Any("err", err))
127
			return
128
		}
129
	}
130
}
131

132
func (s *Server) createDiagnostics(doc protocol.TextDocumentItem) []protocol.Diagnostic {
133
	docSplit := strings.Split(doc.Text, "\n")
134
	(*s.fileURIToContents)[doc.URI.Path()] = docSplit
135
	diagnostics := []protocol.Diagnostic{}
136
	diagnostics = append(diagnostics, protocol.Diagnostic{
137
		Severity: protocol.SeverityError,
138
		Message:  "some diagnostic",
139
		Source:   "lsp-server",
140
		Range: protocol.Range{
141
			Start: protocol.Position{
142
				Line:      0,
143
				Character: 0,
144
			},
145
			End: protocol.Position{
146
				Line:      0,
147
				Character: 0,
148
			},
149
		},
150
	})
151
	return diagnostics
152
}
153

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.