Reputation: 11
I am trying to create a web based code editor which can edit a single python file. Now while editing, i wanted to integrate pyright which is an LSP for python but i am unable to find any documentation which describes how we can use pyright like a language server. All i could find was how to run pyright directly through terminal and get one time suggestions like static analysis, type checking, etc. However, i am looking for full fledged lsp experience where i can send pyright standard JSONRPC calls like textDocument/{onChange,didOpen,didSave,...}
, initialize
, textDocument/completion
, etc.
I came across running pyright as pyright-langserver --stdio
but i am unable to get it to work at all. Like, how am i supposed to pass these Json calls to it?
I tried creating a python script to simulate the JsonRPC calls but it just blocks after outputting it's standard three initialization lines:
Content-Length: 119
{"jsonrpc":"2.0","method":"window/logMessage","params":{"type":3,"message":"Pyright language server 1.1.391 starting"}}Content-Length: 152
{"jsonrpc":"2.0","method":"window/logMessage","params":{"type":3,"message":"Server root directory: file:///opt/homebrew/lib/node_modules/pyright/dist"}}
And any further input i give to it's stdin is just blocked and the program hangs.
The script which i am using:
import subprocess
import json
import time
process = subprocess.Popen(
["pyright-langserver", "--stdio"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
)
def send_request(id, method, params):
request = json.dumps(
{"jsonrpc": "2.0", "id": id, "method": method, "params": params}
)
request += "\n" # Add newline to signify end of request
print(f"Sending request: {request}")
process.stdin.write(request.encode())
process.stdin.flush()
def read_response():
buffer = b""
while b"\r\n\r\n" not in buffer:
buffer += process.stdout.read(1)
headers, content = buffer.split(b"\r\n\r\n", 1)
content_length = next(
int(header.split(b":")[1].strip())
for header in headers.split(b"\r\n")
if header.startswith(b"Content-Length")
)
while len(content) < content_length:
content += process.stdout.read(1)
print(f"Raw JSON response: {content.decode()}")
return json.loads(content.decode())
# init request
send_request(
1,
"initialize",
{"rootUri": "file:///Users/arpit/Programming/Python/test-project"},
)
response = read_response()
print("Initialize response:", response)
# Sending a simple completion request
send_request(
2,
"textDocument/completion",
{
"textDocument": {
"uri": "file:///Users/arpit/Programming/Python/test-project/main.py"
},
"position": {"line": 7, "character": 20},
},
)
response = read_response()
print("Completion response:", response)
# Sending a simple completion request again
send_request(
2,
"textDocument/completion",
{
"textDocument": {
"uri": "file:///Users/arpit/Programming/Python/test-project/main.py"
},
"position": {"line": 5, "character": 10},
},
)
response = read_response()
print("Completion response:", response)
Any help in this is greatly appreciated! All i am getting from searching around the internet is how to integrate pyright into existing code editors like neovim, vim, emacs, etc.
I tried to simulate some JsonRPC calls by giving them through a python script to the pyright process. However, no output comes as a consequence of my input commands. The output which i get is the standard initialization output which we get even if we start the process from terminal, and it just blocks on any further input commands.
Upvotes: 1
Views: 63
Reputation: 21
I had the same problem and after a lot of tries, I finally found why the pyright-langserver didn't respond.
Your requests lack the "header" part. In fact, LSP requires to send requests like this :
Content-Length: 124
{"jsonrpc":"2.0","id":2,"method":"initialize","params":{"processId":35844,"rootUri":"file:///workspace/","capabilities":{}}}
The content length is the length of the JSON object. For my use case, I need to have a pyright-langserver process that I can reach through websockets. I implemented a small docker image that runs a websocket proxy (which writes all the messages it receives into the pyright-langserver stdin). When the websocket proxy receives a new message, it checks if the Content-Length header is present and if not, it concatenates it in the message pushed in the stdin like this :
const request = `Content-Length: ${Buffer.byteLength(jsonMessage, "utf8")}\r\n\r\n${jsonMessage}`;
This way, the pyright-langserver will parse correctly the LSP request and returns a proper answer.
I hope this helps you !
Upvotes: 2