Skip to content

Commit 408c6ce

Browse files
vigneshd332shubham-1806Ashwin125
authored
feat: jsonrpc integration & cpp autocomplete (#5)
* feat: add player code submodule * feat: Symlinks and refactoring * feat[WIP]: Integrate JSONRPC * Feat : Integrate ccls language server * fix: pre commit * Refactor : Improve readability * Fix : send both file and folder path * refactor: readablity * update: submodule * fix: removed unnecessary variables/checks --------- Co-authored-by: shubham-1806 <[email protected]> Co-authored-by: Ashwani <[email protected]>
1 parent 0e49fc1 commit 408c6ce

20 files changed

+280
-117
lines changed

.env.example

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
TARGET=dev
22
SERVER_PORT=8000
3+
FRONTEND_URL=http://localhost:3000

.githooks/pre-commit

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@ echo "\033[36m
55
Running golangci-lint
66
======================================================================"
77

8-
golangci-lint run . --config .golangci.yaml
8+
golangci-lint run --config .golangci.yaml
99

1010
if [ $? != 0 ]; then
1111
echo "\033[31m
1212
======================================================================
1313
golangci-lint check failed
1414
======================================================================"
15+
exit 1
1516
else
1617
echo "\033[32m
1718
======================================================================

.gitmodules

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "player_code"]
2+
path = player_code
3+
url = https://github.com/delta/codecharacter-default-codes-2022.git

Dockerfile

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
## Base
2+
FROM golang:1.18-alpine AS base
3+
4+
RUN apk add --no-cache build-base ccls py3-lsp-server
5+
16
## Build
2-
FROM golang:1.18-alpine AS build
7+
FROM base AS build
38

49
WORKDIR /app
510

@@ -15,7 +20,7 @@ FROM build AS dev
1520

1621
WORKDIR /app
1722

18-
RUN apk add --no-cache make ccls
23+
RUN apk add --no-cache make
1924

2025
RUN go install github.com/cespare/reflex@latest
2126

@@ -24,7 +29,7 @@ CMD ["make watch"]
2429

2530

2631
## Prod
27-
FROM alpine:latest AS prod
32+
FROM base AS prod
2833

2934
WORKDIR /
3035

config/environment.go

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
)
1111

1212
var ServerPort string
13+
var FrontendURL string
1314

1415
func InitConfig() {
1516
err := godotenv.Load()
@@ -18,4 +19,5 @@ func InitConfig() {
1819
}
1920

2021
ServerPort = os.Getenv("SERVER_PORT")
22+
FrontendURL = os.Getenv("FRONTEND_URL")
2123
}

controllers/file.go

+13-15
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,25 @@
11
package controllers
22

33
import (
4-
"fmt"
54
"io/ioutil"
5+
"path/filepath"
66

77
"github.com/delta/codecharacter-lsp-2023/models"
88
)
99

1010
func handleFileUpdate(message map[string]interface{}, ws *models.WebsocketConnection) error {
11-
fmt.Println("Processing File Update Request")
12-
var filename string
13-
switch ws.Language {
14-
case "cpp":
15-
filename = "run.cpp"
16-
case "java":
17-
filename = "run.java"
18-
case "python":
19-
filename = "run.py"
20-
}
21-
err := ioutil.WriteFile(ws.WorkspacePath+"/"+filename, []byte(message["code"].(string)), 0644)
11+
filename := "player" + ws.Language.GetExtension()
12+
return ioutil.WriteFile(ws.WorkspacePath+"/"+filename, []byte(message["code"].(string)), 0644)
13+
}
14+
15+
func getAbsPath(ws *models.WebsocketConnection) error {
16+
absFolderPath, err := filepath.Abs(ws.WorkspacePath)
17+
responseBody := make(map[string]interface{})
2218
if err != nil {
23-
fmt.Println(err)
24-
return err
19+
return SendErrorMessage(ws, err)
2520
}
26-
return nil
21+
responseBody["status"] = "success"
22+
responseBody["folderpath"] = absFolderPath
23+
responseBody["filepath"] = absFolderPath + "/player" + ws.Language.GetExtension()
24+
return SendMessage(ws, responseBody)
2725
}

