Show issue reactions (#421)
``` $ tea issue 230 #230 issue/pull details: show reactions (open) @6543 created 2020-10-22 16:39 since reactions are utf8 now and most terminals too, we can display them nicely :) https://gitea.com/api/v1/repos/gitea/tea/issues/230/reactions -------- 1x 🎉 | 1x 👀 | 1x | 1x 👍 | 1x 👎 | 1x 😆 | 1x 😕 | 1x ❤️ ``` caveats: - reactions are not returned as UTF8 (as was claimed in #230), so they need to be parsed. the library I use doesn't (and can't → ) support all reactions available in gitea - currently only for issues, as reactions for comments mean an additional API request for each comment.. fixes #230 Co-authored-by: Norwin <git@nroo.de> Reviewed-on: https://gitea.com/gitea/tea/pulls/421 Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com> Reviewed-by: techknowlogick <techknowlogick@gitea.io> Co-authored-by: Norwin <noerw@noreply.gitea.io> Co-committed-by: Norwin <noerw@noreply.gitea.io>
This commit is contained in:
parent
7a05be436c
commit
58aaa17e7e
|
@ -54,11 +54,16 @@ func runIssueDetail(cmd *cli.Context, index string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
issue, _, err := ctx.Login.Client().GetIssue(ctx.Owner, ctx.Repo, idx)
|
client := ctx.Login.Client()
|
||||||
|
issue, _, err := client.GetIssue(ctx.Owner, ctx.Repo, idx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
print.IssueDetails(issue)
|
reactions, _, err := client.GetIssueReactions(ctx.Owner, ctx.Repo, idx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
print.IssueDetails(issue, reactions)
|
||||||
|
|
||||||
if issue.Comments > 0 {
|
if issue.Comments > 0 {
|
||||||
err = interact.ShowCommentsMaybeInteractive(ctx, idx, issue.Comments)
|
err = interact.ShowCommentsMaybeInteractive(ctx, idx, issue.Comments)
|
||||||
|
|
|
@ -47,6 +47,6 @@ func editIssueState(cmd *cli.Context, opts gitea.EditIssueOption) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
print.IssueDetails(issue)
|
print.IssueDetails(issue, nil)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -13,6 +13,7 @@ require (
|
||||||
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
|
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
|
||||||
github.com/charmbracelet/glamour v0.3.0
|
github.com/charmbracelet/glamour v0.3.0
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
|
||||||
|
github.com/enescakir/emoji v1.0.0
|
||||||
github.com/go-git/go-git/v5 v5.4.2
|
github.com/go-git/go-git/v5 v5.4.2
|
||||||
github.com/hashicorp/go-version v1.3.0 // indirect
|
github.com/hashicorp/go-version v1.3.0 // indirect
|
||||||
github.com/kevinburke/ssh_config v1.1.0 // indirect
|
github.com/kevinburke/ssh_config v1.1.0 // indirect
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -53,6 +53,8 @@ github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E
|
||||||
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||||
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
||||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||||
|
github.com/enescakir/emoji v1.0.0 h1:W+HsNql8swfCQFtioDGDHCHri8nudlK1n5p2rHCJoog=
|
||||||
|
github.com/enescakir/emoji v1.0.0/go.mod h1:Bt1EKuLnKDTYpLALApstIkAjdDrS/8IAgTkKp+WKFD0=
|
||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
||||||
|
|
|
@ -9,11 +9,12 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/sdk/gitea"
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/enescakir/emoji"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IssueDetails print an issue rendered to stdout
|
// IssueDetails print an issue rendered to stdout
|
||||||
func IssueDetails(issue *gitea.Issue) {
|
func IssueDetails(issue *gitea.Issue, reactions []*gitea.Reaction) {
|
||||||
outputMarkdown(fmt.Sprintf(
|
out := fmt.Sprintf(
|
||||||
"# #%d %s (%s)\n@%s created %s\n\n%s\n",
|
"# #%d %s (%s)\n@%s created %s\n\n%s\n",
|
||||||
issue.Index,
|
issue.Index,
|
||||||
issue.Title,
|
issue.Title,
|
||||||
|
@ -21,7 +22,27 @@ func IssueDetails(issue *gitea.Issue) {
|
||||||
issue.Poster.UserName,
|
issue.Poster.UserName,
|
||||||
FormatTime(issue.Created),
|
FormatTime(issue.Created),
|
||||||
issue.Body,
|
issue.Body,
|
||||||
), issue.HTMLURL)
|
)
|
||||||
|
|
||||||
|
if len(reactions) > 0 {
|
||||||
|
out += fmt.Sprintf("\n---\n\n%s\n", formatReactions(reactions))
|
||||||
|
}
|
||||||
|
|
||||||
|
outputMarkdown(out, issue.HTMLURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatReactions(reactions []*gitea.Reaction) string {
|
||||||
|
reactionCounts := make(map[string]uint16)
|
||||||
|
for _, r := range reactions {
|
||||||
|
reactionCounts[r.Reaction] += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
reactionStrings := make([]string, 0, len(reactionCounts))
|
||||||
|
for reaction, count := range reactionCounts {
|
||||||
|
reactionStrings = append(reactionStrings, fmt.Sprintf("%dx :%s:", count, reaction))
|
||||||
|
}
|
||||||
|
|
||||||
|
return emoji.Parse(strings.Join(reactionStrings, " | "))
|
||||||
}
|
}
|
||||||
|
|
||||||
// IssuesPullsList prints a listing of issues & pulls
|
// IssuesPullsList prints a listing of issues & pulls
|
||||||
|
|
|
@ -25,7 +25,7 @@ func CreateIssue(login *config.Login, repoOwner, repoName string, opts gitea.Cre
|
||||||
return fmt.Errorf("could not create issue: %s", err)
|
return fmt.Errorf("could not create issue: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
print.IssueDetails(issue)
|
print.IssueDetails(issue, nil)
|
||||||
|
|
||||||
fmt.Println(issue.HTMLURL)
|
fmt.Println(issue.HTMLURL)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, built with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Dependency directories (remove the comment below to include it)
|
||||||
|
# vendor/
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 Enes Çakır
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,99 @@
|
||||||
|
# emoji :rocket: :school_satchel: :tada:
|
||||||
|
[![Build Status](https://github.com/enescakir/emoji/workflows/build/badge.svg?branch=master)](https://github.com/enescakir/emoji/actions)
|
||||||
|
[![godoc](https://godoc.org/github.com/enescakir/emoji?status.svg)](https://godoc.org/github.com/enescakir/emoji)
|
||||||
|
[![Go Report Card](https://goreportcard.com/badge/github.com/enescakir/emoji)](https://goreportcard.com/report/github.com/enescakir/emoji)
|
||||||
|
[![Codecov](https://img.shields.io/codecov/c/github/enescakir/emoji)](https://codecov.io/gh/enescakir/emoji)
|
||||||
|
[![MIT License](https://img.shields.io/github/license/enescakir/emoji)](https://github.com/enescakir/emoji/blob/master/LICENSE)
|
||||||
|
|
||||||
|
`emoji` is a minimalistic emoji library for Go. It lets you use emoji characters in strings.
|
||||||
|
|
||||||
|
Inspired by [spatie/emoji](https://github.com/spatie/emoji)
|
||||||
|
|
||||||
|
## Install :floppy_disk:
|
||||||
|
``` bash
|
||||||
|
go get github.com/enescakir/emoji
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage :surfer:
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/enescakir/emoji"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Printf("Hello %v\n", emoji.WavingHand)
|
||||||
|
fmt.Printf("I am %v from %v\n",
|
||||||
|
emoji.ManTechnologist,
|
||||||
|
emoji.FlagForTurkey,
|
||||||
|
)
|
||||||
|
fmt.Printf("Different skin tones.\n default: %v light: %v dark: %v\n",
|
||||||
|
emoji.ThumbsUp,
|
||||||
|
emoji.OkHand.Tone(emoji.Light),
|
||||||
|
emoji.CallMeHand.Tone(emoji.Dark),
|
||||||
|
)
|
||||||
|
fmt.Printf("Emojis with multiple skin tones.\n both medium: %v light and dark: %v\n",
|
||||||
|
emoji.PeopleHoldingHands.Tone(emoji.Medium),
|
||||||
|
emoji.PeopleHoldingHands.Tone(emoji.Light, emoji.Dark),
|
||||||
|
)
|
||||||
|
fmt.Println(emoji.Parse("Emoji aliases are :sunglasses:"))
|
||||||
|
emoji.Println("Use fmt wrappers :+1: with emoji support :tada:")
|
||||||
|
}
|
||||||
|
|
||||||
|
/* OUTPUT
|
||||||
|
|
||||||
|
Hello 👋
|
||||||
|
I am 👨💻 from 🇹🇷
|
||||||
|
Different skin tones.
|
||||||
|
default: 👍 light: 👌🏻 dark: 🤙🏿
|
||||||
|
Emojis with multiple skin tones.
|
||||||
|
both medium: 🧑🏽🤝🧑🏽 light and dark: 🧑🏻🤝🧑🏿
|
||||||
|
Emoji aliases are 😎
|
||||||
|
Use fmt wrappers 👍 with emoji support 🎉
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
|
||||||
|
This package contains emojis constants based on [Full Emoji List v13.0](https://unicode.org/Public/emoji/13.0/emoji-test.txt).
|
||||||
|
```go
|
||||||
|
emoji.CallMeHand // 🤙
|
||||||
|
emoji.CallMeHand.Tone(emoji.Dark) // 🤙🏿
|
||||||
|
```
|
||||||
|
Also, it has additional emoji aliases from [github/gemoji](https://github.com/github/gemoji).
|
||||||
|
```go
|
||||||
|
emoji.Parse(":+1:") // 👍
|
||||||
|
emoji.Parse(":100:") // 💯
|
||||||
|
```
|
||||||
|
|
||||||
|
You can generate country flag emoji with [ISO 3166 Alpha2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) codes:
|
||||||
|
```go
|
||||||
|
emoji.CountryFlag("tr") // 🇹🇷
|
||||||
|
emoji.CountryFlag("US") // 🇺🇸
|
||||||
|
emoji.Parse("country flag alias :flag-gb:") // country flag alias 🇬🇧
|
||||||
|
```
|
||||||
|
|
||||||
|
All constants are generated by `internal/generator`.
|
||||||
|
|
||||||
|
## Testing :hammer:
|
||||||
|
``` bash
|
||||||
|
go test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Todo :pushpin:
|
||||||
|
* Add examples to `godoc`
|
||||||
|
|
||||||
|
## Contributing :man_technologist:
|
||||||
|
I am accepting PRs that add aliases to the package.
|
||||||
|
You have to add it to `customEmojis` list at `internal/generator/main`.
|
||||||
|
|
||||||
|
If you think an emoji constant is not correct, open an issue.
|
||||||
|
Please use [this list](http://unicode.org/emoji/charts/full-emoji-list.html)
|
||||||
|
to look up the correct unicode value and the name of the character.
|
||||||
|
|
||||||
|
## Credits :star:
|
||||||
|
- [Enes Çakır](https://github.com/enescakir)
|
||||||
|
|
||||||
|
## License :scroll:
|
||||||
|
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,4 @@
|
||||||
|
/*
|
||||||
|
Package emoji makes working with emojis easier.
|
||||||
|
*/
|
||||||
|
package emoji
|
|
@ -0,0 +1,124 @@
|
||||||
|
package emoji
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Base attributes
|
||||||
|
const (
|
||||||
|
TonePlaceholder = "@"
|
||||||
|
flagBaseIndex = '\U0001F1E6' - 'a'
|
||||||
|
)
|
||||||
|
|
||||||
|
// Skin tone colors
|
||||||
|
const (
|
||||||
|
Default Tone = ""
|
||||||
|
Light Tone = "\U0001F3FB"
|
||||||
|
MediumLight Tone = "\U0001F3FC"
|
||||||
|
Medium Tone = "\U0001F3FD"
|
||||||
|
MediumDark Tone = "\U0001F3FE"
|
||||||
|
Dark Tone = "\U0001F3FF"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Emoji defines an emoji object with no skin variations.
|
||||||
|
type Emoji string
|
||||||
|
|
||||||
|
// String returns string representation of the simple emoji.
|
||||||
|
func (e Emoji) String() string {
|
||||||
|
return string(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EmojiWithTone defines an emoji object that has skin tone options.
|
||||||
|
type EmojiWithTone struct {
|
||||||
|
oneTonedCode string
|
||||||
|
twoTonedCode string
|
||||||
|
defaultTone Tone
|
||||||
|
}
|
||||||
|
|
||||||
|
// newEmojiWithTone constructs a new emoji object that has skin tone options.
|
||||||
|
func newEmojiWithTone(codes ...string) EmojiWithTone {
|
||||||
|
if len(codes) == 0 {
|
||||||
|
return EmojiWithTone{}
|
||||||
|
}
|
||||||
|
|
||||||
|
one := codes[0]
|
||||||
|
two := codes[0]
|
||||||
|
|
||||||
|
if len(codes) > 1 {
|
||||||
|
two = codes[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return EmojiWithTone{
|
||||||
|
oneTonedCode: one,
|
||||||
|
twoTonedCode: two,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// withDefaultTone sets default tone for an emoji and returns it.
|
||||||
|
func (e EmojiWithTone) withDefaultTone(tone string) EmojiWithTone {
|
||||||
|
e.defaultTone = Tone(tone)
|
||||||
|
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns string representation of the emoji with default skin tone.
|
||||||
|
func (e EmojiWithTone) String() string {
|
||||||
|
return strings.ReplaceAll(e.oneTonedCode, TonePlaceholder, e.defaultTone.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tone returns string representation of the emoji with given skin tone.
|
||||||
|
func (e EmojiWithTone) Tone(tones ...Tone) string {
|
||||||
|
// if no tone given, return with default skin tone
|
||||||
|
if len(tones) == 0 {
|
||||||
|
return e.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
str := e.twoTonedCode
|
||||||
|
replaceCount := 1
|
||||||
|
|
||||||
|
// if one tone given or emoji doesn't have twoTonedCode, use oneTonedCode
|
||||||
|
// Also, replace all with one tone
|
||||||
|
if len(tones) == 1 {
|
||||||
|
str = e.oneTonedCode
|
||||||
|
replaceCount = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace tone one by one
|
||||||
|
for _, t := range tones {
|
||||||
|
// use emoji's default tone
|
||||||
|
if t == Default {
|
||||||
|
t = e.defaultTone
|
||||||
|
}
|
||||||
|
|
||||||
|
str = strings.Replace(str, TonePlaceholder, t.String(), replaceCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tone defines skin tone options for emojis.
|
||||||
|
type Tone string
|
||||||
|
|
||||||
|
// String returns string representation of the skin tone.
|
||||||
|
func (t Tone) String() string {
|
||||||
|
return string(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountryFlag returns a country flag emoji from given country code.
|
||||||
|
// Full list of country codes: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
|
||||||
|
func CountryFlag(code string) (Emoji, error) {
|
||||||
|
if len(code) != 2 {
|
||||||
|
return "", fmt.Errorf("not valid country code: %q", code)
|
||||||
|
}
|
||||||
|
|
||||||
|
code = strings.ToLower(code)
|
||||||
|
flag := countryCodeLetter(code[0]) + countryCodeLetter(code[1])
|
||||||
|
|
||||||
|
return Emoji(flag), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// countryCodeLetter shifts given letter byte as flagBaseIndex.
|
||||||
|
func countryCodeLetter(l byte) string {
|
||||||
|
return string(rune(l) + flagBaseIndex)
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package emoji
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sprint wraps fmt.Sprint with emoji support
|
||||||
|
func Sprint(a ...interface{}) string {
|
||||||
|
return Parse(fmt.Sprint(a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprintf wraps fmt.Sprintf with emoji support
|
||||||
|
func Sprintf(format string, a ...interface{}) string {
|
||||||
|
return Parse(fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprintln wraps fmt.Sprintln with emoji support
|
||||||
|
func Sprintln(a ...interface{}) string {
|
||||||
|
return Parse(fmt.Sprintln(a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print wraps fmt.Print with emoji support
|
||||||
|
func Print(a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Print(Sprint(a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Println wraps fmt.Println with emoji support
|
||||||
|
func Println(a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Println(Sprint(a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Printf wraps fmt.Printf with emoji support
|
||||||
|
func Printf(format string, a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Print(Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fprint wraps fmt.Fprint with emoji support
|
||||||
|
func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Fprint(w, Sprint(a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fprintf wraps fmt.Fprintf with emoji support
|
||||||
|
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Fprint(w, Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fprintln wraps fmt.Fprintln with emoji support
|
||||||
|
func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Fprintln(w, Sprint(a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorf wraps fmt.Errorf with emoji support
|
||||||
|
func Errorf(format string, a ...interface{}) error {
|
||||||
|
return fmt.Errorf(Sprintf(format, a...))
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
module github.com/enescakir/emoji
|
||||||
|
|
||||||
|
go 1.13
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,127 @@
|
||||||
|
package emoji
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
flagRegex = regexp.MustCompile(`^:flag-([a-zA-Z]{2}):$`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Parse replaces emoji aliases (:pizza:) with unicode representation.
|
||||||
|
func Parse(input string) string {
|
||||||
|
var matched strings.Builder
|
||||||
|
var output strings.Builder
|
||||||
|
|
||||||
|
for _, r := range input {
|
||||||
|
// when it's not `:`, it might be inner or outer of the emoji alias
|
||||||
|
if r != ':' {
|
||||||
|
// if matched is empty, it's the outer of the emoji alias
|
||||||
|
if matched.Len() == 0 {
|
||||||
|
output.WriteRune(r)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
matched.WriteRune(r)
|
||||||
|
|
||||||
|
// if it's space, the alias's not valid.
|
||||||
|
// reset matched for breaking the emoji alias
|
||||||
|
if unicode.IsSpace(r) {
|
||||||
|
output.WriteString(matched.String())
|
||||||
|
matched.Reset()
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// r is `:` now
|
||||||
|
// if matched is empty, it's the beginning of the emoji alias
|
||||||
|
if matched.Len() == 0 {
|
||||||
|
matched.WriteRune(r)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// it's the end of the emoji alias
|
||||||
|
match := matched.String()
|
||||||
|
alias := match + ":"
|
||||||
|
|
||||||
|
// check for emoji alias
|
||||||
|
if code, ok := Find(alias); ok {
|
||||||
|
output.WriteString(code)
|
||||||
|
matched.Reset()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// not found any emoji
|
||||||
|
output.WriteString(match)
|
||||||
|
// it might be the beginning of the another emoji alias
|
||||||
|
matched.Reset()
|
||||||
|
matched.WriteRune(r)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// if matched not empty, add it to output
|
||||||
|
if matched.Len() != 0 {
|
||||||
|
output.WriteString(matched.String())
|
||||||
|
matched.Reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
return output.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map returns the emojis map.
|
||||||
|
// Key is the alias of the emoji.
|
||||||
|
// Value is the code of the emoji.
|
||||||
|
func Map() map[string]string {
|
||||||
|
return emojiMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendAlias adds new emoji pair to the emojis map.
|
||||||
|
func AppendAlias(alias, code string) error {
|
||||||
|
if c, ok := emojiMap[alias]; ok {
|
||||||
|
return fmt.Errorf("emoji already exist: %q => %+q", alias, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, r := range alias {
|
||||||
|
if unicode.IsSpace(r) {
|
||||||
|
return fmt.Errorf("emoji alias is not valid: %q", alias)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emojiMap[alias] = code
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exist checks existence of the emoji by alias.
|
||||||
|
func Exist(alias string) bool {
|
||||||
|
_, ok := Find(alias)
|
||||||
|
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find returns the emoji code by alias.
|
||||||
|
func Find(alias string) (string, bool) {
|
||||||
|
if code, ok := emojiMap[alias]; ok {
|
||||||
|
return code, true
|
||||||
|
}
|
||||||
|
|
||||||
|
if flag := checkFlag(alias); len(flag) > 0 {
|
||||||
|
return flag, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkFlag finds flag emoji for `flag-[CODE]` pattern
|
||||||
|
func checkFlag(alias string) string {
|
||||||
|
if matches := flagRegex.FindStringSubmatch(alias); len(matches) == 2 {
|
||||||
|
flag, _ := CountryFlag(matches[1])
|
||||||
|
|
||||||
|
return flag.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
|
@ -93,6 +93,8 @@ github.com/emirpasic/gods/lists/arraylist
|
||||||
github.com/emirpasic/gods/trees
|
github.com/emirpasic/gods/trees
|
||||||
github.com/emirpasic/gods/trees/binaryheap
|
github.com/emirpasic/gods/trees/binaryheap
|
||||||
github.com/emirpasic/gods/utils
|
github.com/emirpasic/gods/utils
|
||||||
|
# github.com/enescakir/emoji v1.0.0
|
||||||
|
github.com/enescakir/emoji
|
||||||
# github.com/go-git/gcfg v1.5.0
|
# github.com/go-git/gcfg v1.5.0
|
||||||
github.com/go-git/gcfg
|
github.com/go-git/gcfg
|
||||||
github.com/go-git/gcfg/scanner
|
github.com/go-git/gcfg/scanner
|
||||||
|
|
Loading…
Reference in New Issue