Skip to content

Commit

Permalink
feat: support schema selection
Browse files Browse the repository at this point in the history
- feat: support schema selection with customized trigger character
- refactor(lsp): add some scopes for completion function to drop value
  earlier
  • Loading branch information
wlh320 committed Feb 27, 2023
1 parent ed165d4 commit b9b9b4a
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 18 deletions.
16 changes: 15 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ pub struct Config {
/// if not empty, only trigger completion with special keys
#[serde(default = "default_trigger_characters")]
pub trigger_characters: Vec<String>,
/// if set, completion request with this string will trigger「方案選單」
#[serde(default = "default_schema_trigger_character")]
pub schema_trigger_character: String,
}

/// settings that can be tweaked during running
Expand All @@ -34,6 +37,8 @@ pub struct Settings {
pub max_candidates: Option<usize>,
/// if not empty, only trigger completion with special keys
pub trigger_characters: Option<Vec<String>>,
/// if set, completion request with this string will trigger「方案選單」
pub schema_trigger_character: Option<String>,
}

impl Default for Config {
Expand All @@ -45,6 +50,7 @@ impl Default for Config {
log_dir: default_log_dir(),
max_candidates: default_max_candidates(),
trigger_characters: default_trigger_characters(),
schema_trigger_character: default_schema_trigger_character(),
}
}
}
Expand All @@ -58,7 +64,7 @@ fn default_max_candidates() -> usize {
}

fn default_trigger_characters() -> Vec<String> {
vec![]
Vec::default()
}

