First attempt with DocQuery

This commit is contained in:
Seongjae Lee
2015-08-30 21:52:20 -07:00
parent 11a6c37c70
commit 56821bccbc
7 changed files with 49 additions and 306 deletions
+45 -52
View File
@@ -2,69 +2,54 @@ path = require 'path'
fs = require 'fs-plus'
_ = require 'underscore-plus'
{$, $$, SelectListView} = require 'atom-space-pen-views'
NoteDirectory = require './note-directory'
Note = require './note'
DocQuery = require 'DocQuery'
module.exports =
class NotationalVelocityView extends SelectListView
initialize: ->
initialize: (state) ->
@initializedAt = new Date()
super
@addClass('notational-velocity from-top overlay')
@rootDirectory = atom.config.get('notational-velocity.directory')
if !fs.existsSync(@rootDirectory)
throw new Error("The given directory #{@rootDirectory} does not exist. "
+ "Set the note directory to the existing one from Settings.")
@noteDirectory = new NoteDirectory(@rootDirectory, null, () => @updateNotes())
@updateNotes()
@prevFilterQuery = ''
@prevCursorPosition = 0
updateNotes: () ->
@notes = @noteDirectory.getNotes()
@setItems(@notes)
@documentsLoaded = false
@docQuery = new DocQuery(@rootDirectory, {recursive: true})
@docQuery.on "ready", () =>
@documentsLoaded = true
@setLoading()
@populateList()
@docQuery.on "added", (fileDetails) =>
@populateList() if @documentsLoaded
@docQuery.on "updated", (fileDetails) =>
@populateList() if @documentsLoaded
@docQuery.on "removed", (fileDetails) =>
@populateList() if @documentsLoaded
selectItem: (filterQuery) ->
if filterQuery.length == 0
@prevCursorPosition = 0
return null
titlePatterns = [
///^#{filterQuery}$///i,
///^#{filterQuery}///i,
]
titleItem = null
for titlePattern in titlePatterns
titleItems = @notes
.filter (x) -> x.getTitle().match(titlePattern) != null
titleItem = if titleItems.length > 0 then titleItems[0] else null
if titleItem != null
break
titleItem = @docQuery.search(filterQuery)[0]
# If title item is not null, auto-fill the search panel.
# But we don't want to fill it when deleting.
editor = @filterEditorView.model
currCursorPosition = editor.getCursorBufferPosition().column
if titleItem != null && @prevCursorPosition < currCursorPosition
@prevFilterQuery = titleItem.getTitle()
editor.setText(filterQuery + titleItem.getTitle().slice(filterQuery.length))
editor.selectLeft(titleItem.getTitle().length - filterQuery.length)
if titleItem != undefined && @prevCursorPosition < currCursorPosition
@prevFilterQuery = titleItem.title
editor.setText(filterQuery + titleItem.title.slice(filterQuery.length))
editor.selectLeft(titleItem.title.length - filterQuery.length)
@prevCursorPosition = currCursorPosition
return titleItem
filter: (filterQuery) ->
if filterQuery.length == 0
return @notes
queries = filterQuery.split(' ')
.filter (x) -> x.length > 0
.map (x) -> new RegExp(x, 'gi')
return @notes
.filter (x) ->
queries
.map (q) -> q.test(x.getText()) || q.test(x.getTitle())
.reduce (x, y) -> x && y
return @docQuery.search(filterQuery)
getFilterKey: ->
'filetext'
@@ -72,30 +57,35 @@ class NotationalVelocityView extends SelectListView
toggle: ->
if @panel?.isVisible()
@hide()
else
else if @documentsLoaded
@populateList()
@show()
else
@setLoading("Loading documents")
@show()
viewForItem: (item) ->
content = item.getText()[0...100]
content = item.body[0...100]
$$ ->
@li class: 'two-lines', =>
@div class: 'primary-line', =>
@span "#{item.getTitle()}"
@div class: 'metadata', "#{item.getModified().toLocaleDateString()}"
@span "#{item.title}"
@div class: 'metadata', "#{item.modifiedAt.toLocaleDateString()}"
@div class: 'secondary-line', "#{content}"
confirmSelection: ->
item = @getSelectedItem()
filePath = null
item = @getSelectedItem()
filePath = null
sanitizedQuery = @getFilterQuery().replace(/\s+$/, '')
calculatedPath = path.join(@rootDirectory, sanitizedQuery + '.md')
if item?
filePath = item.getFilePath()
else
sanitizedQuery = @getFilterQuery().replace(/\s+$/, '')
if sanitizedQuery.length > 0
filePath = path.join(@rootDirectory, sanitizedQuery + '.md')
fs.writeFileSync(filePath, '')
filePath = item.filePath
else if fs.existsSync(calculatedPath)
filePath = calculatedPath
else if sanitizedQuery.length > 0
filePath = calculatedPath
fs.writeFileSync(filePath, '')
if filePath
atom.workspace.open(filePath).then (editor) ->
@@ -126,10 +116,13 @@ class NotationalVelocityView extends SelectListView
@panel?.hide()
populateList: ->
return unless @notes?
filterQuery = @getFilterQuery()
filteredItems = @filter(filterQuery)
filteredItems = null
if filterQuery == "" || filterQuery == undefined
filteredItems = @docQuery.documents
else
filteredItems = @filter(filterQuery)
selectedItem = @selectItem(filterQuery)
@list.empty()
@@ -147,7 +140,7 @@ class NotationalVelocityView extends SelectListView
@selectItemView(@list.find("li:nth-child(#{n})"))
else
@setError(@getEmptyMessage(@notes.length, filteredItems.length))
@setError(@getEmptyMessage(@docQuery.documents.length, filteredItems.length))
schedulePopulateList: ->
# We can skip it when we are just moving the position of the cursor.
+1 -1
View File
@@ -44,7 +44,7 @@ module.exports =
serialize: ->
notationalVelocityViewState: @notationalVelocityView.serialize()
createView: (state) ->
createView: (state, docQuery) ->
unless @notationalVelocityView?
NotationalVelocityView = require './notational-velocity-view'
@notationalVelocityView = new NotationalVelocityView(state.notationalVelocityViewState)
-58
View File
@@ -1,58 +0,0 @@
path = require 'path'
fs = require 'fs-plus'
pathWatcher = require 'pathwatcher'
Note = require './note'
module.exports =
class NoteDirectory
constructor: (@filePath, @parent, @onChangeCallback) ->
@directories = []
@notes = []
@updateMetadata()
@watcher = pathWatcher.watch(@filePath, (event) => @onChange(event))
destroy: ->
@notes.map (x) -> x.destroy()
@directories.map (x) -> x.destroy()
@watcher.close()
updateMetadata: ->
@notes.map (x) -> x.destroy()
@directories.map (x) -> x.destroy()
@directories = []
@notes = []
try
filenames = fs.readdirSync(@filePath)
catch e
return
for filename in filenames
@addChild(path.join(@filePath, filename))
addChild: (filePath) ->
try
fileStat = fs.statSync(filePath)
catch e
return
if fileStat.isDirectory()
@directories.push(new NoteDirectory(filePath, this, @onChangeCallback))
else
if fs.isMarkdownExtension(path.extname(filePath))
@notes.push(new Note(filePath, this, @onChangeCallback))
getNotes: ->
ret = []
ret = ret.concat(@notes)
for directory in @directories
ret = ret.concat(directory.getNotes())
if @parent is null
ret.sort (x, y) -> if x.getModified().getTime() <= y.getModified().getTime() then 1 else -1
return ret
onChange: (event) ->
# For the case of rename and change, it will be handled in its parent.
if event == 'change'
@updateMetadata()
if @onChangeCallback != null
@onChangeCallback()
-37
View File
@@ -1,37 +0,0 @@
path = require 'path'
fs = require 'fs'
pathWatcher = require 'pathwatcher'
module.exports =
class Note
constructor: (@filePath, @parent, @onChangeCallback) ->
@updateMetadata()
@updateText()
@watcher = pathWatcher.watch(@filePath, (event) => @onChange(event))
destroy: ->
@watcher.close()
updateMetadata: ->
@modified = fs.statSync(@filePath).mtime
relativePath = path.relative(atom.config.get('notational-velocity.directory'), @filePath)
@title = path.join(
path.dirname(relativePath),
path.basename(relativePath, path.extname(relativePath))
)
updateText: ->
@text = fs.readFileSync(@filePath, 'utf8')
onChange: (event) ->
# For the case of rename and change, it will be handled in its parent.
if event == 'change' && fs.existsSync(@filePath)
@updateMetadata()
@updateText()
if @onChangeCallback != null
@onChangeCallback()
getTitle: -> @title
getText: -> @text
getModified: -> @modified
getFilePath: -> @filePath
+3 -5
View File
@@ -20,16 +20,14 @@
},
"homepage": "https://github.com/seongjaelee/notational-velocity",
"dependencies": {
"fs-plus": "2.x",
"atom-space-pen-views": "^2.0.3",
"pathwatcher": "^4.2",
"docquery": "^1.1.0",
"fs-plus": "2.x",
"underscore-plus": "^1.6.6"
},
"devDependencies": {
"fs-plus": "2.x",
"atom-space-pen-views": "^2.0.3",
"pathwatcher": "^4.2",
"underscore-plus": "^1.6.6",
"temp": "~0.7.0"
"underscore-plus": "^1.6.6"
}
}
-117
View File
@@ -1,117 +0,0 @@
path = require 'path'
fs = require 'fs-plus'
temp = require 'temp'
pathWatcher = require 'pathwatcher'
NoteDirectory = require '../lib/note-directory'
Note = require '../lib/note'
describe 'NoteDirectory.getNotes', ->
defaultDirectory = atom.config.get('notational-velocity.directory')
tempDirectory = temp.mkdirSync('node-pathwatcher-directory')
noteDirectory = null
isCallbackCalled = false
# We don't want to let the mtimes of two consecutively create files are same.
# This function ensures the mtime of the file created before calling this function to be always
# smaller than to the mtime of the file created after calling this function.
wait = ->
timestampDirectory = temp.mkdirSync('node-pathwatcher-timestamp')
filePath = path.join(timestampDirectory, 'temp')
fs.writeFileSync(filePath, '.')
mtimeOld = fs.statSync(filePath).mtime
mtimeNew = mtimeOld
while mtimeOld >= mtimeNew
fs.writeFileSync(filePath, '.')
mtimeNew = fs.statSync(filePath).mtime
beforeEach ->
isCallbackCalled = false
callback = => isCallbackCalled = true
atom.config.set('notational-velocity.directory', tempDirectory)
fs.writeFileSync(path.join(tempDirectory, 'Readme.md'), 'read me')
wait()
fs.mkdirSync(path.join(tempDirectory, 'Car'))
fs.writeFileSync(path.join(tempDirectory, 'Car', 'Mini.md'), 'mini')
wait()
noteDirectory = new NoteDirectory(tempDirectory, null, callback)
afterEach ->
noteDirectory.destroy()
fs.unlinkSync(path.join(tempDirectory, 'Car', 'Mini.md'))
fs.rmdirSync(path.join(tempDirectory, 'Car'))
fs.unlinkSync(path.join(tempDirectory, 'Readme.md'))
atom.config.set('notational-velocity.directory', defaultDirectory)
it 'gives a list of notes in the order so that the newest one comes first', ->
notes = noteDirectory.getNotes()
expect(notes.length).toEqual(2)
expect(notes[0].getText()).toBe 'mini'
expect(notes[1].getText()).toBe 'read me'
expect(notes[0].getModified().getTime()).toBeGreaterThan(notes[1].getModified().getTime())
it 'changes its order when a note is changed', ->
fs.writeFileSync(path.join(tempDirectory, 'Readme.md'), 'read me new')
wait()
waitsFor -> isCallbackCalled
runs ->
notes = noteDirectory.getNotes()
expect(notes[0].getText()).toBe 'read me new'
expect(notes[1].getText()).toBe 'mini'
it 'changes its order when a note is created', ->
fs.writeFileSync(path.join(tempDirectory, 'Car', 'Prius.md'), 'prius')
wait()
waitsFor -> isCallbackCalled
runs ->
notes = noteDirectory.getNotes()
expect(notes.length).toEqual(3)
expect(notes[0].getText()).toBe 'prius'
expect(notes[1].getText()).toBe 'mini'
expect(notes[2].getText()).toBe 'read me'
fs.unlinkSync(path.join(tempDirectory, 'Car', 'Prius.md'))
it 'changes its order when a note is deleted', ->
fs.unlinkSync(path.join(tempDirectory, 'Car', 'Mini.md'))
wait()
waitsFor -> isCallbackCalled
runs ->
notes = noteDirectory.getNotes()
expect(notes.length).toEqual(1)
expect(notes[0].getText()).toBe 'read me'
# So that it won't spit an error in the teardown stage.
fs.writeFileSync(path.join(tempDirectory, 'Car', 'Mini.md'), 'mini')
it 'changes its order when a note is renamed', ->
oldPath = path.join(tempDirectory, 'Car', 'Mini.md')
newPath = path.join(tempDirectory, 'Mini.md')
fs.renameSync(oldPath, newPath)
wait()
waitsFor -> isCallbackCalled
runs ->
notes = noteDirectory.getNotes()
expect(notes.length).toEqual(2)
expect(notes[0].getTitle()).toBe 'Mini'
expect(notes[1].getTitle()).toBe 'Readme'
# So that it won't spit an error in the teardown stage.
fs.renameSync(newPath, oldPath)
it 'updates properly when a directory is created and a note is created inside it', ->
fs.mkdirSync(path.join(tempDirectory, 'Food'))
fs.writeFileSync(path.join(tempDirectory, 'Food', 'Milk.md'), 'milk')
wait()
waitsFor -> isCallbackCalled
runs ->
notes = noteDirectory.getNotes()
expect(notes.length).toEqual(3)
expect(notes[0].getText()).toBe 'milk'
# So that it won't spit an error in the teardown stage.
fs.unlinkSync(path.join(tempDirectory, 'Food', 'Milk.md'))
fs.rmdirSync(path.join(tempDirectory, 'Food'))
-36
View File
@@ -1,36 +0,0 @@
path = require 'path'
fs = require 'fs-plus'
temp = require 'temp'
pathWatcher = require 'pathwatcher'
Note = require '../lib/note'
describe 'Note', ->
defaultDirectory = atom.config.get('notational-velocity.directory')
tempDirectory = temp.mkdirSync('node-pathwatcher-directory')
tempFilePath = path.join(tempDirectory, 'Temp.md')
beforeEach ->
atom.config.set('notational-velocity.directory', tempDirectory)
fs.writeFileSync(tempFilePath, 'old')
afterEach ->
fs.unlinkSync(tempFilePath)
atom.config.set('notational-velocity.directory', defaultDirectory)
it 'creates a note', ->
note = new Note(tempFilePath, null, null)
expect(note.getTitle()).toBe 'Temp'
expect(note.getText()).toBe 'old'
expect(note.getFilePath()).toBe tempFilePath
note.destroy()
it 'modifies a note', ->
note = new Note(tempFilePath, null, null)
expect(note.getText()).toBe 'old'
fs.writeFileSync(tempFilePath, 'new')
oldModified = note.getModified()
waitsFor -> oldModified != note.getModified()
runs ->
expect(note.getText()).toBe 'new'
note.destroy()