-
-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Labelled hashtag #69
base: master
Are you sure you want to change the base?
Changes from all commits
9076664
25b0248
0c6d53f
f631e41
ebc50da
073d1a3
bc2d959
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,7 @@ | |
- [Delimited Email addresses: `<[email protected]>`](#delimited-email-addresses) | ||
- [Delimited Links: `<http://example.org>`](#delimited-links) | ||
- [Labeled Links: `[Name](url)`](#labled-links) | ||
- [Labeled hashtags: `[Tag][#tag]`](#labeled-tags) | ||
|
||
## Text Enhancements | ||
|
||
|
@@ -182,6 +183,13 @@ Optionally, a client can implement a system to trust a domain (a "don't ask agai | |
|
||
URL parsing allows all valid URLs, no restrictions on schemes, no whitelist is needed, because the format already specifies that it is a link. | ||
|
||
|
||
<a name="labeled-tags" /> | ||
|
||
### Labelled hashtags | ||
|
||
The idea is to have hashtags but labelled with an alternative text. This feature is very unique and less seen in other IMs. | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please also make an example and spec to define how it looks like:
and say what it does: "clicking on it opens the search with that hashtag like for the normal hashtag but looks like a link", or sth along those lines |
||
## Ideas For The Future: | ||
|
||
### `:emoji:` | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,12 +9,12 @@ use nom::{ | |
streaming::take_till1, | ||
}, | ||
character::complete::char, | ||
combinator::{peek, recognize, verify}, | ||
sequence::tuple, | ||
combinator::{consumed, peek, recognize, verify}, | ||
sequence::{delimited, tuple}, | ||
AsChar, IResult, Offset, Slice, | ||
}; | ||
|
||
use super::base_parsers::CustomError; | ||
use super::{base_parsers::CustomError, parse_only_text}; | ||
|
||
fn linebreak(input: &str) -> IResult<&str, char, CustomError<&str>> { | ||
char('\n')(input) | ||
|
@@ -89,143 +89,6 @@ pub(crate) fn email_address(input: &str) -> IResult<&str, Element, CustomError<& | |
} | ||
} | ||
|
||
/* | ||
fn not_link_part_char(c: char) -> bool { | ||
!matches!(c, ':' | '\n' | '\r' | '\t' | ' ') | ||
} | ||
|
||
fn link(input: &str) -> IResult<&str, (), CustomError<&str>> { | ||
let (input, _) = take_while1(link_scheme)(input)?; | ||
} | ||
|
||
/// rough recognition of an link, results gets checked by a real link parser | ||
fn link_intern(input: &str) -> IResult<&str, (), CustomError<&str>> { | ||
let (input, _) = take_while1(not_link_part_char)(input)?; | ||
let (input, _) = tag(":")(input)?; | ||
let i = <&str>::clone(&input); | ||
let (remaining, consumed) = take_while1(is_not_white_space)(i)?; | ||
|
||
let mut parentheses_count = 0usize; // () | ||
let mut curly_brackets_count = 0usize; // {} | ||
let mut brackets_count = 0usize; // [] | ||
let mut angle_brackets = 0usize; // <> | ||
|
||
let mut alternative_offset = None; | ||
for (i, char) in consumed.chars().enumerate() { | ||
match char { | ||
'(' => { | ||
parentheses_count = parentheses_count.saturating_add(1); | ||
// if there is no closing bracket in the link, then don't take the bracket as a part of the link | ||
if (<&str>::clone(&consumed)).slice(i..).find(')').is_none() { | ||
alternative_offset = Some(i); | ||
break; | ||
} | ||
} | ||
'{' => { | ||
curly_brackets_count = curly_brackets_count.saturating_add(1); | ||
// if there is no closing bracket in the link, then don't take the bracket as a part of the link | ||
if (<&str>::clone(&consumed)).slice(i..).find('}').is_none() { | ||
alternative_offset = Some(i); | ||
break; | ||
} | ||
} | ||
'[' => { | ||
brackets_count = brackets_count.saturating_add(1); | ||
// if there is no closing bracket in the link, then don't take the bracket as a part of the link | ||
if (<&str>::clone(&consumed)).slice(i..).find(']').is_none() { | ||
alternative_offset = Some(i); | ||
break; | ||
} | ||
} | ||
'<' => { | ||
angle_brackets = angle_brackets.saturating_add(1); | ||
// if there is no closing bracket in the link, then don't take the bracket as a part of the link | ||
if (<&str>::clone(&consumed)).slice(i..).find('>').is_none() { | ||
alternative_offset = Some(i); | ||
break; | ||
} | ||
} | ||
')' => { | ||
if parentheses_count == 0 { | ||
alternative_offset = Some(i); | ||
break; | ||
} else { | ||
parentheses_count = parentheses_count.saturating_sub(1); | ||
} | ||
} | ||
'}' => { | ||
if curly_brackets_count == 0 { | ||
alternative_offset = Some(i); | ||
break; | ||
} else { | ||
curly_brackets_count = curly_brackets_count.saturating_sub(1); | ||
} | ||
} | ||
']' => { | ||
if brackets_count == 0 { | ||
alternative_offset = Some(i); | ||
break; | ||
} else { | ||
brackets_count = brackets_count.saturating_sub(1); | ||
} | ||
} | ||
'>' => { | ||
if angle_brackets == 0 { | ||
alternative_offset = Some(i); | ||
break; | ||
} else { | ||
angle_brackets = angle_brackets.saturating_sub(1); | ||
} | ||
} | ||
_ => continue, | ||
} | ||
} | ||
|
||
if let Some(offset) = alternative_offset { | ||
let remaining = input.slice(offset..); | ||
Ok((remaining, ())) | ||
} else { | ||
Ok((remaining, ())) | ||
} | ||
} | ||
|
||
pub(crate) fn link(input: &str) -> IResult<&str, Element, CustomError<&str>> { | ||
// basically | ||
//let (input, content) = recognize(link_intern)(input)?; | ||
// but don't eat the last char if it is one of these: `.,;:` | ||
let i = <&str>::clone(&input); | ||
let i2 = <&str>::clone(&input); | ||
let i3 = <&str>::clone(&input); | ||
let (input, content) = match link_intern(i) { | ||
Ok((remaining, _)) => { | ||
let index = i2.offset(remaining); | ||
let consumed = i2.slice(..index); | ||
match consumed.chars().last() { | ||
Some(c) => match c { | ||
'.' | ',' | ':' | ';' => { | ||
let index = input.offset(remaining).saturating_sub(1); | ||
let consumed = i3.slice(..index); | ||
let remaining = input.slice(index..); | ||
Ok((remaining, consumed)) | ||
} | ||
_ => Ok((remaining, consumed)), | ||
}, | ||
_ => Ok((remaining, consumed)), | ||
} | ||
} | ||
Err(e) => Err(e), | ||
}?; | ||
|
||
// check if result is valid link | ||
let (remainder, destination) = LinkDestination::parse_standalone_with_whitelist(content)?; | ||
|
||
if remainder.is_empty() { | ||
Ok((input, Element::Link { destination })) | ||
} else { | ||
Err(nom::Err::Error(CustomError::InvalidLink)) | ||
} | ||
} | ||
*/ | ||
fn is_allowed_bot_cmd_suggestion_char(char: char) -> bool { | ||
match char { | ||
'@' | '\\' | '_' | '.' | '-' | '/' => true, | ||
|
@@ -253,6 +116,32 @@ fn bot_command_suggestion(input: &str) -> IResult<&str, Element, CustomError<&st | |
} | ||
} | ||
|
||
fn labelled_tag(input: &str) -> IResult<&str, Element, CustomError<&str>> { | ||
let (input, label) = delimited( | ||
char('['), | ||
take_while1(|c| !matches!(c, '[' | ']')), | ||
char(']'), | ||
)(input)?; | ||
let elements: Vec<Element> = parse_only_text(label); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
let (input, tag) = delimited( | ||
char('('), | ||
take_while1(|c| !matches!(c, '(' | ')')), | ||
char(')'), | ||
)(input)?; | ||
let (_, (consumed, _output)) = consumed(hashtag)(tag)?; | ||
if consumed == tag { | ||
Ok(( | ||
input, | ||
Element::LabelledTag { | ||
label: elements, | ||
tag: consumed, | ||
}, | ||
)) | ||
} else { | ||
Err(nom::Err::Error(CustomError::UnexpectedContent)) | ||
} | ||
} | ||
|
||
pub(crate) fn parse_text_element( | ||
input: &str, | ||
prev_char: Option<char>, | ||
|
@@ -263,8 +152,12 @@ pub(crate) fn parse_text_element( | |
// | ||
// Also as this is the text element parser, | ||
// text elements parsers MUST NOT call the parser for markdown elements internally | ||
|
||
if let Ok((i, elm)) = hashtag(input) { | ||
{ | ||
//println!("{:?}", labelled_tag(input)); | ||
} | ||
if let Ok((i, elm)) = labelled_tag(input) { | ||
Ok((i, elm)) | ||
Comment on lines
+158
to
+159
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the fabled tag should only be in the markdown set and maybe also in the desktop set, but not in the text set. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You sure? hashtag is already in text elements. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, labeled link and labeled hashtag both modify the text, while text elements only make the text clickable |
||
} else if let Ok((i, elm)) = hashtag(input) { | ||
Ok((i, elm)) | ||
} else if let Ok((i, elm)) = { | ||
if prev_char == Some(' ') || prev_char.is_none() { | ||
|
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we also want to have this in the desktop set?