lapce_rpc/
proxy.rs

1use std::{
2    collections::HashMap,
3    path::PathBuf,
4    sync::{
5        atomic::{AtomicU64, Ordering},
6        Arc,
7    },
8};
9
10use crossbeam_channel::{Receiver, Sender};
11use lsp_types::{
12    request::GotoTypeDefinitionResponse, CodeAction, CodeActionResponse,
13    CompletionItem, DocumentSymbolResponse, GotoDefinitionResponse, Hover,
14    InlayHint, Location, Position, PrepareRenameResponse, SelectionRange,
15    SymbolInformation, TextDocumentItem, TextEdit, WorkspaceEdit,
16};
17use parking_lot::Mutex;
18use serde::{Deserialize, Serialize};
19use lapce_xi_rope::RopeDelta;
20
21use crate::{
22    buffer::BufferId,
23    file::FileNodeItem,
24    plugin::{PluginId, VoltInfo, VoltMetadata},
25    source_control::FileDiff,
26    style::SemanticStyles,
27    terminal::TermId,
28    RequestId, RpcError, RpcMessage,
29};
30
31#[allow(clippy::large_enum_variant)]
32pub enum ProxyRpc {
33    Request(RequestId, ProxyRequest),
34    Notification(ProxyNotification),
35    Shutdown,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
39#[serde(rename_all = "snake_case")]
40#[serde(tag = "method", content = "params")]
41pub enum ProxyRequest {
42    NewBuffer {
43        buffer_id: BufferId,
44        path: PathBuf,
45    },
46    BufferHead {
47        path: PathBuf,
48    },
49    GlobalSearch {
50        pattern: String,
51        case_sensitive: bool,
52    },
53    CompletionResolve {
54        plugin_id: PluginId,
55        completion_item: Box<CompletionItem>,
56    },
57    CodeActionResolve {
58        plugin_id: PluginId,
59        action_item: Box<CodeAction>,
60    },
61    GetHover {
62        request_id: usize,
63        path: PathBuf,
64        position: Position,
65    },
66    GetSignature {
67        buffer_id: BufferId,
68        position: Position,
69    },
70    GetSelectionRange {
71        path: PathBuf,
72        positions: Vec<Position>,
73    },
74    GitGetRemoteFileUrl {
75        file: PathBuf,
76    },
77    GetReferences {
78        path: PathBuf,
79        position: Position,
80    },
81    GetDefinition {
82        request_id: usize,
83        path: PathBuf,
84        position: Position,
85    },
86    GetTypeDefinition {
87        request_id: usize,
88        path: PathBuf,
89        position: Position,
90    },
91    GetInlayHints {
92        path: PathBuf,
93    },
94    GetSemanticTokens {
95        path: PathBuf,
96    },
97    PrepareRename {
98        path: PathBuf,
99        position: Position,
100    },
101    Rename {
102        path: PathBuf,
103        position: Position,
104        new_name: String,
105    },
106    GetCodeActions {
107        path: PathBuf,
108        position: Position,
109    },
110    GetDocumentSymbols {
111        path: PathBuf,
112    },
113    GetWorkspaceSymbols {
114        /// The search query
115        query: String,
116    },
117    GetDocumentFormatting {
118        path: PathBuf,
119    },
120    GetOpenFilesContent {},
121    GetFiles {
122        path: String,
123    },
124    ReadDir {
125        path: PathBuf,
126    },
127    Save {
128        rev: u64,
129        path: PathBuf,
130    },
131    SaveBufferAs {
132        buffer_id: BufferId,
133        path: PathBuf,
134        rev: u64,
135        content: String,
136    },
137    CreateFile {
138        path: PathBuf,
139    },
140    CreateDirectory {
141        path: PathBuf,
142    },
143    TrashPath {
144        path: PathBuf,
145    },
146    RenamePath {
147        from: PathBuf,
148        to: PathBuf,
149    },
150}
151#[derive(Debug, Clone, Serialize, Deserialize)]
152#[serde(rename_all = "snake_case")]
153#[serde(tag = "method", content = "params")]
154pub enum ProxyNotification {
155    Initialize {
156        workspace: Option<PathBuf>,
157        disabled_volts: Vec<String>,
158        plugin_configurations: HashMap<String, HashMap<String, serde_json::Value>>,
159        window_id: usize,
160        tab_id: usize,
161    },
162    OpenFileChanged {
163        path: PathBuf,
164    },
165    OpenPaths {
166        folders: Vec<PathBuf>,
167        files: Vec<PathBuf>,
168    },
169    Shutdown {},
170    Completion {
171        request_id: usize,
172        path: PathBuf,
173        input: String,
174        position: Position,
175    },
176    Update {
177        path: PathBuf,
178        delta: RopeDelta,
179        rev: u64,
180    },
181    UpdatePluginConfigs {
182        configs: HashMap<String, HashMap<String, serde_json::Value>>,
183    },
184    NewTerminal {
185        term_id: TermId,
186        cwd: Option<PathBuf>,
187        shell: String,
188    },
189    InstallVolt {
190        volt: VoltInfo,
191    },
192    RemoveVolt {
193        volt: VoltMetadata,
194    },
195    ReloadVolt {
196        volt: VoltMetadata,
197    },
198    DisableVolt {
199        volt: VoltInfo,
200    },
201    EnableVolt {
202        volt: VoltInfo,
203    },
204    GitCommit {
205        message: String,
206        diffs: Vec<FileDiff>,
207    },
208    GitCheckout {
209        branch: String,
210    },
211    GitDiscardFilesChanges {
212        files: Vec<PathBuf>,
213    },
214    GitDiscardWorkspaceChanges {},
215    GitInit {},
216    TerminalWrite {
217        term_id: TermId,
218        content: String,
219    },
220    TerminalResize {
221        term_id: TermId,
222        width: usize,
223        height: usize,
224    },
225    TerminalClose {
226        term_id: TermId,
227    },
228}
229
230#[derive(Debug, Clone, Serialize, Deserialize)]
231#[serde(rename_all = "snake_case")]
232#[serde(tag = "method", content = "params")]
233pub enum ProxyResponse {
234    GitGetRemoteFileUrl {
235        file_url: String,
236    },
237    NewBufferResponse {
238        content: String,
239    },
240    BufferHeadResponse {
241        version: String,
242        content: String,
243    },
244    ReadDirResponse {
245        items: HashMap<PathBuf, FileNodeItem>,
246    },
247    CompletionResolveResponse {
248        item: Box<CompletionItem>,
249    },
250    CodeActionResolveResponse {
251        item: Box<CodeAction>,
252    },
253    HoverResponse {
254        request_id: usize,
255        hover: Hover,
256    },
257    GetDefinitionResponse {
258        request_id: usize,
259        definition: GotoDefinitionResponse,
260    },
261    GetTypeDefinition {
262        request_id: usize,
263        definition: GotoTypeDefinitionResponse,
264    },
265    GetReferencesResponse {
266        references: Vec<Location>,
267    },
268    GetCodeActionsResponse {
269        plugin_id: PluginId,
270        resp: CodeActionResponse,
271    },
272    GetFilesResponse {
273        items: Vec<PathBuf>,
274    },
275    GetDocumentFormatting {
276        edits: Vec<TextEdit>,
277    },
278    GetDocumentSymbols {
279        resp: DocumentSymbolResponse,
280    },
281    GetWorkspaceSymbols {
282        symbols: Vec<SymbolInformation>,
283    },
284    GetSelectionRange {
285        ranges: Vec<SelectionRange>,
286    },
287    GetInlayHints {
288        hints: Vec<InlayHint>,
289    },
290    GetSemanticTokens {
291        styles: SemanticStyles,
292    },
293    PrepareRename {
294        resp: PrepareRenameResponse,
295    },
296    Rename {
297        edit: WorkspaceEdit,
298    },
299    GetOpenFilesContentResponse {
300        items: Vec<TextDocumentItem>,
301    },
302    GlobalSearchResponse {
303        #[allow(clippy::type_complexity)]
304        matches: HashMap<PathBuf, Vec<(usize, (usize, usize), String)>>,
305    },
306    Success {},
307    SaveResponse {},
308}
309
310pub type ProxyMessage = RpcMessage<ProxyRequest, ProxyNotification, ProxyResponse>;
311
312#[derive(Debug, Clone, Serialize, Deserialize)]
313pub struct ReadDirResponse {
314    pub items: HashMap<PathBuf, FileNodeItem>,
315}
316
317pub trait ProxyCallback: Send + FnOnce(Result<ProxyResponse, RpcError>) {}
318
319impl<F: Send + FnOnce(Result<ProxyResponse, RpcError>)> ProxyCallback for F {}
320
321enum ResponseHandler {
322    Callback(Box<dyn ProxyCallback>),
323    Chan(Sender<Result<ProxyResponse, RpcError>>),
324}
325
326impl ResponseHandler {
327    fn invoke(self, result: Result<ProxyResponse, RpcError>) {
328        match self {
329            ResponseHandler::Callback(f) => f(result),
330            ResponseHandler::Chan(tx) => {
331                let _ = tx.send(result);
332            }
333        }
334    }
335}
336
337pub trait ProxyHandler {
338    fn handle_notification(&mut self, rpc: ProxyNotification);
339    fn handle_request(&mut self, id: RequestId, rpc: ProxyRequest);
340}
341
342#[derive(Clone)]
343pub struct ProxyRpcHandler {
344    tx: Sender<ProxyRpc>,
345    rx: Receiver<ProxyRpc>,
346    id: Arc<AtomicU64>,
347    pending: Arc<Mutex<HashMap<u64, ResponseHandler>>>,
348}
349
350impl ProxyRpcHandler {
351    pub fn new() -> Self {
352        let (tx, rx) = crossbeam_channel::unbounded();
353        Self {
354            tx,
355            rx,
356            id: Arc::new(AtomicU64::new(0)),
357            pending: Arc::new(Mutex::new(HashMap::new())),
358        }
359    }
360
361    pub fn rx(&self) -> &Receiver<ProxyRpc> {
362        &self.rx
363    }
364
365    pub fn mainloop<H>(&self, handler: &mut H)
366    where
367        H: ProxyHandler,
368    {
369        use ProxyRpc::*;
370        for msg in &self.rx {
371            match msg {
372                Request(id, request) => {
373                    handler.handle_request(id, request);
374                }
375                Notification(notification) => {
376                    handler.handle_notification(notification);
377                }
378                Shutdown => {
379                    return;
380                }
381            }
382        }
383    }
384
385    fn request_common(&self, request: ProxyRequest, rh: ResponseHandler) {
386        let id = self.id.fetch_add(1, Ordering::Relaxed);
387
388        self.pending.lock().insert(id, rh);
389
390        let _ = self.tx.send(ProxyRpc::Request(id, request));
391    }
392
393    fn request(&self, request: ProxyRequest) -> Result<ProxyResponse, RpcError> {
394        let (tx, rx) = crossbeam_channel::bounded(1);
395        self.request_common(request, ResponseHandler::Chan(tx));
396        rx.recv().unwrap_or_else(|_| {
397            Err(RpcError {
398                code: 0,
399                message: "io error".to_string(),
400            })
401        })
402    }
403
404    pub fn request_async(
405        &self,
406        request: ProxyRequest,
407        f: impl ProxyCallback + 'static,
408    ) {
409        self.request_common(request, ResponseHandler::Callback(Box::new(f)))
410    }
411
412    pub fn handle_response(
413        &self,
414        id: RequestId,
415        result: Result<ProxyResponse, RpcError>,
416    ) {
417        let handler = { self.pending.lock().remove(&id) };
418        if let Some(handler) = handler {
419            handler.invoke(result);
420        }
421    }
422
423    pub fn notification(&self, notification: ProxyNotification) {
424        let _ = self.tx.send(ProxyRpc::Notification(notification));
425    }
426
427    pub fn git_init(&self) {
428        self.notification(ProxyNotification::GitInit {});
429    }
430
431    pub fn git_commit(&self, message: String, diffs: Vec<FileDiff>) {
432        self.notification(ProxyNotification::GitCommit { message, diffs });
433    }
434
435    pub fn git_checkout(&self, branch: String) {
436        self.notification(ProxyNotification::GitCheckout { branch });
437    }
438
439    pub fn install_volt(&self, volt: VoltInfo) {
440        self.notification(ProxyNotification::InstallVolt { volt });
441    }
442
443    pub fn reload_volt(&self, volt: VoltMetadata) {
444        self.notification(ProxyNotification::ReloadVolt { volt });
445    }
446
447    pub fn remove_volt(&self, volt: VoltMetadata) {
448        self.notification(ProxyNotification::RemoveVolt { volt });
449    }
450
451    pub fn disable_volt(&self, volt: VoltInfo) {
452        self.notification(ProxyNotification::DisableVolt { volt });
453    }
454
455    pub fn enable_volt(&self, volt: VoltInfo) {
456        self.notification(ProxyNotification::EnableVolt { volt });
457    }
458
459    pub fn shutdown(&self) {
460        self.notification(ProxyNotification::Shutdown {});
461        let _ = self.tx.send(ProxyRpc::Shutdown);
462    }
463
464    pub fn initialize(
465        &self,
466        workspace: Option<PathBuf>,
467        disabled_volts: Vec<String>,
468        plugin_configurations: HashMap<String, HashMap<String, serde_json::Value>>,
469        window_id: usize,
470        tab_id: usize,
471    ) {
472        self.notification(ProxyNotification::Initialize {
473            workspace,
474            disabled_volts,
475            plugin_configurations,
476            window_id,
477            tab_id,
478        });
479    }
480
481    pub fn completion(
482        &self,
483        request_id: usize,
484        path: PathBuf,
485        input: String,
486        position: Position,
487    ) {
488        self.notification(ProxyNotification::Completion {
489            request_id,
490            path,
491            input,
492            position,
493        });
494    }
495
496    pub fn new_terminal(
497        &self,
498        term_id: TermId,
499        cwd: Option<PathBuf>,
500        shell: String,
501    ) {
502        self.notification(ProxyNotification::NewTerminal {
503            term_id,
504            cwd,
505            shell,
506        })
507    }
508
509    pub fn terminal_close(&self, term_id: TermId) {
510        self.notification(ProxyNotification::TerminalClose { term_id });
511    }
512
513    pub fn terminal_resize(&self, term_id: TermId, width: usize, height: usize) {
514        self.notification(ProxyNotification::TerminalResize {
515            term_id,
516            width,
517            height,
518        });
519    }
520
521    pub fn terminal_write(&self, term_id: TermId, content: &str) {
522        self.notification(ProxyNotification::TerminalWrite {
523            term_id,
524            content: content.to_string(),
525        });
526    }
527
528    pub fn new_buffer(
529        &self,
530        buffer_id: BufferId,
531        path: PathBuf,
532        f: impl ProxyCallback + 'static,
533    ) {
534        self.request_async(ProxyRequest::NewBuffer { buffer_id, path }, f);
535    }
536
537    pub fn get_buffer_head(
538        &self,
539        _buffer_id: BufferId,
540        path: PathBuf,
541        f: impl ProxyCallback + 'static,
542    ) {
543        self.request_async(ProxyRequest::BufferHead { path }, f);
544    }
545
546    pub fn create_file(&self, path: PathBuf, f: impl ProxyCallback + 'static) {
547        self.request_async(ProxyRequest::CreateFile { path }, f);
548    }
549
550    pub fn create_directory(&self, path: PathBuf, f: impl ProxyCallback + 'static) {
551        self.request_async(ProxyRequest::CreateDirectory { path }, f);
552    }
553
554    pub fn trash_path(&self, path: PathBuf, f: impl ProxyCallback + 'static) {
555        self.request_async(ProxyRequest::TrashPath { path }, f);
556    }
557
558    pub fn rename_path(
559        &self,
560        from: PathBuf,
561        to: PathBuf,
562        f: impl ProxyCallback + 'static,
563    ) {
564        self.request_async(ProxyRequest::RenamePath { from, to }, f);
565    }
566
567    pub fn save_buffer_as(
568        &self,
569        buffer_id: BufferId,
570        path: PathBuf,
571        rev: u64,
572        content: String,
573        f: impl ProxyCallback + 'static,
574    ) {
575        self.request_async(
576            ProxyRequest::SaveBufferAs {
577                buffer_id,
578                path,
579                rev,
580                content,
581            },
582            f,
583        );
584    }
585
586    pub fn global_search(
587        &self,
588        pattern: String,
589        case_sensitive: bool,
590        f: impl ProxyCallback + 'static,
591    ) {
592        self.request_async(
593            ProxyRequest::GlobalSearch {
594                pattern,
595                case_sensitive,
596            },
597            f,
598        );
599    }
600
601    pub fn save(&self, rev: u64, path: PathBuf, f: impl ProxyCallback + 'static) {
602        self.request_async(ProxyRequest::Save { rev, path }, f);
603    }
604
605    pub fn get_files(&self, f: impl ProxyCallback + 'static) {
606        self.request_async(
607            ProxyRequest::GetFiles {
608                path: "path".into(),
609            },
610            f,
611        );
612    }
613
614    pub fn get_open_files_content(&self) -> Result<ProxyResponse, RpcError> {
615        self.request(ProxyRequest::GetOpenFilesContent {})
616    }
617
618    pub fn read_dir(&self, path: PathBuf, f: impl ProxyCallback + 'static) {
619        self.request_async(ProxyRequest::ReadDir { path }, f);
620    }
621
622    pub fn completion_resolve(
623        &self,
624        plugin_id: PluginId,
625        completion_item: CompletionItem,
626        f: impl ProxyCallback + 'static,
627    ) {
628        self.request_async(
629            ProxyRequest::CompletionResolve {
630                plugin_id,
631                completion_item: Box::new(completion_item),
632            },
633            f,
634        );
635    }
636
637    pub fn code_action_resolve(
638        &self,
639        action_item: CodeAction,
640        plugin_id: PluginId,
641        f: impl ProxyCallback + 'static,
642    ) {
643        self.request_async(
644            ProxyRequest::CodeActionResolve {
645                action_item: Box::new(action_item),
646                plugin_id,
647            },
648            f,
649        );
650    }
651
652    pub fn get_hover(
653        &self,
654        request_id: usize,
655        path: PathBuf,
656        position: Position,
657        f: impl ProxyCallback + 'static,
658    ) {
659        self.request_async(
660            ProxyRequest::GetHover {
661                request_id,
662                path,
663                position,
664            },
665            f,
666        );
667    }
668
669    pub fn get_definition(
670        &self,
671        request_id: usize,
672        path: PathBuf,
673        position: Position,
674        f: impl ProxyCallback + 'static,
675    ) {
676        self.request_async(
677            ProxyRequest::GetDefinition {
678                request_id,
679                path,
680                position,
681            },
682            f,
683        );
684    }
685
686    pub fn get_type_definition(
687        &self,
688        request_id: usize,
689        path: PathBuf,
690        position: Position,
691        f: impl ProxyCallback + 'static,
692    ) {
693        self.request_async(
694            ProxyRequest::GetTypeDefinition {
695                request_id,
696                path,
697                position,
698            },
699            f,
700        );
701    }
702
703    pub fn get_references(
704        &self,
705        path: PathBuf,
706        position: Position,
707        f: impl ProxyCallback + 'static,
708    ) {
709        self.request_async(ProxyRequest::GetReferences { path, position }, f);
710    }
711
712    pub fn get_code_actions(
713        &self,
714        path: PathBuf,
715        position: Position,
716        f: impl ProxyCallback + 'static,
717    ) {
718        self.request_async(ProxyRequest::GetCodeActions { path, position }, f);
719    }
720
721    pub fn get_document_formatting(
722        &self,
723        path: PathBuf,
724        f: impl ProxyCallback + 'static,
725    ) {
726        self.request_async(ProxyRequest::GetDocumentFormatting { path }, f);
727    }
728
729    pub fn get_semantic_tokens(
730        &self,
731        path: PathBuf,
732        f: impl ProxyCallback + 'static,
733    ) {
734        self.request_async(ProxyRequest::GetSemanticTokens { path }, f);
735    }
736
737    pub fn get_document_symbols(
738        &self,
739        path: PathBuf,
740        f: impl ProxyCallback + 'static,
741    ) {
742        self.request_async(ProxyRequest::GetDocumentSymbols { path }, f);
743    }
744
745    pub fn get_workspace_symbols(
746        &self,
747        query: String,
748        f: impl ProxyCallback + 'static,
749    ) {
750        self.request_async(ProxyRequest::GetWorkspaceSymbols { query }, f);
751    }
752
753    pub fn prepare_rename(
754        &self,
755        path: PathBuf,
756        position: Position,
757        f: impl ProxyCallback + 'static,
758    ) {
759        self.request_async(ProxyRequest::PrepareRename { path, position }, f);
760    }
761
762    pub fn git_get_remote_file_url(
763        &self,
764        file: PathBuf,
765        f: impl ProxyCallback + 'static,
766    ) {
767        self.request_async(ProxyRequest::GitGetRemoteFileUrl { file }, f);
768    }
769
770    pub fn rename(
771        &self,
772        path: PathBuf,
773        position: Position,
774        new_name: String,
775        f: impl ProxyCallback + 'static,
776    ) {
777        self.request_async(
778            ProxyRequest::Rename {
779                path,
780                position,
781                new_name,
782            },
783            f,
784        );
785    }
786
787    pub fn get_inlay_hints(&self, path: PathBuf, f: impl ProxyCallback + 'static) {
788        self.request_async(ProxyRequest::GetInlayHints { path }, f);
789    }
790
791    pub fn update(&self, path: PathBuf, delta: RopeDelta, rev: u64) {
792        self.notification(ProxyNotification::Update { path, delta, rev });
793    }
794
795    pub fn update_plugin_configs(
796        &self,
797        configs: HashMap<String, HashMap<String, serde_json::Value>>,
798    ) {
799        self.notification(ProxyNotification::UpdatePluginConfigs { configs });
800    }
801
802    pub fn git_discard_files_changes(&self, files: Vec<PathBuf>) {
803        self.notification(ProxyNotification::GitDiscardFilesChanges { files });
804    }
805
806    pub fn git_discard_workspace_changes(&self) {
807        self.notification(ProxyNotification::GitDiscardWorkspaceChanges {});
808    }
809
810    pub fn get_selection_range(
811        &self,
812        path: PathBuf,
813        positions: Vec<Position>,
814        f: impl ProxyCallback + 'static,
815    ) {
816        self.request_async(ProxyRequest::GetSelectionRange { path, positions }, f);
817    }
818}
819
820impl Default for ProxyRpcHandler {
821    fn default() -> Self {
822        Self::new()
823    }
824}