Skip to content

Commit 187d190

Browse files
committed
Support --json-(object|null|false)-value options
1 parent 4714ca3 commit 187d190

File tree

4 files changed

+72
-21
lines changed

4 files changed

+72
-21
lines changed

src/app.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use log::{warn, info};
44
use anyhow::Result;
55
use serde_json as json;
66

7-
use crate::{rpcio, bytecode};
7+
use crate::{rpcio, bytecode::{self, BytecodeOptions}};
88
use crate::lsp_message::{LspRequest, LspResponse, LspResponseError};
99

1010
fn process_channel_to_writer(channel_sub: mpsc::Receiver<String>,
@@ -63,15 +63,16 @@ fn process_client_reader(reader: impl std::io::Read,
6363
}
6464

6565
fn process_server_reader(reader: impl std::io::Read,
66-
channel_pub: mpsc::Sender<String>) -> Result<()> {
66+
channel_pub: mpsc::Sender<String>,
67+
bytecode_options: BytecodeOptions) -> Result<()> {
6768
let mut bufreader = std::io::BufReader::new(reader);
6869
loop {
6970
let msg = rpcio::rpc_read(&mut bufreader)?;
7071
if msg.is_empty() {
7172
break
7273
}
7374
let json_val = json::from_str(&msg)?;
74-
match bytecode::generate_bytecode_repl(&json_val, bytecode::BytecodeOptions::default()) {
75+
match bytecode::generate_bytecode_repl(&json_val, bytecode_options.clone()) {
7576
Ok(bytecode_str) => {
7677
channel_pub.send(bytecode_str)?;
7778
},
@@ -84,9 +85,14 @@ fn process_server_reader(reader: impl std::io::Read,
8485
Ok(())
8586
}
8687

88+
pub struct AppOptions {
89+
pub bytecode_options: bytecode::BytecodeOptions,
90+
}
91+
8792
pub fn run_app_forever(client_reader: impl std::io::Read + Send + 'static,
8893
client_writer: impl std::io::Write + Send + 'static,
89-
mut server_cmd: std::process::Command) -> Result<std::process::ExitStatus> {
94+
mut server_cmd: std::process::Command,
95+
options: AppOptions) -> Result<std::process::ExitStatus> {
9096
info!("Running server {:?}", server_cmd);
9197
let mut proc = server_cmd
9298
.stdin(std::process::Stdio::piped())
@@ -117,7 +123,7 @@ pub fn run_app_forever(client_reader: impl std::io::Read + Send + 'static,
117123
let proc_stdout = proc.stdout.take().unwrap();
118124
std::thread::spawn(move || {
119125
info!("Started server->client read thread");
120-
process_server_reader(proc_stdout, s2c_channel_pub).unwrap();
126+
process_server_reader(proc_stdout, s2c_channel_pub, options.bytecode_options).unwrap();
121127
info!("Finished server->client read thread");
122128
});
123129
}

src/bytecode.rs

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::collections::BTreeMap;
2+
use std::str::FromStr;
23

34
use anyhow::{Result, bail};
45
use serde_json as json;
@@ -17,6 +18,22 @@ pub enum LispObject {
1718
Vector(Vec<LispObject>),
1819
}
1920

21+
impl FromStr for LispObject {
22+
type Err = anyhow::Error;
23+
24+
fn from_str(s: &str) -> Result<Self> {
25+
if s == "nil" {
26+
Ok(Self::Nil)
27+
} else if s == "t" {
28+
Ok(Self::T)
29+
} else if s.starts_with(":") {
30+
Ok(Self::Symbol(s[1..].to_string()))
31+
} else {
32+
bail!("Supported LispObject: {}", s)
33+
}
34+
}
35+
}
36+
2037
impl LispObject {
2138
fn to_repl(&self) -> String {
2239
match self {
@@ -139,26 +156,27 @@ impl Op {
139156
}
140157
}
141158

142-
#[derive(Clone, Copy, Debug)]
159+
#[derive(Clone, Copy, Debug, PartialEq, Eq, clap::ValueEnum)]
143160
pub enum ObjectType {
144161
Plist,
145162
Hashtable,
146163
Alist,
147164
}
148165

166+
#[derive(Clone)]
149167
pub struct BytecodeOptions {
150168
pub object_type: ObjectType,
151169
// TODO: array_type
152-
pub null_object: LispObject,
153-
pub false_object: LispObject,
170+
pub null_value: LispObject,
171+
pub false_value: LispObject,
154172
}
155173

156174
impl Default for BytecodeOptions {
157175
fn default() -> Self {
158176
Self {
159177
object_type: ObjectType::Plist,
160-
null_object: LispObject::Nil,
161-
false_object: LispObject::Nil,
178+
null_value: LispObject::Nil,
179+
false_value: LispObject::Nil,
162180
}
163181
}
164182
}
@@ -270,10 +288,10 @@ impl BytecodeCompiler {
270288
fn compile_value(&mut self, value: &json::Value) {
271289
match value {
272290
&json::Value::Null => {
273-
self.compile_constant_op(self.options.null_object.clone());
291+
self.compile_constant_op(self.options.null_value.clone());
274292
},
275293
&json::Value::Bool(false) => {
276-
self.compile_constant_op(self.options.false_object.clone());
294+
self.compile_constant_op(self.options.false_value.clone());
277295
},
278296
&json::Value::Bool(true) => {
279297
self.compile_constant_op(LispObject::T);

src/main.rs

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,30 @@ use anyhow::{Result, bail};
22
use clap::Parser;
33

44
use emacs_lsp_booster::app;
5+
use emacs_lsp_booster::bytecode;
56

67

7-
#[derive(Parser, Default)]
8+
#[derive(Parser)]
89
#[command(long_about = None, about = None,
9-
arg_required_else_help = true, after_help = "For backward compatibility, `emacs-lsp-booster <SERVER_CMD>...` (without any options) is also supported\n" )]
10+
arg_required_else_help = true, after_help = "For backward compatibility, `emacs-lsp-booster <SERVER_CMD>...` (without any options) is also supported" )]
1011
struct Cli {
1112
#[command(flatten)]
1213
verbose: clap_verbosity_flag::Verbosity,
1314

1415
#[arg(last = true)]
1516
server_cmd: Vec<String>,
17+
18+
#[arg(long, default_value = "plist",
19+
help = "Lisp type used to represent a JSON object. Plist is the most performant one.\nMust match what lsp client expects.\n")]
20+
json_object_type: bytecode::ObjectType,
21+
22+
#[arg(long, default_value = "nil",
23+
help = "Which lisp value is used to represent a JSON null value. Support :SYMBOL or nil.\nMust match what lsp client expects.\n")]
24+
json_null_value: bytecode::LispObject,
25+
26+
#[arg(long, default_value = "nil",
27+
help = "Which lisp value is used to represent a JSON false value. Support :SYMBOL or nil.\nMust match what lsp client expects.\n")]
28+
json_false_value: bytecode::LispObject,
1629
}
1730

1831
fn parse_args<T, S>(args: T) -> Cli
@@ -21,10 +34,9 @@ where T: IntoIterator<Item=S>,
2134
let args = args.into_iter().map(|x| x.into()).collect::<Vec<String>>();
2235
// backward compatible. support `emacs-lsp-booster server_cmd args...` directly
2336
if args.len() > 1 && !args[1].starts_with('-') && !args.contains(&"--".into()) {
24-
Cli {
25-
server_cmd: args[1..].to_vec(),
26-
..Default::default()
27-
}
37+
let mut fake_args = vec![args[0].clone(), "--".into()];
38+
fake_args.extend_from_slice(&args[1..]);
39+
Cli::parse_from(fake_args)
2840
} else {
2941
Cli::parse_from(args)
3042
}
@@ -48,7 +60,13 @@ fn main() -> Result<()> {
4860
let mut cmd = std::process::Command::new(&cli.server_cmd[0]);
4961
cmd.args(&cli.server_cmd[1..]);
5062

51-
let exit_status = app::run_app_forever(std::io::stdin(), std::io::stdout(), cmd)?;
63+
let exit_status = app::run_app_forever(std::io::stdin(), std::io::stdout(), cmd, app::AppOptions {
64+
bytecode_options: bytecode::BytecodeOptions {
65+
object_type: cli.json_object_type,
66+
null_value: cli.json_null_value,
67+
false_value: cli.json_false_value,
68+
},
69+
})?;
5270
std::process::exit(exit_status.code().unwrap_or(1))
5371
}
5472

@@ -60,7 +78,14 @@ fn test_parse_args() {
6078
let cli = parse_args(vec!["emacs-lsp-booster", "--", "server_cmd", "arg1"]);
6179
assert_eq!(cli.server_cmd, vec!["server_cmd", "arg1"]);
6280

63-
let cli = parse_args(vec!["emacs-lsp-booster", "-v", "--", "server_cmd", "arg1"]);
81+
let cli = parse_args(vec!["emacs-lsp-booster", "-v",
82+
"--json-object-type", "hashtable",
83+
"--json-null-value", ":null",
84+
"--json-false-value", ":json-false",
85+
"--", "server_cmd", "arg1"]);
6486
assert_eq!(cli.verbose.log_level_filter(), log::LevelFilter::Warn);
6587
assert_eq!(cli.server_cmd, vec!["server_cmd", "arg1"]);
88+
assert_eq!(cli.json_object_type, bytecode::ObjectType::Hashtable);
89+
assert_eq!(cli.json_null_value, bytecode::LispObject::Symbol("null".into()));
90+
assert_eq!(cli.json_false_value, bytecode::LispObject::Symbol("json-false".into()));
6691
}

tests/app_test.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ fn test_app_with_echo_server() -> Result<()> {
2525
let mut cmd = std::process::Command::new("timeout");
2626
cmd.args(&["1", "cat"]);
2727

28-
let exit_status = app::run_app_forever(input_pair_out, output_file, cmd)?;
28+
let exit_status = app::run_app_forever(input_pair_out, output_file, cmd, app::AppOptions {
29+
bytecode_options: Default::default(),
30+
})?;
2931
assert!(!exit_status.success()); // timeout kill
3032

3133
let output = std::fs::read_to_string(tmpdir.path().join("output.txt"))?;

0 commit comments

Comments
 (0)