Auto-complete only the path of a Markdown link by typing: [custom title](( (#43)

pull/50/head
Mickaël Menu 3 years ago committed by GitHub
parent aa68199df8
commit 6771eb0e43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file.
* New code actions to create a note using the current selection as title.
* Custom commands to [run `new` and `index` from your editor](docs/editors-integration.md#custom-commands).
* Diagnostics to [report dead links or wiki-link titles](docs/config-lsp.md).
* Auto-complete only the path of a Markdown link by typing `[custom title]((`.
* Customize the format of `fzf`'s lines [with your own template](docs/tool-fzf.md).
```toml
[tool]

@ -91,7 +91,7 @@ func NewServer(opts ServerOpts) *Server {
ResolveProvider: boolPtr(true),
}
triggerChars := []string{"[", "#", ":"}
triggerChars := []string{"(", "[", "#", ":"}
capabilities.ExecuteCommandProvider = &protocol.ExecuteCommandOptions{
Commands: []string{
@ -184,6 +184,11 @@ func NewServer(opts ServerOpts) *Server {
return nil, err
}
switch doc.LookBehind(params.Position, 3) {
case "]((":
return server.buildLinkCompletionList(doc, notebook, params)
}
switch doc.LookBehind(params.Position, 2) {
case "[[":
return server.buildLinkCompletionList(doc, notebook, params)
@ -666,7 +671,7 @@ func (s *Server) buildInsertForTag(name string, triggerChar string, config core.
}
func (s *Server) buildLinkCompletionList(doc *document, notebook *core.Notebook, params *protocol.CompletionParams) ([]protocol.CompletionItem, error) {
linkFormatter, err := notebook.NewLinkFormatter()
linkFormatter, err := newLinkFormatter(doc, notebook, params)
if err != nil {
return nil, err
}
@ -690,6 +695,14 @@ func (s *Server) buildLinkCompletionList(doc *document, notebook *core.Notebook,
return items, nil
}
func newLinkFormatter(doc *document, notebook *core.Notebook, params *protocol.CompletionParams) (core.LinkFormatter, error) {
if doc.LookBehind(params.Position, 3) == "]((" {
return core.NewMarkdownLinkFormatter(notebook.Config.Format.Markdown, true)
} else {
return notebook.NewLinkFormatter()
}
}
func (s *Server) newCompletionItem(notebook *core.Notebook, note core.MinimalNote, doc *document, pos protocol.Position, linkFormatter core.LinkFormatter) (item protocol.CompletionItem, err error) {
kind := protocol.CompletionItemKindReference
item.Kind = &kind
@ -739,9 +752,10 @@ func (s *Server) newTextEditForLink(notebook *core.Notebook, note core.MinimalNo
}
// Some LSP clients (e.g. VSCode) auto-pair brackets, so we need to
// remove the closing ]] after the completion.
// remove the closing ]] or )) after the completion.
endOffset := 0
if doc.LookForward(pos, 2) == "]]" {
suffix := doc.LookForward(pos, 2)
if suffix == "]]" || suffix == "))" {
endOffset = 2
}

@ -15,47 +15,36 @@ type LinkFormatter func(path string, title string) (string, error)
// NewLinkFormatter generates a new LinkFormatter from the user Markdown
// configuration.
func NewLinkFormatter(config MarkdownConfig, templateLoader TemplateLoader) (LinkFormatter, error) {
var formatter LinkFormatter
var err error
switch config.LinkFormat {
case "markdown", "":
formatter, err = newMarkdownLinkFormatter(config)
return NewMarkdownLinkFormatter(config, false)
case "wiki":
formatter, err = newWikiLinkFormatter(config)
return NewWikiLinkFormatter(config)
default:
formatter, err = newCustomLinkFormatter(config, templateLoader)
return NewCustomLinkFormatter(config, templateLoader)
}
if err != nil {
return nil, err
}
return func(path, title string) (string, error) {
if config.LinkDropExtension {
path = paths.DropExt(path)
}
if config.LinkEncodePath {
path = strings.ReplaceAll(url.PathEscape(path), "%2F", "/")
}
return formatter(path, title)
}, nil
}
func newMarkdownLinkFormatter(config MarkdownConfig) (LinkFormatter, error) {
func NewMarkdownLinkFormatter(config MarkdownConfig, onlyHref bool) (LinkFormatter, error) {
return func(path, title string) (string, error) {
path = formatPath(path, config)
if !config.LinkEncodePath {
path = strings.ReplaceAll(path, `\`, `\\`)
path = strings.ReplaceAll(path, `)`, `\)`)
}
title = strings.ReplaceAll(title, `\`, `\\`)
title = strings.ReplaceAll(title, `]`, `\]`)
return fmt.Sprintf("[%s](%s)", title, path), nil
if onlyHref {
return fmt.Sprintf("(%s)", path), nil
} else {
title = strings.ReplaceAll(title, `\`, `\\`)
title = strings.ReplaceAll(title, `]`, `\]`)
return fmt.Sprintf("[%s](%s)", title, path), nil
}
}, nil
}
func newWikiLinkFormatter(config MarkdownConfig) (LinkFormatter, error) {
func NewWikiLinkFormatter(config MarkdownConfig) (LinkFormatter, error) {
return func(path, title string) (string, error) {
path = formatPath(path, config)
if !config.LinkEncodePath {
path = strings.ReplaceAll(path, `\`, `\\`)
path = strings.ReplaceAll(path, `]]`, `\]]`)
@ -64,7 +53,7 @@ func newWikiLinkFormatter(config MarkdownConfig) (LinkFormatter, error) {
}, nil
}
func newCustomLinkFormatter(config MarkdownConfig, templateLoader TemplateLoader) (LinkFormatter, error) {
func NewCustomLinkFormatter(config MarkdownConfig, templateLoader TemplateLoader) (LinkFormatter, error) {
wrap := errors.Wrapperf("failed to render custom link with format: %s", config.LinkFormat)
template, err := templateLoader.LoadTemplate(config.LinkFormat)
if err != nil {
@ -72,6 +61,7 @@ func newCustomLinkFormatter(config MarkdownConfig, templateLoader TemplateLoader
}
return func(path, title string) (string, error) {
path = formatPath(path, config)
return template.Render(customLinkRenderContext{Path: path, Title: title})
}, nil
}
@ -80,3 +70,13 @@ type customLinkRenderContext struct {
Path string
Title string
}
func formatPath(path string, config MarkdownConfig) string {
if config.LinkDropExtension {
path = paths.DropExt(path)
}
if config.LinkEncodePath {
path = strings.ReplaceAll(url.PathEscape(path), "%2F", "/")
}
return path
}

@ -36,6 +36,36 @@ func TestMarkdownLinkFormatter(t *testing.T) {
test("path/to note.md", "An interesting subject", "[An interesting subject](path/to%20note)")
}
func TestMarkdownLinkFormatterOnlyHref(t *testing.T) {
newTester := func(encodePath, dropExtension bool) func(path, expected string) {
formatter, err := NewMarkdownLinkFormatter(MarkdownConfig{
LinkFormat: "markdown",
LinkEncodePath: encodePath,
LinkDropExtension: dropExtension,
}, true)
assert.Nil(t, err)
return func(path, expected string) {
actual, err := formatter(path, "")
assert.Nil(t, err)
assert.Equal(t, actual, expected)
}
}
test := newTester(false, false)
test("path/to note.md", "(path/to note.md)")
test("", "()")
test("path/to note.md", "(path/to note.md)")
test(`path/(no\te).md`, `(path/(no\\te\).md)`)
test = newTester(true, false)
test("path/to note.md", "(path/to%20note.md)")
test(`path/(no\te).md`, `(path/%28no%5Cte%29.md)`)
test = newTester(false, true)
test("path/to note.md", "(path/to note)")
test = newTester(true, true)
test("path/to note.md", "(path/to%20note)")
}
func TestWikiLinkFormatter(t *testing.T) {
newTester := func(encodePath, dropExtension bool) func(path, title, expected string) {
formatter, err := NewLinkFormatter(MarkdownConfig{

Loading…
Cancel
Save