Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
A
AloqaIM-Android
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
AloqaIM-Android
Commits
c0f72f28
Commit
c0f72f28
authored
Sep 24, 2018
by
Leonardo Aramaki
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix markdown parsing for mentions, emphasis and strong emphasis
parent
cb57e76e
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
108 additions
and
24 deletions
+108
-24
StrikethroughDelimiterProcessor.kt
...et/android/chatroom/ui/StrikethroughDelimiterProcessor.kt
+45
-0
MessageParser.kt
...src/main/java/chat/rocket/android/helper/MessageParser.kt
+62
-23
dependencies.gradle
dependencies.gradle
+1
-1
No files found.
app/src/main/java/chat/rocket/android/chatroom/ui/StrikethroughDelimiterProcessor.kt
0 → 100644
View file @
c0f72f28
package
chat.rocket.android.chatroom.ui
import
org.commonmark.ext.gfm.strikethrough.Strikethrough
import
org.commonmark.node.Node
import
org.commonmark.node.Text
import
org.commonmark.parser.delimiter.DelimiterProcessor
import
org.commonmark.parser.delimiter.DelimiterRun
class
StrikethroughDelimiterProcessor
:
DelimiterProcessor
{
override
fun
getOpeningCharacter
():
Char
{
return
'~'
}
override
fun
getClosingCharacter
():
Char
{
return
'~'
}
override
fun
getMinLength
():
Int
{
return
1
}
override
fun
getDelimiterUse
(
opener
:
DelimiterRun
,
closer
:
DelimiterRun
):
Int
{
return
if
(
opener
.
length
()
>=
2
&&
closer
.
length
()
>=
2
)
{
// Use exactly two delimiters even if we have more, and don't care about internal openers/closers.
2
}
else
{
1
}
}
override
fun
process
(
opener
:
Text
,
closer
:
Text
,
delimiterCount
:
Int
)
{
// Wrap nodes between delimiters in strikethrough.
val
strikethrough
=
Strikethrough
()
var
tmp
:
Node
?
=
opener
.
next
while
(
tmp
!=
null
&&
tmp
!==
closer
)
{
val
next
=
tmp
.
next
strikethrough
.
appendChild
(
tmp
)
tmp
=
next
}
opener
.
insertAfter
(
strikethrough
)
}
}
app/src/main/java/chat/rocket/android/helper/MessageParser.kt
View file @
c0f72f28
...
...
@@ -5,6 +5,7 @@ import android.content.Context
import
android.graphics.Canvas
import
android.graphics.Paint
import
android.graphics.RectF
import
android.text.Spannable
import
android.text.Spanned
import
android.text.style.ClickableSpan
import
android.text.style.ImageSpan
...
...
@@ -13,6 +14,7 @@ import android.util.Patterns
import
android.view.View
import
androidx.core.content.res.ResourcesCompat
import
chat.rocket.android.R
import
chat.rocket.android.chatroom.ui.StrikethroughDelimiterProcessor
import
chat.rocket.android.emoji.EmojiParser
import
chat.rocket.android.emoji.EmojiRepository
import
chat.rocket.android.emoji.EmojiTypefaceSpan
...
...
@@ -21,16 +23,23 @@ import chat.rocket.android.server.domain.useRealName
import
chat.rocket.android.util.extensions.openTabbedUrl
import
chat.rocket.common.model.SimpleUser
import
chat.rocket.core.model.Message
import
org.commonmark.Extension
import
org.commonmark.ext.gfm.strikethrough.StrikethroughExtension
import
org.commonmark.ext.gfm.tables.TablesExtension
import
org.commonmark.node.AbstractVisitor
import
org.commonmark.node.Document
import
org.commonmark.node.Emphasis
import
org.commonmark.node.ListItem
import
org.commonmark.node.Node
import
org.commonmark.node.OrderedList
import
org.commonmark.node.StrongEmphasis
import
org.commonmark.node.Text
import
ru.noties.markwon.Markwon
import
org.commonmark.parser.Parser
import
ru.noties.markwon.SpannableBuilder
import
ru.noties.markwon.SpannableConfiguration
import
ru.noties.markwon.renderer.SpannableMarkdownVisitor
import
ru.noties.markwon.tasklist.TaskListExtension
import
java.util.*
import
javax.inject.Inject
class
MessageParser
@Inject
constructor
(
...
...
@@ -39,8 +48,6 @@ class MessageParser @Inject constructor(
private
val
settings
:
PublicSettings
)
{
private
val
parser
=
Markwon
.
createParser
()
/**
* Render markdown and other rules on message to rich text with spans.
*
...
...
@@ -52,6 +59,16 @@ class MessageParser @Inject constructor(
fun
render
(
message
:
Message
,
selfUsername
:
String
?
=
null
):
CharSequence
{
var
text
:
String
=
message
.
message
val
mentions
=
mutableListOf
<
String
>()
val
parser
=
Parser
.
Builder
()
.
extensions
(
Arrays
.
asList
<
Extension
>(
StrikethroughExtension
.
create
(),
TablesExtension
.
create
(),
TaskListExtension
.
create
()
))
.
customDelimiterProcessor
(
StrikethroughDelimiterProcessor
())
.
build
()
message
.
mentions
?.
forEach
{
val
mention
=
getMention
(
it
)
mentions
.
add
(
mention
)
...
...
@@ -59,12 +76,17 @@ class MessageParser @Inject constructor(
text
=
text
.
replace
(
"@${it.username}"
,
mention
)
}
}
val
builder
=
SpannableBuilder
()
val
content
=
EmojiRepository
.
shortnameToUnicode
(
text
)
val
parentNode
=
parser
.
parse
(
toLenientMarkdown
(
content
))
val
parentNode
=
parser
.
parse
(
content
)
parentNode
.
accept
(
EmphasisVisitor
())
parentNode
.
accept
(
StrongEmphasisVisitor
())
parentNode
.
accept
(
MarkdownVisitor
(
configuration
,
builder
))
parentNode
.
accept
(
LinkVisitor
(
builder
))
parentNode
.
accept
(
EmojiVisitor
(
context
,
configuration
,
builder
))
message
.
mentions
?.
let
{
parentNode
.
accept
(
MentionVisitor
(
context
,
builder
,
mentions
,
selfUsername
))
}
...
...
@@ -72,13 +94,6 @@ class MessageParser @Inject constructor(
return
builder
.
text
()
}
// Convert to a lenient markdown consistent with Rocket.Chat web markdown instead of the official specs.
private
fun
toLenientMarkdown
(
text
:
String
):
String
{
return
text
.
trim
().
replace
(
"\\*(.+)\\*"
.
toRegex
())
{
"**${it.groupValues[1].trim()}**"
}
.
replace
(
"\\~(.+)\\~"
.
toRegex
())
{
"~~${it.groupValues[1].trim()}~~"
}
.
replace
(
"\\_(.+)\\_"
.
toRegex
())
{
"_${it.groupValues[1].trim()}_"
}
}
private
fun
getMention
(
user
:
SimpleUser
):
String
{
return
if
(
settings
.
useRealName
())
{
user
.
name
?:
"@${user.username}"
...
...
@@ -87,6 +102,31 @@ class MessageParser @Inject constructor(
}
}
class
EmphasisVisitor
:
AbstractVisitor
()
{
override
fun
visit
(
emphasis
:
Emphasis
)
{
if
(
emphasis
.
openingDelimiter
==
"*"
&&
emphasis
.
firstChild
!=
null
)
{
val
child
=
emphasis
.
firstChild
val
strongEmphasis
=
StrongEmphasis
()
strongEmphasis
.
appendChild
(
child
)
emphasis
.
insertBefore
(
strongEmphasis
)
emphasis
.
unlink
()
}
}
}
class
StrongEmphasisVisitor
:
AbstractVisitor
()
{
override
fun
visit
(
strongEmphasis
:
StrongEmphasis
)
{
if
(
strongEmphasis
.
openingDelimiter
==
"__"
&&
strongEmphasis
.
firstChild
!=
null
)
{
val
child
=
strongEmphasis
.
firstChild
val
emphasis
=
Emphasis
()
emphasis
.
appendChild
(
child
)
strongEmphasis
.
insertBefore
(
emphasis
)
strongEmphasis
.
unlink
()
}
}
}
class
MentionVisitor
(
context
:
Context
,
private
val
builder
:
SpannableBuilder
,
...
...
@@ -98,28 +138,28 @@ class MessageParser @Inject constructor(
private
val
othersBackgroundColor
=
ResourcesCompat
.
getColor
(
context
.
resources
,
android
.
R
.
color
.
transparent
,
context
.
theme
)
private
val
myselfTextColor
=
ResourcesCompat
.
getColor
(
context
.
resources
,
R
.
color
.
colorWhite
,
context
.
theme
)
private
val
myselfBackgroundColor
=
ResourcesCompat
.
getColor
(
context
.
resources
,
R
.
color
.
colorAccent
,
context
.
theme
)
private
val
mentionP
adding
=
context
.
resources
.
getDimensionPixelSize
(
R
.
dimen
.
padding_mention
).
toFloat
()
private
val
mentionR
adius
=
context
.
resources
.
getDimensionPixelSize
(
R
.
dimen
.
radius_mention
).
toFloat
()
private
val
p
adding
=
context
.
resources
.
getDimensionPixelSize
(
R
.
dimen
.
padding_mention
).
toFloat
()
private
val
r
adius
=
context
.
resources
.
getDimensionPixelSize
(
R
.
dimen
.
radius_mention
).
toFloat
()
override
fun
visit
(
t
:
Tex
t
)
{
val
text
=
t
.
literal
override
fun
visit
(
document
:
Documen
t
)
{
val
text
=
builder
.
text
()
val
mentionsList
=
mentions
.
toMutableList
().
also
{
it
.
add
(
"@all"
)
it
.
add
(
"@here"
)
}.
toLis
t
()
}.
distinc
t
()
mentionsList
.
forEach
{
val
mentionMe
=
it
==
currentUser
||
it
==
"@all"
||
it
==
"@here"
var
offset
=
text
.
indexOf
(
it
,
0
,
tru
e
)
var
offset
=
text
.
indexOf
(
string
=
it
,
startIndex
=
0
,
ignoreCase
=
fals
e
)
while
(
offset
>
-
1
)
{
val
textColor
=
if
(
mentionMe
)
myselfTextColor
else
othersTextColor
val
backgroundColor
=
if
(
mentionMe
)
myselfBackgroundColor
else
othersBackgroundColor
val
usernameSpan
=
MentionSpan
(
backgroundColor
,
textColor
,
mentionRadius
,
mentionP
adding
,
val
usernameSpan
=
MentionSpan
(
backgroundColor
,
textColor
,
radius
,
p
adding
,
mentionMe
)
// Add 1 to end offset to include the @.
val
end
=
offset
+
it
.
length
+
1
builder
.
setSpan
(
usernameSpan
,
offset
,
end
,
0
)
offset
=
text
.
indexOf
(
"@$it"
,
end
,
tru
e
)
val
end
=
offset
+
it
.
length
builder
.
setSpan
(
usernameSpan
,
offset
,
end
,
Spannable
.
SPAN_EXCLUSIVE_EXCLUSIVE
)
offset
=
text
.
indexOf
(
string
=
"@$it"
,
startIndex
=
end
,
ignoreCase
=
fals
e
)
}
}
}
...
...
@@ -179,7 +219,7 @@ class MessageParser @Inject constructor(
}
private
fun
newLine
()
{
if
(
builder
.
length
()
>
0
&&
'\n'
!=
builder
.
lastChar
())
{
if
(
builder
.
isNotEmpty
()
&&
'\n'
!=
builder
.
lastChar
())
{
builder
.
append
(
'\n'
)
}
}
...
...
@@ -204,7 +244,6 @@ class MessageParser @Inject constructor(
consumed
.
add
(
link
)
}
}
visitChildren
(
text
)
}
}
...
...
dependencies.gradle
View file @
c0f72f28
...
...
@@ -48,7 +48,7 @@ ext {
kotshi
:
'1.0.4'
,
frescoImageViewer
:
'0.5.1'
,
markwon
:
'
1.1.1
'
,
markwon
:
'
2.0.0
'
,
aVLoadingIndicatorView:
'2.1.3'
,
glide
:
'4.8.0'
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment