mirror of
https://github.com/wassname/nvatom.git
synced 2026-08-03 12:50:25 +08:00
Replace docquery to note-watcher, which supports lunr search index loading
NoteWatcher holds NoteCache, which caches recent notes and file modified time to help search index recovery.
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
chokidar = require 'chokidar'
|
||||
fs = require 'fs'
|
||||
lunr = require 'lunr'
|
||||
path = require 'path'
|
||||
_ = require 'underscore-plus'
|
||||
zlib = require 'zlib'
|
||||
{EventEmitter} = require 'events'
|
||||
NoteCache = require './note-cache'
|
||||
|
||||
module.exports =
|
||||
class NoteWatcher extends EventEmitter
|
||||
@cacheVersion = 0
|
||||
|
||||
constructor: (@_baseDirectory, @_extensions, @_maxItems) ->
|
||||
@_maxItems = @_maxItems ? 100
|
||||
@_extensions = @_extensions ? ['.txt', '.md']
|
||||
@_noteCache = new NoteCache(@_baseDirectory, @_maxItems)
|
||||
@_restoreSearchIndex() || @_initSearchIndex()
|
||||
@_startWatcher()
|
||||
|
||||
save: ->
|
||||
cache = {
|
||||
baseDirectory: @_baseDirectory,
|
||||
extensions: @_extensions,
|
||||
version: @_cacheVersion,
|
||||
searchIndex: JSON.stringify(@_searchIndex),
|
||||
noteCache: @_noteCache.toJSON(),
|
||||
}
|
||||
fs.writeFileSync(
|
||||
path.join(@_baseDirectory, 'nvatom.cache'),
|
||||
zlib.deflateSync(JSON.stringify(cache)))
|
||||
|
||||
close: ->
|
||||
@_watcher.close()
|
||||
|
||||
search: (query) ->
|
||||
if query? and query.length > 0
|
||||
return @_searchIndex.search(query)
|
||||
.slice(0, @_maxItems)
|
||||
.map((x) => @_noteCache.getNote(x.ref))
|
||||
else
|
||||
return @_noteCache.getRecentNotes()
|
||||
|
||||
length: ->
|
||||
@_noteCache.length()
|
||||
|
||||
_initSearchIndex: ->
|
||||
@_state = 'initializing'
|
||||
@_searchIndex = lunr(() ->
|
||||
@field('title', { boost: 10 })
|
||||
@field('body')
|
||||
)
|
||||
|
||||
_restoreSearchIndex: ->
|
||||
cacheFilePath = path.join(@_baseDirectory, 'nvatom.cache')
|
||||
return false unless fs.existsSync(cacheFilePath)
|
||||
cache = JSON.parse(zlib.inflateSync(fs.readFileSync(cacheFilePath)))
|
||||
return false if cache == null
|
||||
return false unless _.isEqual(cache.baseDirectory, @_baseDirectory)
|
||||
return false unless _.isEqual(cache.extensions, @_extensions)
|
||||
return false unless _.isEqual(cache.version, @_cacheVersion)
|
||||
@_searchIndex = lunr.Index.load(JSON.parse(cache.searchIndex))
|
||||
# TODO: Make this as an interface. Old note cache does not have to know upsert, remove, ready and so on.
|
||||
# It only needs to know getNoteIds, hasNoteId, and getModifiedAt.
|
||||
@_oldNoteCache = new NoteCache(@_baseDirectory, @_maxItems).load(JSON.parse(cache.noteCache))
|
||||
@_state = 'recovering'
|
||||
return true
|
||||
|
||||
_startWatcher: ->
|
||||
options = {
|
||||
ignored: (filePath, fileStat) =>
|
||||
if fileStat?.isFile() then @_extensions.indexOf(path.extname(filePath)) < 0 else false
|
||||
}
|
||||
|
||||
@_watcher = chokidar
|
||||
.watch @_baseDirectory, options
|
||||
.on 'add', (args) => @_add(args)
|
||||
.on 'change', (args) => @_change(args)
|
||||
.on 'unlink', (args) => @_unlink(args)
|
||||
.on 'ready', () => @_ready()
|
||||
|
||||
_add: (filePath) ->
|
||||
@_noteCache.upsert(@_toNoteId(filePath), fs.statSync(filePath).mtime)
|
||||
if @_state != 'recovering'
|
||||
@_searchIndex.add(@_toFileDetails(filePath))
|
||||
@emit 'update', filePath
|
||||
|
||||
_change: (filePath) ->
|
||||
@_noteCache.upsert(@_toNoteId(filePath), fs.statSync(filePath).mtime)
|
||||
if @_state != 'recovering'
|
||||
@_searchIndex.update(@_toFileDetails(filePath))
|
||||
@emit 'update', filePath
|
||||
|
||||
_unlink: (filePath) ->
|
||||
@_noteCache.remove(@_toNoteId(filePath))
|
||||
if @_state != 'recovering'
|
||||
@_searchIndex.remove(@_toFileDetails(filePath))
|
||||
@emit 'update', filePath
|
||||
|
||||
_ready: () ->
|
||||
@_noteCache.ready()
|
||||
if @_state == 'recovering'
|
||||
@_updateSearchIndex(@_oldNoteCache, @_noteCache)
|
||||
delete @_oldNoteCache
|
||||
@_state = 'ready'
|
||||
@emit 'ready'
|
||||
|
||||
_updateSearchIndex: (oldNoteCache, newNoteCache) ->
|
||||
for noteId in oldNoteCache.getNoteIds()
|
||||
if !newNoteCache.hasNoteId(noteId)
|
||||
@_searchIndex.remove(@_toFileDetails(@_toFilePath(noteId)))
|
||||
else if oldNoteCache.getModifiedAt(noteId) <= newNoteCache.getModifiedAt(noteId)
|
||||
@_searchIndex.update(@_toFileDetails(@_toFilePath(noteId)))
|
||||
for noteId in newNoteCache.getNoteIds()
|
||||
if !oldNoteCache.hasNoteId(noteId)
|
||||
@_searchIndex.add(@_toFileDetails(@_toFilePath(noteId)))
|
||||
|
||||
_toNoteId: (filePath) ->
|
||||
path.relative(@_baseDirectory, filePath)
|
||||
|
||||
_toFilePath: (noteId) ->
|
||||
path.join(@_baseDirectory, noteId)
|
||||
|
||||
_toFileDetails: (filePath) ->
|
||||
fileName = path.basename(filePath)
|
||||
return {
|
||||
id: @_toNoteId(filePath),
|
||||
title: path.basename(fileName, path.extname(fileName)),
|
||||
body: if fs.existsSync(filePath) then fs.readFileSync(filePath, { encoding: 'utf8' }) else ''
|
||||
}
|
||||
Reference in New Issue
Block a user