Skip to content

Commit

Permalink
Make it vs-code compatible
Browse files Browse the repository at this point in the history
VScode has some hard-coded assumptions of how a LSP binary is called:
https://github.com/microsoft/vscode-languageserver-node/blob/0cb3812e7d540ef3a904e96df795bc37a21de9b0/client/src/node/main.ts#L378-L387

Therefore, we adjust the command line args.
  • Loading branch information
PatWie committed Aug 9, 2024
1 parent 554c642 commit f20e635
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 49 deletions.
120 changes: 120 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ thiserror = "1.0.63"
lsp-types = "0.97.0"
tree-sitter-cpp = "0.22.3"
tree-sitter-go = "0.21.0"
clap = { version = "4.5.14", features = ["derive"] }
19 changes: 9 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,25 +92,24 @@ See [configs](./config/code_actions/) for examples.

### Using the Language Server

1. Copy the contents of the `code_actions` configs directory to
1. Copy or symlink the contents of the `code_actions` configs directory to
`$HOME/.config/polyglot_ls/code_actions/`.

2. Run the server:
```sh
mkdir -p ${HOME}/.config/polyglot_ls
ln -s $(realpath config/code_actions) ${HOME}/.config/polyglot_ls/code_actions
```

```sh
./target/release/polyglot_ls
```

For debugging:
2. Run the server:

```sh
./target/release/polyglot_ls --listen
./target/release/polyglot_ls --socket=9257
```

For direct usage in Neovim:

```sh
./target/release/polyglot_ls --stdin
./target/release/polyglot_ls --stdio
```

## Limitations
Expand All @@ -128,7 +127,7 @@ local configs = require 'lspconfig.configs'
if not configs.polyglot_ls then
configs.polyglot_ls = {
default_config = {
cmd = { "/path/to/polyglot_ls" , "--stdin" },
cmd = { "/path/to/polyglot_ls" , "--stdio" },
-- for debugging, launch "polyglot_ls" with --listen and use:
-- cmd = vim.lsp.rpc.connect('127.0.0.1', 9257),
filetypes = { 'python', 'rust', 'text', 'go', 'gitcommit', 'markdown' },
Expand Down
38 changes: 38 additions & 0 deletions config/code_actions/__all__/answer.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
local M = {
is_triggered = function(lsp_range)
return true
end,

action_name = function()
return "Answer below"
end,

process_answer = function(llm_response, lsp_range)
return llm_response
end,

create_prompt = function(lsp_range)
local question_text = active_doc:text_from_range(lsp_range)
local prompt = table.concat({
[=====[ Human:
You are a professional Assistant. Answer the given question. Your output can contain markdown. You output the assistant response.
Here is the question:
<question> ]=====], question_text, [=====[
</question>
Assistant: ]=====] })
print(prompt)
return prompt
end,

placement_range = function(lsp_range)
-- place below
return {
start_line = lsp_range.end_line + 1,
start_character = 0,
end_line = lsp_range.end_line + 1,
end_character = 0,
}
end
}

return M
111 changes: 72 additions & 39 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use clap::{ArgGroup, Parser};
use code_action_providers::config;
use code_action_providers::lua_provider::LuaProvider;
use code_action_providers::parsed_document::ParsedDocument;
Expand All @@ -20,7 +21,15 @@ use tower_lsp::jsonrpc::Result;
use tower_lsp::lsp_types::*;
use tower_lsp::{Client, LanguageServer, LspService, Server};

const SUPPORTED_LANGUAGES: [&str; 7] = ["rust", "python", "text", "go", "__all__", "markdown", "gitcommit"];
const SUPPORTED_LANGUAGES: [&str; 7] = [
"rust",
"python",
"text",
"go",
"__all__",
"markdown",
"gitcommit",
];

#[derive(Debug, Serialize, Deserialize)]
pub struct ResolveActionKind {
Expand Down Expand Up @@ -314,6 +323,31 @@ fn read_language_config_files(config_dir: &Path, filter: &str) -> Vec<PathBuf> {
}
config_files
}
// https://github.com/microsoft/vscode-languageserver-node/blob/0cb3812e7d540ef3a904e96df795bc37a21de9b0/client/src/node/main.ts#L378-L387
#[derive(Parser)]
#[command(
name = "polyglot_ls",
version = "1.0",
about = "An LLM-based lsp with lua scription and tree-sitter context"
)]
#[command(group(
ArgGroup::new("input")
.required(true)
.args(&["socket", "stdio", "bind"]),
))]
struct Args {
/// Socket the LSP server will listen on
#[arg(long)]
socket: Option<u16>, // Option to allow it to be optional

/// Socket the LSP server will bind on
#[arg(long)]
bind: Option<u16>, // Option to allow it to be optional

/// LSP server will read input from stdin
#[arg(long)]
stdio: bool, // Just a flag, no value needed
}

