Skip to content

Commit

Permalink
Configuration option to replace :shortcode: with the corresponding …
Browse files Browse the repository at this point in the history
…the emoji.
  • Loading branch information
andymandias committed Feb 13, 2025
1 parent 440523d commit becc1e3
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 17 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Added:

- Emoji picker accessible via `:` in text input. See [configuration](https://halloy.squidowl.org/configuration/buffer.html#bufferemojis)
- Emoji picker accessible via `:` in text input and emoji auto-replace for `:shortcode:`. See [configuration](https://halloy.squidowl.org/configuration/buffer.html#bufferemojis)
- Added an option to show or hide images in preview cards by default

Fixed:
Expand Down
14 changes: 14 additions & 0 deletions book/src/configuration/buffer.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ Emojis settings.
[buffer.emojis]
show_picker = true
skin_tone = "default"
auto_replace = true
```

### `show_picker`
Expand Down Expand Up @@ -274,6 +275,19 @@ Skin tone selected when picking an emoji.
skin_tone = "default"
```

### `auto_replace`

Automatically replace `:shortcode:` in text input with the corresponding emoji.

```toml
# Type: boolean
# Values: true, false
# Default: true

[buffer.emojis]
auto_replace = true
```

## `[buffer.internal_messages]`

Internal messages are messages sent from Halloy itself.
Expand Down
3 changes: 3 additions & 0 deletions data/src/config/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,16 @@ pub struct Emojis {
pub show_picker: bool,
#[serde(default)]
pub skin_tone: SkinTone,
#[serde(default = "default_bool_true")]
pub auto_replace: bool,
}

impl Default for Emojis {
fn default() -> Self {
Self {
show_picker: default_bool_true(),
skin_tone: Default::default(),
auto_replace: default_bool_true(),
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/buffer/input_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ impl State {
self.completion
.process(&input, users, channels, &isupport, config);

let input = self.completion.complete_emoji(&input).unwrap_or(input);

history.record_draft(Draft {
buffer: buffer.clone(),
text: input,
Expand Down
60 changes: 44 additions & 16 deletions src/buffer/input_view/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,8 @@ impl Completion {
}

self.emojis = Emojis::default();
} else if let Some(shortcode) = config
.buffer
.emojis
.show_picker
} else if let Some(shortcode) = (config.buffer.emojis.show_picker
|| config.buffer.emojis.auto_replace)
.then(|| {
input
.split(' ')
Expand All @@ -71,7 +69,7 @@ impl Completion {
})
.flatten()
{
self.emojis.process(shortcode);
self.emojis.process(shortcode, config);

self.commands = Commands::default();
self.text = Text::default();
Expand All @@ -92,6 +90,14 @@ impl Completion {
.or(self.emojis.select(config).map(Entry::Emoji))
}

pub fn complete_emoji(&self, input: &str) -> Option<String> {
if let Emojis::Selected { emoji } = self.emojis {
Some(replace_last_word_with_emoji(input, emoji))
} else {
None
}
}

pub fn tab(&mut self, reverse: bool) -> Option<Entry> {
if self.commands.tab(reverse) {
return None;
Expand Down Expand Up @@ -195,15 +201,7 @@ impl Entry {

new_input
}
Entry::Emoji(emoji) => {
let mut words: Vec<_> = input.split(' ').collect();

if let Some(last_word) = words.last_mut() {
*last_word = emoji;
}

words.join(" ")
}
Entry::Emoji(emoji) => replace_last_word_with_emoji(input, emoji),
}
}
}
Expand Down Expand Up @@ -1743,6 +1741,9 @@ enum Emojis {
highlighted: Option<usize>,
filtered: Vec<&'static str>,
},
Selected {
emoji: &'static str,
},
}

impl Default for Emojis {
Expand All @@ -1752,14 +1753,31 @@ impl Default for Emojis {
}

impl Emojis {
fn process(&mut self, last_word: &str) {
fn process(&mut self, last_word: &str, config: &Config) {
let last_word = last_word.strip_prefix(":").unwrap_or("");

if last_word.is_empty() {
*self = Self::default();
return;
}

if let Some(shortcode) = config
.buffer
.emojis
.auto_replace
.then(|| last_word.strip_suffix(":"))
.flatten()
{
if let Some(emoji) = pick_emoji(shortcode, config.buffer.emojis.skin_tone) {
*self = Emojis::Selected { emoji };

return;
}
} else if !config.buffer.emojis.show_picker {
*self = Self::default();
return;
}

let mut filtered = emojis::iter()
.flat_map(|emoji| {
emoji.shortcodes().filter_map(|shortcode| {
Expand Down Expand Up @@ -1816,7 +1834,7 @@ impl Emojis {

fn view<'a, Message: 'a>(&self, config: &Config) -> Option<Element<'a, Message>> {
match self {
Self::Idle => None,
Self::Idle | Self::Selected { .. } => None,
Self::Selecting {
highlighted,
filtered,
Expand Down Expand Up @@ -1894,6 +1912,16 @@ fn pick_emoji(shortcode: &str, skin_tone: SkinTone) -> Option<&'static str> {
})
}

fn replace_last_word_with_emoji(input: &str, emoji: &str) -> String {
let mut words: Vec<_> = input.split(' ').collect();

if let Some(last_word) = words.last_mut() {
*last_word = emoji;
}

words.join(" ")
}

fn selecting_tab<T>(highlighted: &mut Option<usize>, filtered: &[T], reverse: bool) {
if filtered.is_empty() {
*highlighted = None;
Expand Down

0 comments on commit becc1e3

Please sign in to comment.