Skip to content

Commit d15dee5

Browse files
committed
refractor
1 parent fc5288a commit d15dee5

File tree

9 files changed

+452
-429
lines changed

9 files changed

+452
-429
lines changed

README.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
1-
# BaseCode LSP
1+
# basecode-lsp
22

3-
BaseCode LSP is a versatile Language Server Protocol (LSP) implementation designed to provide snippet completion, text suggestions,
3+
basecode-lsp is a versatile Language Server Protocol (LSP) implementation designed to provide snippet completion, text suggestions,
44
and keyword recognition across multiple programming languages. It aims to enhance the development experience by offering seamless integration with various editors and IDEs.
55

66
## Features
77

88
- [x] word completion
99
- [x] snippet completion
1010
- [x] file path completion
11-
- [ ] spellcheck
12-
- [ ] tmux
11+
- [x] tmux
1312
- [ ] cmdline
1413

1514
## Installation
1615

17-
To install BaseCode LSP, follow these steps:
16+
To install basecode-lsp, follow these steps:
1817

1918
1. **Clone the Repository**:
2019

src/basecode_lsp/backend.rs

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
use super::file::*;
2+
use super::snippet::*;
3+
use super::tmux::*;
4+
use super::trie::*;
5+
use super::util::*;
6+
7+
use clap::Parser;
8+
use hashbrown::HashMap;
9+
use simple_log::*;
10+
use tokio::sync::Mutex;
11+
use tower_lsp::jsonrpc::Result;
12+
use tower_lsp::lsp_types::*;
13+
use tower_lsp::*;
14+
15+
#[derive(Parser, Debug)]
16+
#[command(version, about, long_about = None)]
17+
pub struct LspArgs {
18+
#[arg(long)]
19+
snippet_folder: Option<String>,
20+
#[arg(long)]
21+
root_folder: Option<String>,
22+
#[arg(long, default_value_t = 2)]
23+
min_word_len: usize,
24+
#[arg(long, default_value_t = true)]
25+
tmux_source: bool,
26+
#[arg(long)]
27+
pub debug: bool,
28+
}
29+
30+
#[derive(Debug)]
31+
pub struct Backend {
32+
documents: Mutex<HashMap<String, String>>,
33+
snippets: Mutex<HashMap<String, Vec<Snippet>>>,
34+
trie: Mutex<Trie>,
35+
tmux_source: Mutex<Vec<String>>,
36+
lsp_args: LspArgs,
37+
}
38+
39+
#[tower_lsp::async_trait]
40+
impl LanguageServer for Backend {
41+
async fn initialize(&self, _: InitializeParams) -> Result<InitializeResult> {
42+
if let Some(snippet_folder) = self.lsp_args.snippet_folder.clone() {
43+
info!("loading snippet folder: {}", snippet_folder);
44+
let mut snippets_lock = self.snippets.lock().await;
45+
prepare_snippet(snippet_folder, &mut snippets_lock);
46+
}
47+
48+
let trigger_characters = Some(vec!["/".to_string(), "\"".to_string(), "'".to_string()]);
49+
Ok(InitializeResult {
50+
capabilities: ServerCapabilities {
51+
text_document_sync: Some(TextDocumentSyncCapability::Kind(
52+
TextDocumentSyncKind::FULL,
53+
)),
54+
completion_provider: Some(CompletionOptions {
55+
resolve_provider: Some(false),
56+
trigger_characters,
57+
..CompletionOptions::default()
58+
}),
59+
..ServerCapabilities::default()
60+
},
61+
..InitializeResult::default()
62+
})
63+
}
64+
65+
async fn shutdown(&self) -> Result<()> {
66+
info!("shutdown basecode-lsp");
67+
Ok(())
68+
}
69+
70+
async fn did_open(&self, params: DidOpenTextDocumentParams) {
71+
let mut document_lock = self.documents.lock().await;
72+
document_lock.insert(
73+
params.text_document.uri.to_string(),
74+
params.text_document.text.clone(),
75+
);
76+
77+
self.add_words(params.text_document.text.clone()).await;
78+
self.maybe_update_tmux().await;
79+
}
80+
81+
async fn did_close(&self, params: DidCloseTextDocumentParams) {
82+
let mut document_lock = self.documents.lock().await;
83+
84+
let uri = params.text_document.uri.to_string();
85+
if let Some(content) = document_lock.get(&uri) {
86+
self.remove_words(content.clone()).await;
87+
}
88+
document_lock.remove(&uri);
89+
self.maybe_update_tmux().await;
90+
}
91+
92+
async fn did_change(&self, params: DidChangeTextDocumentParams) {
93+
let mut document_lock = self.documents.lock().await;
94+
95+
let uri = params.text_document.uri.to_string();
96+
if let Some(content) = document_lock.get_mut(&uri) {
97+
self.remove_words(content.clone()).await;
98+
if let Some(last_change) = params.content_changes.last() {
99+
*content = last_change.text.clone();
100+
}
101+
}
102+
for content_change in params.content_changes.iter() {
103+
self.add_words(content_change.text.clone()).await;
104+
}
105+
self.maybe_update_tmux().await;
106+
}
107+
108+
async fn completion(&self, params: CompletionParams) -> Result<Option<CompletionResponse>> {
109+
let text_document_position = params.text_document_position.clone();
110+
let position = text_document_position.position;
111+
112+
let mut completions = Vec::new();
113+
if let Some(current_line) = self.get_current_line(&params).await {
114+
let prefix = get_word_prefix(&current_line, position.character as i32);
115+
116+
let trie_lock = self.trie.lock().await;
117+
let words = trie_lock.suggest_completions(&prefix);
118+
let mut all_words = words;
119+
120+
let tmux_words = self.prepare_tmux_words().await;
121+
all_words.extend(tmux_words);
122+
123+
let suffixes = get_possible_current_word(&current_line, position.character as i32);
124+
all_words.sort();
125+
all_words.dedup();
126+
words_to_completion_items(all_words, &suffixes, &mut completions);
127+
128+
let file_uri = params.text_document_position.text_document.uri.to_string();
129+
let snippets = self.suggest_snippets(&file_uri, &prefix).await;
130+
snippets_to_completion_items(snippets, &mut completions);
131+
132+
if let Some(root_folder) = self.lsp_args.root_folder.clone() {
133+
let file_items = get_file_items(&current_line, &root_folder);
134+
file_items_to_completion_items(file_items, &params, &mut completions);
135+
}
136+
}
137+
Ok(Some(CompletionResponse::Array(completions)))
138+
}
139+
}
140+
141+
impl Backend {
142+
pub fn new(lsp_args: LspArgs) -> Self {
143+
return Self {
144+
documents: Mutex::new(HashMap::new()),
145+
snippets: Mutex::new(HashMap::new()),
146+
trie: Mutex::new(Trie::new()),
147+
tmux_source: Mutex::new(Vec::new()),
148+
lsp_args,
149+
};
150+
}
151+
152+
async fn add_words(&self, content: String) {
153+
let mut trie_lock = self.trie.lock().await;
154+
for token in content.split_whitespace() {
155+
let words = process_token(token, self.lsp_args.min_word_len);
156+
for w in words {
157+
trie_lock.insert(&w);
158+
}
159+
}
160+
}
161+
162+
async fn remove_words(&self, content: String) {
163+
let mut trie_lock = self.trie.lock().await;
164+
for token in content.split_whitespace() {
165+
let words = process_token(token, self.lsp_args.min_word_len);
166+
for w in words {
167+
trie_lock.remove(&w);
168+
}
169+
}
170+
}
171+
172+
async fn get_current_line(&self, params: &CompletionParams) -> Option<String> {
173+
let text_document_position = params.text_document_position.clone();
174+
let uri = text_document_position.text_document.uri.to_string();
175+
let document_lock = self.documents.lock().await;
176+
let position = text_document_position.position;
177+
if let Some(content) = document_lock.get(&uri) {
178+
let current_line: Option<&str> = content.split("\n").nth(position.line as usize);
179+
if let Some(line) = current_line {
180+
return Some(line.to_string());
181+
}
182+
}
183+
None
184+
}
185+
186+
async fn suggest_snippets(&self, file_uri: &str, prefix: &str) -> Vec<Snippet> {
187+
let snippet_lock = self.snippets.lock().await;
188+
let snippet_names = get_snippet_names(file_uri);
189+
let mut result = Vec::new();
190+
for &snippet_name in snippet_names.iter() {
191+
if let Some(snippets) = snippet_lock.get(snippet_name) {
192+
for snippet in snippets.iter() {
193+
if snippet.name.contains(prefix) {
194+
result.push(snippet.clone());
195+
}
196+
}
197+
}
198+
}
199+
result
200+
}
201+
202+
async fn maybe_update_tmux(&self) {
203+
if self.lsp_args.tmux_source {
204+
let tmux_content = retrieve_tmux_words();
205+
let mut data = self.tmux_source.lock().await;
206+
data.clear();
207+
data.extend(tmux_content);
208+
}
209+
}
210+
211+
async fn prepare_tmux_words(&self) -> Vec<String> {
212+
let data = self.tmux_source.lock().await;
213+
let mut output = Vec::new();
214+
for word in data.iter() {
215+
output.push(word.clone());
216+
}
217+
output
218+
}
219+
}

