xi_core_lib/
client.rs

1// Copyright 2018 The xi-editor Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Requests and notifications from the core to front-ends.
16
17use std::time::Instant;
18
19use serde_json::{self, Value};
20use xi_rpc::{self, RpcPeer};
21
22use crate::config::Table;
23use crate::plugins::rpc::ClientPluginInfo;
24use crate::plugins::Command;
25use crate::styles::ThemeSettings;
26use crate::syntax::LanguageId;
27use crate::tabs::ViewId;
28use crate::width_cache::{WidthReq, WidthResponse};
29
30/// An interface to the frontend.
31pub struct Client(RpcPeer);
32
33impl Client {
34    pub fn new(peer: RpcPeer) -> Self {
35        Client(peer)
36    }
37
38    pub fn update_view(&self, view_id: ViewId, update: &Update) {
39        self.0.send_rpc_notification(
40            "update",
41            &json!({
42                "view_id": view_id,
43                "update": update,
44            }),
45        );
46    }
47
48    pub fn scroll_to(&self, view_id: ViewId, line: usize, col: usize) {
49        self.0.send_rpc_notification(
50            "scroll_to",
51            &json!({
52                "view_id": view_id,
53                "line": line,
54                "col": col,
55            }),
56        );
57    }
58
59    pub fn config_changed(&self, view_id: ViewId, changes: &Table) {
60        self.0.send_rpc_notification(
61            "config_changed",
62            &json!({
63                "view_id": view_id,
64                "changes": changes,
65            }),
66        );
67    }
68
69    pub fn available_themes(&self, theme_names: Vec<String>) {
70        self.0.send_rpc_notification("available_themes", &json!({ "themes": theme_names }))
71    }
72
73    pub fn available_languages(&self, languages: Vec<LanguageId>) {
74        self.0.send_rpc_notification("available_languages", &json!({ "languages": languages }))
75    }
76
77    pub fn theme_changed(&self, name: &str, theme: &ThemeSettings) {
78        self.0.send_rpc_notification(
79            "theme_changed",
80            &json!({
81                "name": name,
82                "theme": theme,
83            }),
84        );
85    }
86
87    pub fn language_changed(&self, view_id: ViewId, new_lang: &LanguageId) {
88        self.0.send_rpc_notification(
89            "language_changed",
90            &json!({
91                "view_id": view_id,
92                "language_id": new_lang,
93            }),
94        );
95    }
96
97    /// Notify the client that a plugin has started.
98    pub fn plugin_started(&self, view_id: ViewId, plugin: &str) {
99        self.0.send_rpc_notification(
100            "plugin_started",
101            &json!({
102                "view_id": view_id,
103                "plugin": plugin,
104            }),
105        );
106    }
107
108    /// Notify the client that a plugin has stopped.
109    ///
110    /// `code` is not currently used; in the future may be used to
111    /// pass an exit code.
112    pub fn plugin_stopped(&self, view_id: ViewId, plugin: &str, code: i32) {
113        self.0.send_rpc_notification(
114            "plugin_stopped",
115            &json!({
116                "view_id": view_id,
117                "plugin": plugin,
118                "code": code,
119            }),
120        );
121    }
122
123    /// Notify the client of the available plugins.
124    pub fn available_plugins(&self, view_id: ViewId, plugins: &[ClientPluginInfo]) {
125        self.0.send_rpc_notification(
126            "available_plugins",
127            &json!({
128                "view_id": view_id,
129                "plugins": plugins }),
130        );
131    }
132
133    pub fn update_cmds(&self, view_id: ViewId, plugin: &str, cmds: &[Command]) {
134        self.0.send_rpc_notification(
135            "update_cmds",
136            &json!({
137                "view_id": view_id,
138                "plugin": plugin,
139                "cmds": cmds,
140            }),
141        );
142    }
143
144    pub fn def_style(&self, style: &Value) {
145        self.0.send_rpc_notification("def_style", &style)
146    }
147
148    pub fn find_status(&self, view_id: ViewId, queries: &Value) {
149        self.0.send_rpc_notification(
150            "find_status",
151            &json!({
152                "view_id": view_id,
153                "queries": queries,
154            }),
155        );
156    }
157
158    pub fn replace_status(&self, view_id: ViewId, replace: &Value) {
159        self.0.send_rpc_notification(
160            "replace_status",
161            &json!({
162                "view_id": view_id,
163                "status": replace,
164            }),
165        );
166    }
167
168    /// Ask front-end to measure widths of strings.
169    pub fn measure_width(&self, reqs: &[WidthReq]) -> Result<WidthResponse, xi_rpc::Error> {
170        let req_json = serde_json::to_value(reqs).expect("failed to serialize width req");
171        let resp = self.0.send_rpc_request("measure_width", &req_json)?;
172        Ok(serde_json::from_value(resp).expect("failed to deserialize width response"))
173    }
174
175    pub fn alert<S: AsRef<str>>(&self, msg: S) {
176        self.0.send_rpc_notification("alert", &json!({ "msg": msg.as_ref() }));
177    }
178
179    pub fn add_status_item(
180        &self,
181        view_id: ViewId,
182        source: &str,
183        key: &str,
184        value: &str,
185        alignment: &str,
186    ) {
187        self.0.send_rpc_notification(
188            "add_status_item",
189            &json!({
190                "view_id": view_id,
191                "source": source,
192                "key": key,
193                "value": value,
194                "alignment": alignment
195            }),
196        );
197    }
198
199    pub fn update_status_item(&self, view_id: ViewId, key: &str, value: &str) {
200        self.0.send_rpc_notification(
201            "update_status_item",
202            &json!({
203                "view_id": view_id,
204                "key": key,
205                "value": value,
206            }),
207        );
208    }
209
210    pub fn remove_status_item(&self, view_id: ViewId, key: &str) {
211        self.0.send_rpc_notification(
212            "remove_status_item",
213            &json!({
214                "view_id": view_id,
215                "key": key,
216            }),
217        );
218    }
219
220    pub fn show_hover(&self, view_id: ViewId, request_id: usize, result: String) {
221        self.0.send_rpc_notification(
222            "show_hover",
223            &json!({
224                "view_id": view_id,
225                "request_id": request_id,
226                "result": result,
227            }),
228        )
229    }
230
231    pub fn schedule_idle(&self, token: usize) {
232        self.0.schedule_idle(token)
233    }
234
235    pub fn schedule_timer(&self, timeout: Instant, token: usize) {
236        self.0.schedule_timer(timeout, token);
237    }
238}
239
240#[derive(Debug, Serialize)]
241pub struct Update {
242    pub(crate) ops: Vec<UpdateOp>,
243    pub(crate) pristine: bool,
244    pub(crate) annotations: Vec<Value>,
245}
246
247#[derive(Debug, Serialize)]
248pub(crate) struct UpdateOp {
249    op: OpType,
250    n: usize,
251    #[serde(skip_serializing_if = "Option::is_none")]
252    lines: Option<Vec<Value>>,
253    #[serde(rename = "ln")]
254    #[serde(skip_serializing_if = "Option::is_none")]
255    first_line_number: Option<usize>,
256}
257
258impl UpdateOp {
259    pub(crate) fn invalidate(n: usize) -> Self {
260        UpdateOp { op: OpType::Invalidate, n, lines: None, first_line_number: None }
261    }
262
263    pub(crate) fn skip(n: usize) -> Self {
264        UpdateOp { op: OpType::Skip, n, lines: None, first_line_number: None }
265    }
266
267    pub(crate) fn copy(n: usize, line: usize) -> Self {
268        UpdateOp { op: OpType::Copy, n, lines: None, first_line_number: Some(line) }
269    }
270
271    pub(crate) fn insert(lines: Vec<Value>) -> Self {
272        UpdateOp { op: OpType::Insert, n: lines.len(), lines: Some(lines), first_line_number: None }
273    }
274}
275
276#[derive(Debug, Clone, Serialize)]
277#[serde(rename_all = "lowercase")]
278enum OpType {
279    #[serde(rename = "ins")]
280    Insert,
281    Skip,
282    Invalidate,
283    Copy,
284}