Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ notesmd-cli create "{note-name}" --content "abcde" --open --editor

### Move / Rename Note

Moves a given note(path from top level of vault) with new name given (top level of vault). If given same path but different name then its treated as a rename. All links inside vault are updated to match new name.
Moves a given note(path from top level of vault) with new name given (top level of vault). If given same path but different name then its treated as a rename. All links inside vault are updated to match new name. Intermediate directories in the destination path are created automatically.

```bash
# Renames a note in default obsidian
Expand Down
14 changes: 12 additions & 2 deletions pkg/obsidian/note.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,19 @@ func (m *Note) Move(originalPath string, newPath string) error {
o := AddMdSuffix(originalPath)
n := AddMdSuffix(newPath)

err := os.Rename(o, n)
// Check the source first so a missing note is reported as such and does not
// leave behind a destination directory created below.
if _, err := os.Stat(o); err != nil {
return errors.New(NoteDoesNotExistError)
}

if err != nil {
// Create any intermediate directories the destination requires, as create
// and daily already do for their note paths.
if err := os.MkdirAll(filepath.Dir(n), 0755); err != nil {
return fmt.Errorf("failed to create note directory: %w", err)
}

if err := os.Rename(o, n); err != nil {
return errors.New(NoteDoesNotExistError)
}

Expand Down
45 changes: 45 additions & 0 deletions pkg/obsidian/note_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,51 @@ func TestMoveNote(t *testing.T) {
// Assert
assert.Equal(t, err.Error(), obsidian.NoteDoesNotExistError)
})

t.Run("Move note to destination directory that does not exist", func(t *testing.T) {
// Arrange
tempDir := t.TempDir()
originalPath := filepath.Join(tempDir, "original.md")
newPath := filepath.Join(tempDir, "one", "two", "moved")
expectedNewPath := filepath.Join(tempDir, "one", "two", "moved.md")

err := os.WriteFile(originalPath, []byte(originalContent), 0644)
if err != nil {
t.Fatal(err)
}

// Act
noteManager := obsidian.Note{}
err = noteManager.Move(filepath.Join(tempDir, "original"), newPath)

// Assert
assert.NoError(t, err, "Expected no error while moving note into a missing directory")

_, err = os.Stat(originalPath)
assert.True(t, os.IsNotExist(err), "Original file still exists at %s, expected it to be moved", originalPath)

newContent, err := os.ReadFile(expectedNewPath)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, originalContent, string(newContent), "New file content is %q, expected %q", string(newContent), originalContent)
})

t.Run("Missing note does not create destination directory", func(t *testing.T) {
// Arrange
tempDir := t.TempDir()
newFolder := filepath.Join(tempDir, "newFolder")

// Act
noteManager := obsidian.Note{}
err := noteManager.Move(filepath.Join(tempDir, "missing"), filepath.Join(newFolder, "missing"))

// Assert
assert.Equal(t, obsidian.NoteDoesNotExistError, err.Error())

_, statErr := os.Stat(newFolder)
assert.True(t, os.IsNotExist(statErr), "Destination directory %s was created for a note that does not exist", newFolder)
})
}

func createTmpDirAndFiles(t *testing.T, perm os.FileMode, files []string, content []byte) string {
Expand Down