src/file.rs renamed to src/basecode_lsp/file.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,27 +26,30 @@ pub fn get_file_items(current_line: &str, root_folder: &str) -> Vec<(String, usi
2626
if current_line.len() > MAX_LINE_LENGTH {
2727
return Vec::new();
2828
}
29-
29+
3030
let indices: Vec<usize> = current_line.char_indices().map(|(i, _)| i).collect();
3131
let mut file_items = Vec::new();
32-
for (j, _) in current_line.char_indices().filter(|&(_, ch)| ch == '/' || ch == '\\') {
32+
for (j, _) in current_line
33+
.char_indices()
34+
.filter(|&(_, ch)| ch == '/' || ch == '\\')
35+
{
3336
for &i in indices.iter() {
3437
if i > j {
3538
continue;
3639
}
37-
let p = &current_line[i..j+1];
38-
40+
let p = &current_line[i..j + 1];
41+
3942
for base in [root_folder, ""].iter().map(PathBuf::from) {
4043
let path = base.join(p);
4144
file_items.extend(
4245
list_all_file_items(&path)
4346
.into_iter()
44-
.map(|file_path| (file_path, j))
47+
.map(|file_path| (file_path, j)),
4548
);
4649
}
4750
}
4851
}
49-
52+
5053
file_items.sort();
5154
file_items.dedup();
5255
file_items

src/basecode_lsp/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pub mod backend;
2+
pub mod file;
3+
pub mod snippet;
4+
pub mod tmux;
5+
pub mod trie;
6+
pub mod util;

src/snippets.rs renamed to src/basecode_lsp/snippet.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use glob::glob;
2+
use hashbrown::HashMap;
23
use simple_log::error;
3-
use std::collections::HashMap;
44
use std::fs;
55
use std::path::Path;
66
use std::sync::OnceLock;

src/tmux.rs renamed to src/basecode_lsp/tmux.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,9 @@ pub fn retrieve_tmux_words() -> Vec<String> {
6666
}
6767
}
6868

69-
result = result.into_iter()
70-
.filter(|s| s.len() >= 3 && !s.chars().all(|c| c.is_numeric())) // Filters out numbers and strings shorter than 3 characters
69+
result = result
70+
.into_iter()
71+
.filter(|s| s.len() >= 3 && !s.chars().all(|c| c.is_numeric())) // Filters out numbers and strings shorter than 3 characters
7172
.collect();
7273
result.sort();
7374
result.dedup();

src/trie.rs renamed to src/basecode_lsp/trie.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use std::cmp::max;
21
use hashbrown::HashMap;
2+
use std::cmp::max;
33

44
#[derive(Debug, Default)]
55
pub struct TrieNode {

0 commit comments

Comments
 (0)