Skip to content
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

fix(lsp): ignore diagnostic check within code blocks #399

Merged
merged 10 commits into from
Apr 6, 2024
79 changes: 75 additions & 4 deletions internal/adapter/lsp/document.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,6 @@ func (d *document) LookForward(pos protocol.Position, length int) string {
return string(utf16.Decode(utf16Bytes[charIdx:(charIdx + length)]))
}

var wikiLinkRegex = regexp.MustCompile(`\[?\[\[(.+?)(?: *\| *(.+?))?\]\]`)
var markdownLinkRegex = regexp.MustCompile(`\[([^\]]+?[^\\])\]\((.+?[^\\])\)`)
var fileURIregex = regexp.MustCompile(`file:///`)

// LinkFromRoot returns a Link to this document from the root of the given
// notebook.
func (d *document) LinkFromRoot(nb *core.Notebook) (*documentLink, error) {
Expand Down Expand Up @@ -194,6 +190,66 @@ func (d *document) DocumentLinkAt(pos protocol.Position) (*documentLink, error)
return nil, nil
}

// Recursive function to check whether a link is within inline code.
func linkWithinInlineCode(strBuffer string, linkStart, linkEnd int, insideInline bool) bool {
if backtickId := strings.Index(strBuffer, "`"); backtickId >= 0 && backtickId < linkEnd {
return linkWithinInlineCode(strBuffer[backtickId+1:],
linkStart-backtickId-1, linkEnd-backtickId-1, !insideInline)
} else {
return insideInline
}
}

var wikiLinkRegex = regexp.MustCompile(`\[?\[\[(.+?)(?: *\| *(.+?))?\]\]`)
var markdownLinkRegex = regexp.MustCompile(`\[([^\]]+?[^\\])\]\((.+?[^\\])\)`)
var fileURIregex = regexp.MustCompile(`file:///`)
var fencedStartRegex = regexp.MustCompile(`^(` + "```" + `|~~~).*`)
var fencedEndRegex = regexp.MustCompile(`^(` + "```" + `|~~~)\s*`)
var indentedRegex = regexp.MustCompile(`^(\s{4}|\t).+`)

var insideInline = false
var insideFenced = false
var insideIndented = false
var currentCodeBlockStart = -1

// check whether the current line in document is within a fenced or indented
// code block
func isLineWithinCodeBlock(lines []string, lineIndex int, line string) bool {
// if line is already within code fences or indented code block
if insideFenced {
if fencedEndRegex.FindStringIndex(line) != nil &&
lines[currentCodeBlockStart][:3] == line[:3] {
// Fenced code block ends with this line
insideFenced = false
currentCodeBlockStart = -1
}
return true
} else if insideIndented {
if indentedRegex.FindStringIndex(line) == nil && len(line) > 0 {
// Indeted code block ends with this line
insideIndented = false
currentCodeBlockStart = -1
} else {
return true
}
} else {
// Check whether the current line is the start of a code fence or
// indented code block
if fencedStartRegex.FindStringIndex(line) != nil {
insideFenced = true
currentCodeBlockStart = lineIndex
return true
} else if indentedRegex.FindStringIndex(line) != nil &&
(lineIndex > 0 && len(lines[lineIndex-1]) == 0 || lineIndex == 0) {
insideIndented = true
currentCodeBlockStart = lineIndex
return true
}
}
return false

}

// DocumentLinks returns all the internal and external links found in the
// document.
func (d *document) DocumentLinks() ([]documentLink, error) {
Expand All @@ -202,6 +258,10 @@ func (d *document) DocumentLinks() ([]documentLink, error) {
lines := d.GetLines()
for lineIndex, line := range lines {

if isLineWithinCodeBlock(lines, lineIndex, line) {
continue
}

appendLink := func(href string, start, end int, hasTitle bool, isWikiLink bool) {
if href == "" {
return
Expand Down Expand Up @@ -233,6 +293,10 @@ func (d *document) DocumentLinks() ([]documentLink, error) {
// note: match[0:1] is the entire match, match[2:3] is the contents of
// brackets, match[4:5] is contents of parentheses
for _, match := range markdownLinkRegex.FindAllStringSubmatchIndex(line, -1) {
// Ignore when inside backticks: `[title](file)`
if linkWithinInlineCode(line, match[0], match[1], insideInline) {
continue
}

// Ignore embedded images ![title](file.png)
if match[0] > 0 && line[match[0]-1] == '!' {
Expand All @@ -259,10 +323,17 @@ func (d *document) DocumentLinks() ([]documentLink, error) {
}

for _, match := range wikiLinkRegex.FindAllStringSubmatchIndex(line, -1) {
// Ignore when inside backticks: `[[filename]]`
if linkWithinInlineCode(line, match[0], match[1], insideInline) {
continue
}
href := line[match[2]:match[3]]
hasTitle := match[4] != -1
appendLink(href, match[0], match[1], hasTitle, true)
}
if strings.Count(line, "`")%2 == 1 {
insideInline = !insideInline
}
}

return links, nil
Expand Down