fn default_shared_data_dir() -> PathBuf {
Expand All @@ -75,6 +81,10 @@ fn default_log_dir() -> PathBuf {
proj_dirs.cache_dir().to_path_buf()
}

fn default_schema_trigger_character() -> String {
String::default()
}

#[test]
fn test_default_config() {
let config: Config = Default::default();
Expand All @@ -84,4 +94,8 @@ fn test_default_config() {
assert_eq!(config.log_dir, default_log_dir());
assert_eq!(config.max_candidates, default_max_candidates());
assert_eq!(config.trigger_characters, default_trigger_characters());
assert_eq!(
config.schema_trigger_character,
default_schema_trigger_character()
);
}
4 changes: 3 additions & 1 deletion src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ pub static RAW_RE: Lazy<Regex> = Lazy::new(|| Regex::new(RAW_PTN).unwrap());
pub static AUTO_TRIGGER_RE: Lazy<Regex> = Lazy::new(|| Regex::new(AUTO_TRIGGER_PTN).unwrap());

// keycodes
pub const K_BACKSPACE: i32 = 0xff08;
// note: run `xmodmap -pk` in shell
pub const KEY_BACKSPACE: i32 = 0xff08;
pub const KEY_F4: i32 = 0xffc1;
30 changes: 27 additions & 3 deletions src/input.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::consts::KEY_F4;
use crate::rime::Rime;
use crate::utils::{diff, DiffResult};
use ouroboros::self_referencing;
Expand Down Expand Up @@ -78,13 +79,32 @@ impl InputState {
}
}

pub fn handle_new_input(&self, new_offset: usize, new_input: &Input) -> InputResult {
fn handle_schema(session_id: usize) -> InputResult {
let rime = Rime::global();
rime.process_key(session_id, KEY_F4);
let raw_input = rime.get_raw_input(session_id);
InputResult {
session_id,
raw_input,
}
}

pub fn handle_new_input(
&self,
new_offset: usize,
new_input: &Input,
schema_trigger: &str,
) -> InputResult {
let rime = Rime::global();
let session_id = rime.find_session(self.session_id);
// new typing
if self.offset != new_offset || self.session_id != session_id {
rime.clear_composition(session_id);
return Self::handle_new_typing(session_id, new_input);
if !schema_trigger.is_empty() && new_input.borrow_pinyin() == &schema_trigger {
return Self::handle_schema(session_id);
} else {
return Self::handle_new_typing(session_id, new_input);
}
}
// continue last typing
// handle pinyin
Expand All @@ -94,7 +114,11 @@ impl InputState {
DiffResult::Delete(suffix) => rime.delete_keys(session_id, suffix.len()),
DiffResult::New => {
rime.clear_composition(session_id);
rime.process_str(session_id, new_input.borrow_pinyin());
if !schema_trigger.is_empty() && new_input.borrow_pinyin() == &schema_trigger {
rime.process_key(session_id, KEY_F4);
} else {
rime.process_str(session_id, new_input.borrow_pinyin());
}
}
_ => (),
}
Expand Down
31 changes: 20 additions & 11 deletions src/lsp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ impl Backend {
self.compile_regex(&v).await;
config.trigger_characters = v;
}
if let Some(v) = settings.schema_trigger_character {
config.schema_trigger_character = v;
}
}

async fn create_work_done_progress(&self, token: NumberOrString) -> Result<NumberOrString> {
Expand Down Expand Up @@ -137,17 +140,16 @@ impl Backend {
}

async fn get_completions(&self, uri: Url, position: Position) -> Option<Vec<CompletionItem>> {
let max_candidates = self.config.read().await.max_candidates;
let is_trigger_set = !self.config.read().await.trigger_characters.is_empty();
let rime = Rime::global();

// get new input
let rope = self.documents.get(&uri.to_string())?;
let line_pos = Position::new(position.line, 0);
let line_begin = utils::position_to_offset(&rope, line_pos)?;
let line_begin = {
let line_pos = Position::new(position.line, 0);
utils::position_to_offset(&rope, line_pos)?
};
let curr_char = utils::position_to_offset(&rope, position)?;
let new_input = {
let re = self.regex.read().await;
let is_trigger_set = !self.config.read().await.trigger_characters.is_empty();
(curr_char <= rope.len_chars()).then(|| {
let slice = rope.slice(line_begin..curr_char).as_str()?;
if utils::need_to_check_trigger(is_trigger_set, slice) {
Expand All @@ -165,11 +167,15 @@ impl Backend {
session_id,
raw_input,
} = match (*last_state).as_ref() {
Some(state) => state.handle_new_input(new_offset, &new_input),
Some(state) => {
let schema_trigger = &self.config.read().await.schema_trigger_character;
state.handle_new_input(new_offset, &new_input, schema_trigger)
}
None => InputState::handle_first_state(&new_input),
};

// get candidates from current session
let rime = Rime::global();
let RimeResponse {
submitted,
candidates,
Expand All @@ -190,8 +196,10 @@ impl Backend {
// candidates to completions
let range = Range::new(utils::offset_to_position(&rope, real_offset)?, position);
let filter_text = new_input.borrow_raw_text().to_string();
let order_to_sort_text = utils::build_order_to_sort_text(max_candidates);

let order_to_sort_text = {
let max_candidates = self.config.read().await.max_candidates;
utils::build_order_to_sort_text(max_candidates)
};
let candidate_to_completion_item = move |c: Candidate| -> CompletionItem {
CompletionItem {
label: format!("{}. {}{}", c.order, &submitted, &c.text),
Expand All @@ -206,11 +214,12 @@ impl Backend {
..Default::default()
}
};

// update input state
*last_state = Some(InputState::new(new_input, session_id, new_offset));
// return completions
let cand_iter = candidates.into_iter();
Some(cand_iter.map(candidate_to_completion_item).collect())
let item_iter = candidates.into_iter().map(candidate_to_completion_item);
Some(item_iter.collect())
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/rime.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::consts::{K_BACKSPACE, RAW_RE};
use crate::consts::{KEY_BACKSPACE, RAW_RE};
use librime_sys as librime;
use once_cell::sync::OnceCell;
use std::ffi::{CStr, CString, NulError};
Expand Down Expand Up @@ -258,7 +258,7 @@ impl Rime {

pub fn delete_keys(&self, session_id: usize, len: usize) {
for _ in 0..len {
self.process_key(session_id, K_BACKSPACE);
self.process_key(session_id, KEY_BACKSPACE);
}
}

Expand Down

0 comments on commit b9b9b4a

Please sign in to comment.