controllers/jsonrpc.go

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package controllers
2+
3+
import (
4+
"bufio"
5+
"encoding/json"
6+
"fmt"
7+
"strconv"
8+
"strings"
9+
10+
"github.com/delta/codecharacter-lsp-2023/models"
11+
)
12+
13+
func StreamReader(ws *models.WebsocketConnection) {
14+
if ws.LSPServer.Process == nil {
15+
return
16+
}
17+
for {
18+
var responseMessageBytes []byte
19+
reader := bufio.NewReader(ws.LSPServer.Stdout)
20+
contentLengthLine, err := reader.ReadBytes('\n')
21+
var contentLengthHeader string
22+
contentLengthHeader = string(contentLengthLine)
23+
if err != nil {
24+
if err.Error() == "EOF" {
25+
return
26+
}
27+
continue
28+
}
29+
const prefix = "Content-Length: "
30+
const suffix = "\r\n"
31+
contentLengthHeader = strings.TrimPrefix(contentLengthHeader, prefix)
32+
contentLengthHeader = strings.TrimSuffix(contentLengthHeader, suffix)
33+
contentLength, err := strconv.Atoi(contentLengthHeader)
34+
if err != nil {
35+
continue
36+
}
37+
_, err = reader.ReadBytes('\n')
38+
if err != nil {
39+
continue
40+
}
41+
for i := 0; i < contentLength; i++ {
42+
currbyte, err := reader.ReadByte()
43+
if err != nil {
44+
continue
45+
}
46+
responseMessageBytes = append(responseMessageBytes, currbyte)
47+
}
48+
var responseMessage map[string]interface{}
49+
err = json.Unmarshal(responseMessageBytes, &responseMessage)
50+
if err != nil {
51+
continue
52+
}
53+
_ = SendMessage(ws, responseMessage)
54+
}
55+
}
56+
57+
func handleJSONRPC(ws *models.WebsocketConnection, requestMessageBytes []byte) error {
58+
contentLength := len(requestMessageBytes)
59+
header := fmt.Sprintf("Content-Length: %d\n\n", contentLength)
60+
requestMessage := header + string(requestMessageBytes)
61+
_, err := ws.LSPServer.Stdin.Write([]byte(requestMessage))
62+
return err
63+
}

controllers/message.go

+13-11
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package controllers
22

33
import (
44
"encoding/json"
5-
"fmt"
65

76
"github.com/delta/codecharacter-lsp-2023/models"
87
"github.com/gorilla/websocket"
@@ -15,7 +14,6 @@ func HandleMessage(ws *models.WebsocketConnection, messageBytes []byte) error {
1514
return err
1615
}
1716
_, isPresent := message["jsonrpc"]
18-
fmt.Println("Is JSONRPC? : ", isPresent)
1917
if isPresent {
2018
return handleJSONRPCRequest(ws, messageBytes)
2119
}
@@ -27,22 +25,26 @@ func SendMessage(ws *models.WebsocketConnection, message map[string]interface{})
2725
if err != nil {
2826
return err
2927
}
30-
err = ws.Connection.WriteMessage(websocket.TextMessage, messageBytes)
31-
if err != nil {
32-
return err
33-
}
34-
return nil
28+
return ws.Connection.WriteMessage(websocket.TextMessage, messageBytes)
29+
}
30+
31+
func SendErrorMessage(ws *models.WebsocketConnection, message error) error {
32+
responseBody := make(map[string]interface{})
33+
responseBody["status"] = "error"
34+
responseBody["message"] = message.Error()
35+
return SendMessage(ws, responseBody)
3536
}
3637

3738
func handleJSONRPCRequest(ws *models.WebsocketConnection, messageBytes []byte) error {
38-
fmt.Println("JSONRPC Request : ", string(messageBytes), " with ID : ", ws.ID)
39-
return nil
39+
return handleJSONRPC(ws, messageBytes)
4040
}
4141