#[tokio::main]
async fn main() {
Expand Down Expand Up @@ -373,42 +407,41 @@ async fn main() {
indexed_text: Arc::new(RwLock::new(nonsense::IndexedText::new("".to_owned()))),
});

let mut args = std::env::args();
match args.nth(1).as_deref() {
None => {
// If no argument is supplied (args is just the program name), then
// we presume that the client has opened the TCP port and is waiting
// for us to connect. This is the connection pattern used by clients
// built with vscode-langaugeclient.
let stream = TcpStream::connect("127.0.0.1:9257").await.unwrap();
let (read, write) = tokio::io::split(stream);
#[cfg(feature = "runtime-agnostic")]
let (read, write) = (read.compat(), write.compat_write());

Server::new(read, write, socket).serve(service).await;
}
Some("--stdin") => {
let (stdin, stdout) = (tokio::io::stdin(), tokio::io::stdout());
#[cfg(feature = "runtime-agnostic")]
let (stdin, stdout) = (stdin.compat(), stdout.compat_write());

Server::new(stdin, stdout, socket).serve(service).await;
}
Some("--listen") => {
// If the `--listen` argument is supplied, then the roles are
// reversed: we need to start a server and wait for the client to
// connect.
let listener = TcpListener::bind("127.0.0.1:9257").await.unwrap();
let (stream, _) = listener.accept().await.unwrap();
let (read, write) = tokio::io::split(stream);
#[cfg(feature = "runtime-agnostic")]
let (read, write) = (read.compat(), write.compat_write());

Server::new(read, write, socket).serve(service).await;
}
Some(arg) => panic!(
"Unrecognized argument: {}. Use --listen to listen for connections or --stdin to use stdin.",
arg
),
};
let args = Args::parse();

if let Some(port) = args.socket {
// If no argument is supplied (args is just the program name), then
// we presume that the client has opened the TCP port and is waiting
// for us to connect. This is the connection pattern used by clients
// built with vscode-langaugeclient.
let stream = TcpStream::connect(format!("127.0.0.1:{port}"))
.await
.unwrap();

let (read, write) = tokio::io::split(stream);
#[cfg(feature = "runtime-agnostic")]
let (read, write) = (read.compat(), write.compat_write());

Server::new(read, write, socket).serve(service).await;
} else if args.stdio {
let (stdin, stdout) = (tokio::io::stdin(), tokio::io::stdout());
#[cfg(feature = "runtime-agnostic")]
let (stdin, stdout) = (stdin.compat(), stdout.compat_write());
Server::new(stdin, stdout, socket).serve(service).await;
} else if let Some(port) = args.bind {
// If the `--bind` argument is supplied, then the roles are
// reversed: we need to start a server and wait for the client to
// connect.
let listener = TcpListener::bind(format!("127.0.0.1:{port}"))
.await
.unwrap();
let (stream, _) = listener.accept().await.unwrap();
let (read, write) = tokio::io::split(stream);
#[cfg(feature = "runtime-agnostic")]
let (read, write) = (read.compat(), write.compat_write());

Server::new(read, write, socket).serve(service).await;
} else {
println!("No input method specified");
}
}

0 comments on commit f20e635

Please sign in to comment.