1
0
Fork 0
mirror of https://github.com/anyproto/anytype-heart.git synced 2025-06-10 18:10:49 +09:00

human readable filenames + numbered lists + filter deleted pages

This commit is contained in:
Sergey Cherepanov 2022-02-18 16:53:59 +03:00
parent fef51f1aa5
commit 283a32d8cc
No known key found for this signature in database
GPG key ID: 87F8EDE8FBDF637C
13 changed files with 175 additions and 117 deletions

View file

@ -3,19 +3,13 @@ package export
import (
"bytes"
"context"
"github.com/anytypeio/go-anytype-middleware/core/converter/dot"
"github.com/anytypeio/go-anytype-middleware/core/converter/graphjson"
"math/rand"
"path/filepath"
"strconv"
"sync"
"unicode/utf8"
"github.com/anytypeio/go-anytype-middleware/app"
"github.com/anytypeio/go-anytype-middleware/core/block"
sb "github.com/anytypeio/go-anytype-middleware/core/block/editor/smartblock"
"github.com/anytypeio/go-anytype-middleware/core/block/process"
"github.com/anytypeio/go-anytype-middleware/core/converter"
"github.com/anytypeio/go-anytype-middleware/core/converter/dot"
"github.com/anytypeio/go-anytype-middleware/core/converter/graphjson"
"github.com/anytypeio/go-anytype-middleware/core/converter/md"
"github.com/anytypeio/go-anytype-middleware/core/converter/pbc"
"github.com/anytypeio/go-anytype-middleware/core/converter/pbjson"
@ -27,7 +21,15 @@ import (
"github.com/anytypeio/go-anytype-middleware/pkg/lib/logging"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/model"
"github.com/anytypeio/go-anytype-middleware/util/pbtypes"
"github.com/anytypeio/go-anytype-middleware/util/text"
"github.com/globalsign/mgo/bson"
"github.com/gogo/protobuf/types"
"github.com/gosimple/slug"
"math/rand"
"path/filepath"
"strconv"
"strings"
"sync"
)
const CName = "export"
@ -71,7 +73,7 @@ func (e *export) Export(req pb.RpcExportRequest) (path string, succeed int, err
}
defer queue.Stop(err)
docIds, err := e.idsForExport(req.DocIds, req.IncludeNested)
docs, err := e.docsForExport(req.DocIds, req.IncludeNested)
if err != nil {
return
}
@ -96,24 +98,24 @@ func (e *export) Export(req pb.RpcExportRequest) (path string, succeed int, err
format = dot.ExportFormatSVG
}
mc := dot.NewMultiConverter(format)
mc.SetKnownLinks(docIds)
mc.SetKnownDocs(docs)
var werr error
if succeed, werr = e.writeMultiDoc(mc, wr, docIds, queue); werr != nil {
if succeed, werr = e.writeMultiDoc(mc, wr, docs, queue); werr != nil {
log.Warnf("can't export docs: %v", werr)
}
} else if req.Format == pb.RpcExport_GRAPH_JSON {
mc := graphjson.NewMultiConverter()
mc.SetKnownLinks(docIds)
mc.SetKnownDocs(docs)
var werr error
if succeed, werr = e.writeMultiDoc(mc, wr, docIds, queue); werr != nil {
if succeed, werr = e.writeMultiDoc(mc, wr, docs, queue); werr != nil {
log.Warnf("can't export docs: %v", werr)
}
} else {
for _, docId := range docIds {
for docId := range docs {
did := docId
if err = queue.Wait(func() {
log.With("threadId", did).Debugf("write doc")
if werr := e.writeDoc(req.Format, wr, docIds, queue, did, req.IncludeFiles); werr != nil {
if werr := e.writeDoc(req.Format, wr, docs, queue, did, req.IncludeFiles); werr != nil {
log.With("threadId", did).Warnf("can't export doc: %v", werr)
} else {
succeed++
@ -132,7 +134,8 @@ func (e *export) Export(req pb.RpcExportRequest) (path string, succeed int, err
return wr.Path(), succeed, nil
}
func (e *export) idsForExport(reqIds []string, includeNested bool) (ids []string, err error) {
func (e *export) docsForExport(reqIds []string, includeNested bool) (docs map[string]*types.Struct, err error) {
docs = make(map[string]*types.Struct)
if len(reqIds) == 0 {
var res []*model.ObjectInfo
res, _, err = e.a.ObjectStore().QueryObjectInfo(database.Query{
@ -142,6 +145,11 @@ func (e *export) idsForExport(reqIds []string, includeNested bool) (ids []string
Condition: model.BlockContentDataviewFilter_Equal,
Value: pbtypes.Bool(false),
},
{
RelationKey: bundle.RelationKeyIsDeleted.String(),
Condition: model.BlockContentDataviewFilter_Equal,
Value: pbtypes.Bool(false),
},
},
}, []smartblock.SmartBlockType{
smartblock.SmartBlockTypeHome,
@ -153,17 +161,11 @@ func (e *export) idsForExport(reqIds []string, includeNested bool) (ids []string
}
for _, r := range res {
ids = append(ids, r.Id)
docs[r.Id] = r.Details
}
return ids, nil
return docs, nil
}
var m map[string]struct{}
if includeNested {
m = make(map[string]struct{}, len(reqIds)*10)
} else {
m = make(map[string]struct{}, len(reqIds))
}
var getNested func(id string)
getNested = func(id string) {
links, err := e.a.ObjectStore().GetOutboundLinksById(id)
@ -172,7 +174,7 @@ func (e *export) idsForExport(reqIds []string, includeNested bool) (ids []string
return
}
for _, link := range links {
if _, exists := m[link]; !exists {
if _, exists := docs[link]; !exists {
sbt, err2 := smartblock.SmartBlockTypeFromID(link)
if err2 != nil {
log.Errorf("failed to get smartblocktype of id %s", link)
@ -181,28 +183,54 @@ func (e *export) idsForExport(reqIds []string, includeNested bool) (ids []string
if sbt != smartblock.SmartBlockTypePage && sbt != smartblock.SmartBlockTypeSet {
continue
}
ids = append(ids, link)
m[link] = struct{}{}
getNested(link)
rec, _ := e.a.ObjectStore().QueryById(links)
if len(rec) > 0 {
docs[link] = rec[0].Details
getNested(link)
}
}
}
}
for _, id := range reqIds {
if _, exists := m[id]; !exists {
ids = append(ids, id)
m[id] = struct{}{}
if includeNested {
if len(reqIds) > 0 {
var res []*model.ObjectInfo
res, _, err = e.a.ObjectStore().QueryObjectInfo(database.Query{
Filters: []*model.BlockContentDataviewFilter{
{
RelationKey: bundle.RelationKeyId.String(),
Condition: model.BlockContentDataviewFilter_In,
Value: pbtypes.StringList(reqIds),
},
{
RelationKey: bundle.RelationKeyIsArchived.String(),
Condition: model.BlockContentDataviewFilter_Equal,
Value: pbtypes.Bool(false),
},
{
RelationKey: bundle.RelationKeyIsDeleted.String(),
Condition: model.BlockContentDataviewFilter_Equal,
Value: pbtypes.Bool(false),
},
},
}, nil)
if err != nil {
return
}
var ids []string
for _, r := range res {
docs[r.Id] = r.Details
ids = append(ids, r.Id)
}
if includeNested {
for _, id := range ids {
getNested(id)
}
}
}
return
}
func (e *export) writeMultiDoc(mw converter.MultiConverter, wr writer, docIds []string, queue process.Queue) (succeed int, err error) {
for _, did := range docIds {
func (e *export) writeMultiDoc(mw converter.MultiConverter, wr writer, docs map[string]*types.Struct, queue process.Queue) (succeed int, err error) {
for did := range docs {
if err = queue.Wait(func() {
log.With("threadId", did).Debugf("write doc")
werr := e.bs.Do(did, func(b sb.SmartBlock) error {
@ -248,12 +276,14 @@ func (e *export) writeMultiDoc(mw converter.MultiConverter, wr writer, docIds []
return
}
func (e *export) writeDoc(format pb.RpcExportFormat, wr writer, docIds []string, queue process.Queue, docId string, exportFiles bool) (err error) {
func (e *export) writeDoc(format pb.RpcExportFormat, wr writer, docInfo map[string]*types.Struct, queue process.Queue, docId string, exportFiles bool) (err error) {
return e.bs.Do(docId, func(b sb.SmartBlock) error {
if pbtypes.GetBool(b.CombinedDetails(), bundle.RelationKeyIsArchived.String()) {
return nil
}
if pbtypes.GetBool(b.CombinedDetails(), bundle.RelationKeyIsDeleted.String()) {
return nil
}
var conv converter.Converter
switch format {
case pb.RpcExport_Markdown:
@ -263,9 +293,17 @@ func (e *export) writeDoc(format pb.RpcExportFormat, wr writer, docIds []string,
case pb.RpcExport_JSON:
conv = pbjson.NewConverter(b)
}
conv.SetKnownLinks(docIds)
conv.SetKnownDocs(docInfo)
result := conv.Convert()
filename := docId + conv.Ext()
if format == pb.RpcExport_Markdown {
s := b.NewState()
name := pbtypes.GetString(s.Details(), bundle.RelationKeyName.String())
if name == "" {
name = s.Snippet()
}
filename = wr.Namer().Get("", docId, name, conv.Ext())
}
if docId == e.a.PredefinedBlocks().Home {
filename = "index" + conv.Ext()
}
@ -304,7 +342,8 @@ func (e *export) saveFile(wr writer, hash string) (err error) {
if err != nil {
return
}
filename := filepath.Join("files", wr.Namer().Get(hash, file.Meta().Name))
origName := file.Meta().Name
filename := wr.Namer().Get("files", hash, filepath.Base(origName), filepath.Ext(origName))
rd, err := file.Reader()
if err != nil {
return
@ -321,7 +360,8 @@ func (e *export) saveImage(wr writer, hash string) (err error) {
if err != nil {
return
}
filename := filepath.Join("files", wr.Namer().Get(hash, orig.Meta().Name))
origName := orig.Meta().Name
filename := wr.Namer().Get("files", hash, filepath.Base(origName), filepath.Ext(origName))
rd, err := orig.Reader()
if err != nil {
return
@ -329,40 +369,34 @@ func (e *export) saveImage(wr writer, hash string) (err error) {
return wr.WriteFile(filename, rd)
}
func newNamer() *fileNamer {
return &fileNamer{
func newNamer() *namer {
return &namer{
names: make(map[string]string),
}
}
type fileNamer struct {
type namer struct {
// id -> name and name -> id
names map[string]string
mu sync.Mutex
}
func (fn *fileNamer) Get(hash, title string) (name string) {
const fileLenLimit = 30
func (fn *namer) Get(path, hash, title, ext string) (name string) {
const fileLenLimit = 48
fn.mu.Lock()
defer fn.mu.Unlock()
var ok bool
if name, ok = fn.names[hash]; ok {
return name
}
if l := utf8.RuneCountInString(title); l > fileLenLimit {
buf := bytes.NewBuffer(nil)
for i := l - fileLenLimit; i < l; i++ {
buf.WriteRune([]rune(title)[i])
}
name = buf.String()
} else {
name = title
}
title = slug.Make(strings.TrimSuffix(title, ext))
name = text.Truncate(title, fileLenLimit)
name = strings.TrimSuffix(name, text.TruncateEllipsis)
var (
i = 0
b = 36
)
gname := name
gname := filepath.Join(path, name+ext)
for {
if _, ok = fn.names[gname]; !ok {
fn.names[hash] = gname
@ -371,6 +405,6 @@ func (fn *fileNamer) Get(hash, title string) (name string) {
}
i++
n := int64(i * b)
gname = strconv.FormatInt(rand.Int63n(n), b) + "_" + name
gname = filepath.Join(path, name+"_"+strconv.FormatInt(rand.Int63n(n), b)+ext)
}
}

View file

@ -2,6 +2,7 @@ package export
import (
"fmt"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
@ -11,15 +12,19 @@ func TestFileNamer_Get(t *testing.T) {
fn := newNamer()
names := make(map[string]bool)
nl := []string{
"some_long_name_12345678901234567890.jpg",
"some_long_name_12345678901234567890.jpg",
"files/some_long_name_12345678901234567890.jpg",
"files/some_long_name_12345678901234567890.jpg",
"some_long_name_12345678901234567890.jpg",
"one.png",
"two.png",
"two.png",
"сделай норм!.pdf",
"some very long name maybe note or just unreal long title.md",
"some very long name maybe note or just unreal long title.md",
}
for i, v := range nl {
nm := fn.Get(fmt.Sprint(i), v)
nm := fn.Get(filepath.Dir(v), fmt.Sprint(i), filepath.Base(v), filepath.Ext(v))
t.Log(nm)
names[nm] = true
assert.NotEmpty(t, nm, v)
}

View file

@ -12,7 +12,7 @@ import (
type writer interface {
Path() string
Namer() *fileNamer
Namer() *namer
WriteFile(filename string, r io.Reader) (err error)
Close() (err error)
}
@ -33,11 +33,11 @@ func newDirWriter(path string) (writer, error) {
type dirWriter struct {
path string
fn *fileNamer
fn *namer
m sync.Mutex
}
func (d *dirWriter) Namer() *fileNamer {
func (d *dirWriter) Namer() *namer {
d.m.Lock()
defer d.m.Unlock()
if d.fn == nil {
@ -85,10 +85,10 @@ type zipWriter struct {
zw *zip.Writer
f io.Closer
m sync.Mutex
fn *fileNamer
fn *namer
}
func (d *zipWriter) Namer() *fileNamer {
func (d *zipWriter) Namer() *namer {
d.m.Lock()
defer d.m.Unlock()
if d.fn == nil {

View file

@ -1,10 +1,13 @@
package converter
import "github.com/anytypeio/go-anytype-middleware/core/block/editor/state"
import (
"github.com/anytypeio/go-anytype-middleware/core/block/editor/state"
"github.com/gogo/protobuf/types"
)
type Converter interface {
Convert() (result []byte)
SetKnownLinks(ids []string) Converter
SetKnownDocs(docs map[string]*types.Struct) Converter
FileHashes() []string
ImageHashes() []string
Ext() string

View file

@ -1,3 +1,4 @@
//go:build !gomobile && !windows && !nographviz
// +build !gomobile,!windows,!nographviz
package dot
@ -5,6 +6,7 @@ package dot
import (
"bytes"
"fmt"
"github.com/gogo/protobuf/types"
"io/ioutil"
"github.com/goccy/go-graphviz"
@ -16,7 +18,6 @@ import (
"github.com/anytypeio/go-anytype-middleware/pkg/lib/core/smartblock"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/model"
"github.com/anytypeio/go-anytype-middleware/util/pbtypes"
"github.com/anytypeio/go-anytype-middleware/util/slice"
)
func NewMultiConverter(format graphviz.Format) converter.MultiConverter {
@ -51,7 +52,7 @@ type linkInfo struct {
type dot struct {
graph *cgraph.Graph
graphviz *graphviz.Graphviz
knownIds []string
knownDocs map[string]*types.Struct
fileHashes []string
imageHashes []string
exportFormat graphviz.Format
@ -59,8 +60,8 @@ type dot struct {
linksByNode map[string][]linkInfo
}
func (d *dot) SetKnownLinks(ids []string) converter.Converter {
d.knownIds = ids
func (d *dot) SetKnownDocs(docs map[string]*types.Struct) converter.Converter {
d.knownDocs = docs
return d
}
@ -115,7 +116,7 @@ func (d *dot) Add(st *state.State) error {
if err != nil {
continue
}
if slice.FindPos(d.knownIds, objId) == -1 {
if _, ok := d.knownDocs[objId]; !ok {
continue
}
if t != smartblock.SmartBlockTypeAnytypeProfile && t != smartblock.SmartBlockTypePage {
@ -137,7 +138,7 @@ func (d *dot) Add(st *state.State) error {
if err != nil {
continue
}
if slice.FindPos(d.knownIds, depId) == -1 {
if _, ok := d.knownDocs[depId]; !ok {
continue
}

View file

@ -8,7 +8,7 @@ import (
"github.com/anytypeio/go-anytype-middleware/pkg/lib/core/smartblock"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/model"
"github.com/anytypeio/go-anytype-middleware/util/pbtypes"
"github.com/anytypeio/go-anytype-middleware/util/slice"
"github.com/gogo/protobuf/types"
)
func NewMultiConverter() converter.MultiConverter {
@ -52,15 +52,15 @@ type Graph struct {
}
type graphjson struct {
knownIds []string
knownDocs map[string]*types.Struct
fileHashes []string
imageHashes []string
nodes map[string]*Node
linksByNode map[string][]*Edge
}
func (g *graphjson) SetKnownLinks(ids []string) converter.Converter {
g.knownIds = ids
func (g *graphjson) SetKnownDocs(docs map[string]*types.Struct) converter.Converter {
g.knownDocs = docs
return g
}
@ -99,7 +99,7 @@ func (g *graphjson) Add(st *state.State) error {
if err != nil {
continue
}
if slice.FindPos(g.knownIds, objId) == -1 {
if _, ok := g.knownDocs[objId]; !ok {
continue
}
if t != smartblock.SmartBlockTypeAnytypeProfile && t != smartblock.SmartBlockTypePage {
@ -121,7 +121,7 @@ func (g *graphjson) Add(st *state.State) error {
if err != nil {
continue
}
if slice.FindPos(g.knownIds, depId) == -1 {
if _, ok := g.knownDocs[depId]; !ok {
continue
}

View file

@ -3,8 +3,11 @@ package md
import (
"bytes"
"fmt"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/bundle"
"github.com/gogo/protobuf/types"
"html"
"net/url"
"path/filepath"
"sort"
"strings"
@ -14,11 +17,10 @@ import (
"github.com/anytypeio/go-anytype-middleware/pkg/lib/core"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/model"
"github.com/anytypeio/go-anytype-middleware/util/pbtypes"
"github.com/anytypeio/go-anytype-middleware/util/slice"
)
type FileNamer interface {
Get(hash, title string) (name string)
Get(path, hash, title, ext string) (name string)
}
func NewMDConverter(a core.Service, s *state.State, fn FileNamer) converter.Converter {
@ -33,9 +35,10 @@ type MD struct {
fileHashes []string
imageHashes []string
mw *marksWriter
knownLinks []string
fn FileNamer
knownDocs map[string]*types.Struct
mw *marksWriter
fn FileNamer
}
func (h *MD) Convert() (result []byte) {
@ -64,6 +67,7 @@ func (h *MD) Ext() string {
type renderState struct {
indent string
listOpened bool
listNumber int
}
func (in renderState) AddNBSpace() *renderState {
@ -71,7 +75,7 @@ func (in renderState) AddNBSpace() *renderState {
}
func (in renderState) AddSpace() *renderState {
return &renderState{indent: in.indent + " "}
return &renderState{indent: in.indent + " "}
}
func (h *MD) render(b *model.Block, in *renderState) {
@ -123,6 +127,7 @@ func (h *MD) renderText(b *model.Block, in *renderState) {
if in.listOpened && text.Style != model.BlockContentText_Marked && text.Style != model.BlockContentText_Numbered {
h.buf.WriteString(" \n")
in.listOpened = false
in.listNumber = 0
}
h.buf.WriteString(in.indent)
@ -168,7 +173,8 @@ func (h *MD) renderText(b *model.Block, in *renderState) {
h.renderChilds(b, in.AddSpace())
in.listOpened = true
case model.BlockContentText_Numbered:
h.buf.WriteString(`1. `)
in.listNumber++
h.buf.WriteString(fmt.Sprintf(`%d. `, in.listNumber))
renderText()
h.renderChilds(b, in.AddSpace())
in.listOpened = true
@ -186,10 +192,10 @@ func (h *MD) renderFile(b *model.Block, in *renderState) {
name := escape.MarkdownCharacters(html.EscapeString(file.Name))
h.buf.WriteString(in.indent)
if file.Type != model.BlockContentFile_Image {
fmt.Fprintf(h.buf, "[%s](files/%s) \n", name, url.PathEscape(h.fn.Get(file.Hash, file.Name)))
fmt.Fprintf(h.buf, "[%s](%s) \n", name, h.fn.Get("files", file.Hash, filepath.Base(file.Name), filepath.Ext(file.Name)))
h.fileHashes = append(h.fileHashes, file.Hash)
} else {
fmt.Fprintf(h.buf, "![%s](files/%s) \n", name, url.PathEscape(h.fn.Get(file.Hash, file.Name)))
fmt.Fprintf(h.buf, "![%s](%s) \n", name, h.fn.Get("files", file.Hash, filepath.Base(file.Name), filepath.Ext(file.Name)))
h.imageHashes = append(h.imageHashes, file.Hash)
}
}
@ -228,18 +234,12 @@ func (h *MD) renderLayout(b *model.Block, in *renderState) {
func (h *MD) renderLink(b *model.Block, in *renderState) {
l := b.GetLink()
h.a.ObjectStore().GetByIDs()
if l != nil && l.TargetBlockId != "" && h.isKnownLink(l.TargetBlockId) {
var title string
ois, err := h.a.ObjectStore().GetByIDs(l.TargetBlockId)
if err == nil && len(ois) > 0 {
title = pbtypes.GetString(ois[0].Details, "name")
if l != nil && l.TargetBlockId != "" {
title, filename, ok := h.getLinkInfo(l.TargetBlockId)
if ok {
h.buf.WriteString(in.indent)
fmt.Fprintf(h.buf, "[%s](%s) \n", escape.MarkdownCharacters(html.EscapeString(title)), filename)
}
if title == "" {
title = l.TargetBlockId
}
h.buf.WriteString(in.indent)
fmt.Fprintf(h.buf, "[%s](%s) \n", escape.MarkdownCharacters(html.EscapeString(title)), l.TargetBlockId+".md")
}
}
@ -260,13 +260,25 @@ func (h *MD) marksWriter(text *model.BlockContentText) *marksWriter {
return h.mw.Init(text)
}
func (h *MD) SetKnownLinks(ids []string) converter.Converter {
h.knownLinks = ids
func (h *MD) SetKnownDocs(docs map[string]*types.Struct) converter.Converter {
h.knownDocs = docs
return h
}
func (h *MD) isKnownLink(docId string) bool {
return slice.FindPos(h.knownLinks, docId) != -1
func (h *MD) getLinkInfo(docId string) (title, filename string, ok bool) {
info, ok := h.knownDocs[docId]
if !ok {
return
}
title = pbtypes.GetString(info, bundle.RelationKeyName.String())
if title == "" {
title = pbtypes.GetString(info, bundle.RelationKeySnippet.String())
}
if title == "" {
title = docId
}
filename = h.fn.Get("", docId, title, h.Ext())
return
}
type marksWriter struct {
@ -299,11 +311,12 @@ func (mw *marksWriter) writeMarks(pos int) {
fmt.Fprintf(mw.h.buf, "](%s)", urlS)
}
case model.BlockContentTextMark_Mention, model.BlockContentTextMark_Object:
if mw.h.isKnownLink(m.Param) {
_, filename, ok := mw.h.getLinkInfo(m.Param)
if ok {
if start {
mw.h.buf.WriteString("[")
} else {
fmt.Fprintf(mw.h.buf, "](%s)", m.Param+".md")
fmt.Fprintf(mw.h.buf, "](%s)", filename)
}
}
case model.BlockContentTextMark_Keyboard:

View file

@ -65,20 +65,14 @@ func TestMD_Convert(t *testing.T) {
Range: &model.Range{12, 18},
Type: model.BlockContentTextMark_Strikethrough,
},
{
Range: &model.Range{21, 29},
Type: model.BlockContentTextMark_Mention,
Param: "some_page_id",
},
},
},
},
},
})
c := NewMDConverter(nil, s, nil)
c.SetKnownLinks([]string{"some_page_id"})
res := c.Convert()
exp := "***[some](http://golang.org)*** [t](http://golang.org) [e](http://golang.org)xt **wi~~th m~~**~~ar~~ks [@mention](some_page_id.md) \n"
exp := "***[some](http://golang.org)*** [t](http://golang.org) [e](http://golang.org)xt **wi~~th m~~**~~ar~~ks @mention \n"
assert.Equal(t, exp, string(res))
})
}

View file

@ -5,6 +5,7 @@ import (
"github.com/anytypeio/go-anytype-middleware/core/converter"
"github.com/anytypeio/go-anytype-middleware/pb"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/model"
"github.com/gogo/protobuf/types"
)
func NewConverter(s state.Doc) converter.Converter {
@ -37,7 +38,7 @@ func (p *pbc) Ext() string {
return ".pb"
}
func (p *pbc) SetKnownLinks(ids []string) converter.Converter {
func (p *pbc) SetKnownDocs(map[string]*types.Struct) converter.Converter {
return p
}

View file

@ -6,6 +6,7 @@ import (
"github.com/anytypeio/go-anytype-middleware/pb"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/model"
"github.com/gogo/protobuf/jsonpb"
"github.com/gogo/protobuf/types"
)
func NewConverter(s state.Doc) converter.Converter {
@ -39,7 +40,7 @@ func (p *pbj) Ext() string {
return ".pb.json"
}
func (p *pbj) SetKnownLinks(ids []string) converter.Converter {
func (p *pbj) SetKnownDocs(map[string]*types.Struct) converter.Converter {
return p
}

1
go.mod
View file

@ -26,6 +26,7 @@ require (
github.com/gogo/protobuf v1.3.2
github.com/golang/mock v1.6.0
github.com/google/uuid v1.3.0
github.com/gosimple/slug v1.12.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645

4
go.sum
View file

@ -514,6 +514,10 @@ github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gosimple/slug v1.12.0 h1:xzuhj7G7cGtd34NXnW/yF0l+AGNfWqwgh/IXgFy7dnc=
github.com/gosimple/slug v1.12.0/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ=
github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o=
github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=

View file

@ -5,8 +5,9 @@ import (
"unicode/utf8"
)
const TruncateEllipsis = " …"
func Truncate(text string, length int) string {
var ellipsis = " …"
if utf8.RuneCountInString(text) <= length {
return text
}
@ -27,7 +28,7 @@ func Truncate(text string, length int) string {
endTextPos = lastWordIndex
}
out := text[0:endTextPos]
return out + ellipsis
return out + TruncateEllipsis
}
}
return text