diff --git a/migrate.go b/migrate.go index 2a0b9e6..cd101d4 100644 --- a/migrate.go +++ b/migrate.go @@ -82,13 +82,13 @@ func MigrateNotes(from string, to string, tagFile string) error { if tagOption.TargetDirectory != "" && targetDir != "" && targetDir != tagOption.TargetDirectory { log.Printf("WARNING: Target directory '%s' for tag '%s' conflict with directives (%s) from another tag. Continuing with existing value.\n", tagOption.TargetDirectory, tagName, targetDir) - } else { + } else if targetDir == "" { targetDir = tagOption.TargetDirectory } if tagOption.HandlingStrategy != "" && handlingStrategy != "" && handlingStrategy != tagOption.HandlingStrategy { log.Printf("WARNING: Handling strategy '%s' for tag '%s' conflict with directives (%s) from another tag. Continuing with existing value.\n", tagOption.HandlingStrategy, tagName, handlingStrategy) - } else { + } else if handlingStrategy == "" { if tagOption.HandlingStrategy == "same-folder" || tagOption.HandlingStrategy == "one-note-per-folder" || tagOption.HandlingStrategy == "" { handlingStrategy = tagOption.HandlingStrategy } else { diff --git a/note.go b/note.go index 92487df..0fa2343 100644 --- a/note.go +++ b/note.go @@ -15,6 +15,8 @@ import ( "regexp" "sort" "strings" + "unicode" + "unicode/utf8" ) // Regular expression to detect Bear tags. @@ -32,9 +34,10 @@ var reFile *regexp.Regexp var reImage *regexp.Regexp func init() { - // This regex has a catch: it catches a leading and trailing extra character. + // This regex has a catch: it matches a leading and trailing extra character. // This is because Go does not support look-ahead/look-behind markers. - reTag = regexp.MustCompile(`(^|\s)#([\p{L}][-\p{L}\p{N}/$_§%=+°({[\\@]*)($|\s)`) + // So we need to implement look-ahead/look-behind by ourself. + reTag = regexp.MustCompile(`(^|.?)#([\p{L}][-\p{L}\p{N}/$_§%=+°({[\\@]*)(.?|$)`) // Those two regex are straightforward reFile = regexp.MustCompile(`([^<]+)`) @@ -47,9 +50,9 @@ type Tag struct { Name string // Position of this tag in the Markdown file position []int - // The character before the tag (see Regex description above) + // The character before the tag (for look-ahead, see Regex description above) before string - // The character after the tag (see Regex description above) + // The character after the tag (for look-behind, see Regex description above) after string } @@ -57,12 +60,22 @@ type Tag struct { // characters) and position in file. func NewTag(content string, position []int) Tag { var tag Tag - tag.position = position parts := reTag.FindStringSubmatch(content) if len(parts) > 0 { - tag.before = parts[1] - tag.Name = parts[2] - tag.after = parts[3] + beforeIsEmpty := len(parts[1]) == 0 + before, _ := utf8.DecodeRuneInString(parts[1]) + beforeIsSpace := unicode.IsSpace(before) + afterIsEmpty := len(parts[3]) == 0 + after, _ := utf8.DecodeRuneInString(parts[3]) + afterIsSpace := unicode.IsSpace(after) + + // A valid tag is surrounded by either a space character or nothing + if (beforeIsEmpty || beforeIsSpace) && (afterIsEmpty || afterIsSpace) { + tag.position = position + tag.before = parts[1] + tag.Name = parts[2] + tag.after = parts[3] + } } return tag } @@ -151,7 +164,10 @@ func LoadNote(content string) *Note { var note Note note.content = content for _, match := range reTag.FindAllStringIndex(content, -1) { - note.Tags = append(note.Tags, NewTag(content[match[0]:match[1]], match)) + tag := NewTag(content[match[0]:match[1]], match) + if len(tag.Name) > 0 { + note.Tags = append(note.Tags, tag) + } } for _, match := range reFile.FindAllStringIndex(content, -1) { note.Files = append(note.Files, NewFile(content[match[0]:match[1]], match)) diff --git a/note_test.go b/note_test.go index 8986b4f..be12251 100644 --- a/note_test.go +++ b/note_test.go @@ -7,15 +7,25 @@ import ( ) func TestNewTag(t *testing.T) { - tagContent := "#test/123" + tagContent := " #test/123 " tag := NewTag(tagContent, []int{0, len(tagContent)}) assert.Equal(t, "test/123", tag.Name, "tag name must be equal") // Back to string - assert.Equal(t, "#test/123", tag.String(), "tag content must be equal") + assert.Equal(t, " #test/123 ", tag.String(), "tag content must be equal") tag.Name = "" - assert.Equal(t, "", tag.String(), "tag content must be empty") + assert.Equal(t, " ", tag.String(), "tag content must be empty") +} + +func TestNewTagLookAround(t *testing.T) { + testCases := [][]string{{" #test/123 ", "test/123"}, {"/#trap ", ""}, {" #trap#", ""}, {"#ok", "ok"}} + for _, testCase := range testCases { + tagContent := testCase[0] + expected := testCase[1] + tag := NewTag(tagContent, []int{0, len(tagContent)}) + assert.Equal(t, expected, tag.Name, "tag name must be equal") + } } func TestNewFile(t *testing.T) { @@ -61,21 +71,29 @@ And some tags in a list - #foo/bar@baz - #and-a_very%special$one/avec/des/éèà -[it's a trap](https://www.perdu.com/#toto) +[it's a trap](https://www.perdu.com/#trap) + +Another trap: https://www.perdu.com/#trap another trap: world #1 +Traps, traps, traps... #trap#trap + +#two-tags #one-after-another + #end` note := LoadNote(md) // Tags - assert.Len(t, note.Tags, 5, "There must be 5 tags") + assert.Len(t, note.Tags, 7, "There must be 7 tags") assert.Equal(t, "tag", note.Tags[0].Name, "first tag must be 'tag'") assert.Equal(t, "foo", note.Tags[1].Name, "second tag must be 'foo'") assert.Equal(t, "foo/bar@baz", note.Tags[2].Name, "third tag must be 'foo/bar@baz'") assert.Equal(t, "and-a_very%special$one/avec/des/éèà", note.Tags[3].Name, "fourth tag must be 'and-a_very%special$one/avec/des/éèà'") - assert.Equal(t, "end", note.Tags[4].Name, "fifth tag must be 'end'") + assert.Equal(t, "two-tags", note.Tags[4].Name, "fifth tag must be 'two-tags'") + assert.Equal(t, "one-after-another", note.Tags[5].Name, "sixth tag must be 'one-after-another'") + assert.Equal(t, "end", note.Tags[6].Name, "seventh tag must be 'end'") // Files assert.Len(t, note.Files, 2, "There must be 2 files") @@ -89,7 +107,7 @@ another trap: world #1 // Alter tags, files and images note.Tags[1].Name = "" - note.Tags[4].Name = "not-really" + note.Tags[6].Name = "not-really" note.Files[0].Location = "note2/my file.pdf" note.Files[1].Location = "note2/my other file.pdf" note.Images[0].Location = "note2/image 2.jpg" @@ -118,10 +136,16 @@ And some tags in a list - #foo/bar@baz - #and-a_very%special$one/avec/des/éèà -[it's a trap](https://www.perdu.com/#toto) +[it's a trap](https://www.perdu.com/#trap) + +Another trap: https://www.perdu.com/#trap another trap: world #1 +Traps, traps, traps... #trap#trap + +#two-tags #one-after-another + #not-really` newNote := note.WriteNote() assert.Equal(t, expectedMd, newNote, "notes must be equal")