package notelinks import ( "regexp" "strings" "chat-switchboard/models" ) // wikiRe matches [[Title]], [[Title|Display]], ![[Title]], and ![[Title|Display]]. // Title first char rejects [ and ] to avoid matching inside [[[triple]]]. var wikiRe = regexp.MustCompile(`(!?)\[\[([^\[\]|][^\]|]*?)(?:\|([^\]]+?))?\]\]`) // ExtractWikilinks parses [[Title]], [[Title|Display]], and ![[Title]] from markdown content. // Returns deduplicated links preserving first occurrence. // Ignores matches preceded by [ (e.g. [[[triple]]]) since Go regexp lacks lookbehind. func ExtractWikilinks(content string) []models.NoteLink { indices := wikiRe.FindAllStringSubmatchIndex(content, -1) seen := make(map[string]bool) var links []models.NoteLink for _, idx := range indices { matchStart := idx[0] // start of full match (including optional !) // Skip if preceded by [ — rejects [[[triple]]] patterns if matchStart > 0 && content[matchStart-1] == '[' { continue } // Extract submatches by index pairs: idx[2*n], idx[2*n+1] title := strings.TrimSpace(content[idx[4]:idx[5]]) // group 2: title key := strings.ToLower(title) if key == "" || seen[key] { continue } seen[key] = true link := models.NoteLink{ TargetTitle: title, IsTransclusion: content[idx[2]:idx[3]] == "!", // group 1 } // group 3: display text (optional — idx[6:7] may be -1) if idx[6] >= 0 && idx[7] >= 0 { link.DisplayText = strings.TrimSpace(content[idx[6]:idx[7]]) } links = append(links, link) } return links }