4242
func handleWebSocketRequest(ws *models.WebsocketConnection, message map[string]interface{}) error {
43-
fmt.Println("Websocket Request : ", message, " with ID : ", ws.ID)
44-
if message["operation"] == "fileUpdate" {
43+
switch message["operation"] {
44+
case "fileUpdate":
4545
return handleFileUpdate(message, ws)
46+
case "getAbsPath":
47+
return getAbsPath(ws)
4648
}
4749
return nil
4850
}

go.mod

+1-5
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,7 @@ require (
66
github.com/fatih/color v1.13.0
77
github.com/joho/godotenv v1.4.0
88
github.com/labstack/echo/v4 v4.9.0
9-
)
10-
11-
require github.com/gorilla/websocket v1.5.0
12-
13-
require (
9+
github.com/gorilla/websocket v1.5.0
1410
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
1511
github.com/google/uuid v1.3.0
1612
github.com/labstack/gommon v0.4.0 // indirect

main.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@ func main() {
1313

1414
server := echo.New()
1515
utils.InitLogger(server)
16-
server.Use(middleware.CORS())
1716
config.InitConfig()
17+
server.Use(middleware.CORSWithConfig(middleware.CORSConfig{
18+
AllowOrigins: []string{config.FrontendURL},
19+
AllowHeaders: []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAccept},
20+
}))
1821
router.InitRoutes(server)
1922

2023
server.Logger.Fatal(server.Start(":" + config.ServerPort))

models/language.go

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package models
2+
3+
type Language int
4+
5+
const (
6+
Cpp = iota
7+
Java
8+
Python
9+
)
10+
11+
func (lang Language) GetLanguage() string {
12+
return []string{"cpp", "java", "python"}[lang]
13+
}
14+
15+
func (lang Language) GetExtension() string {
16+
return []string{".cpp", ".java", ".py"}[lang]
17+
}

models/lsp.go

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package models
2+
3+
import (
4+
"io"
5+
"os"
6+
"os/exec"
7+
)
8+
9+
type LSPServer struct {
10+
Process *exec.Cmd
11+
Stdin io.WriteCloser
12+
Stdout io.ReadCloser
13+
DevNullFd *os.File
14+
}

models/websocket.go

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
11
package models
22

33
import (
4-
"os/exec"
5-
64
"github.com/google/uuid"
75
"github.com/gorilla/websocket"
86
)
97

108
type WebsocketConnection struct {
119
ID uuid.UUID
1210
Connection *websocket.Conn
13-
Language string
11+
Language Language
1412
WorkspacePath string
15-
LSPServer *exec.Cmd
13+
LSPServer LSPServer
1614
}

player_code

Submodule player_code added at 16fbce4

readme.md

+4
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@
3333
```sh
3434
cp .env.example .env
3535
```
36+
- Init Submodules
37+
```sh
38+
git submodule update --init
39+
```
3640
- Enable githooks
3741
```sh
3842
git config core.hooksPath .githooks

router/websocket.go

+1-5
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,5 @@ import (
66
)
77

88
func handleWebSocketConnection(c echo.Context) error {
9-
err := utils.InitWebsocket(c)
10-
if err != nil {
11-
return err
12-
}
13-
return nil
9+
return utils.InitWebsocket(c)
1410
}

servers/ccls.go

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package servers
2+
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
"os"
7+
"os/exec"
8+
9+
"github.com/delta/codecharacter-lsp-2023/models"
10+
)
11+
12+
func createCppServer(ws *models.WebsocketConnection) error {
13+
filename := "compile_commands.json"
14+
err := ioutil.WriteFile(ws.WorkspacePath+"/"+filename, []byte(createCompileCommands(ws)), 0644)
15+
if err != nil {
16+
return err
17+
}
18+
ws.LSPServer.Process = exec.Command("ccls", `--init={
19+
"index":{
20+
"onChange":true,
21+
"trackDependency":0,
22+
"threads":2,
23+
"comments":0
24+
},
25+
"cache":{
26+
"retainInMemory":1
27+
},
28+
"diagnostics":{
29+
"onSave":1500
30+
}
31+
}`)
32+
ws.LSPServer.Stdin, err = ws.LSPServer.Process.StdinPipe()
33+
if err != nil {
34+
return err
35+
}
36+
ws.LSPServer.Stdout, err = ws.LSPServer.Process.StdoutPipe()
37+
if err != nil {
38+
return err
39+
}
40+
devnull, _ := os.OpenFile(os.DevNull, os.O_WRONLY, 0755)
41+
ws.LSPServer.Process.Stderr = devnull
42+
ws.LSPServer.DevNullFd = devnull
43+
return ws.LSPServer.Process.Start()
44+
}
45+
46+
func createCompileCommands(ws *models.WebsocketConnection) string {
47+
return fmt.Sprintf(`[
48+
{
49+
"directory": "%[1]s",
50+
"command": "/usr/bin/c++ -std=c++17 -I%[1]s/player_code.h -o CMakeFiles/MyProject.dir/main.cpp.o -c %[1]s/main.cpp",
51+
"file": "%[1]s/main.cpp"
52+
},
53+
{
54+
"directory": "%[1]s",
55+
"command": "/usr/bin/c++ -std=c++17 -I%[1]s/player_code.h -o CMakeFiles/MyProject.dir/player_code.cpp.o -c %[1]s/player_code.cpp",
56+
"file": "%[1]s/player_code.cpp"
57+
},
58+
{
59+
"directory": "%[1]s",
60+
"command": "/usr/bin/c++ -std=c++17 -I%[1]s/player_code.h -o CMakeFiles/MyProject.dir/player.cpp.o -c %[1]s/player.cpp",
61+
"file": "%[1]s/player.cpp"
62+
}
63+
]`, ws.WorkspacePath)
64+
}

0 commit comments

Comments
 (0)