Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
.update(0,
[viewCtrl.rowHeightS], R.nthArg(-1),
[viewCtrl.sessionStartS.map('.rowHeight')], R.nthArg(-1))
.skipDuplicates()
const itemsP = this.sifterResultP.map('.items')
const lastSelectionChangeTimeP = Bacon
.mergeAll(
viewCtrl.keyUpS.map(Date.now),
viewCtrl.keyDownS.map(Date.now),
viewCtrl.clickedCellS.map(Date.now),
this.searchStrS.map(Date.now),
)
.toProperty(Date.now())
this.selectedFilenameS = Bacon
.update(undefined,
[viewCtrl.activePathS, this.notesPathP, this.notesP, lastSelectionChangeTimeP], (old, path, notesPath, notes, lastSelectionChangeTime) => {
if (Date.now() - lastSelectionChangeTime < 100) return old // make sure selection by user (e.g. keyup) has precedence over path change, since the delay of the trigger otherwise cause jumpy behavior
if (!path) return
const filename = notesPath.filename(path)
if (notes[filename]) return filename
},
[viewCtrl.keyUpS, itemsP], (oldFilename, _, items) => {
// stop at beginning of list; start from end of list if there was no old selected index
const oldIndex = oldFilename && items.findIndex(item => item.id === oldFilename)
let newIndex = isNaN(oldIndex) ? items.length : oldIndex
newIndex = Math.max(newIndex - 1, 0)
return items[newIndex].id
},
[viewCtrl.keyDownS, itemsP], (oldFilename, _, items) => {
// stop at end of list; start from the beginning of list if there was no old selected index
export default function ({filesProp, resultsProp, keyDownBus, selectIndexBus, activePathStream, deselectStream}) {
const escKeyStream = keyDownBus.filter(R.propEq('keyCode', 27))
const downKeyStream = keyDownBus.filter(R.propEq('keyCode', 40)).doAction('.preventDefault')
const upKeyStream = keyDownBus.filter(R.propEq('keyCode', 38)).doAction('.preventDefault')
// Selection is a semi-complicated piece; Keep an internal datastucture that keep tabs on path/item/index,
// to be able to re-calculate them on state changes
const selectedProp = Bacon
.update(
{},
[deselectStream], R.always({}),
[escKeyStream], R.always({}),
// ; Select next item
[downKeyStream, filesProp, resultsProp], (current, _, files, {items}) => {
const i = R.defaultTo(-1, current.index) + 1 // start on first item
const nextItem = items[i]
if (nextItem) {
return {
item: nextItem,
path: files[nextItem.id].path,
index: i
}
} else {
export default function () {
const terminate = this.async()
const msgStream = Bacon.fromEvent(process, 'message')
const disposeStream = msgStream.filter(R.propEq('type', 'dispose'))
const queryStream = msgStream.filter(R.propEq('type', 'query')).map(R.prop('query'))
const newItemsStream = msgStream.filter(R.propEq('type', 'add')).map(R.prop('item'))
const removedItemsStream = msgStream.filter(R.propEq('type', 'rm')).map(R.prop('path'))
const itemsProp = Bacon.update(
[],
[newItemsStream], (items, item) => items.concat({
title: Path.basename(item.path),
content: fs.readFileSync(item.path, 'utf8')
}),
[removedItemsStream], (items, path) => items.filter(item => item.path !== path)
)
const sifterProp = itemsProp
.debounce(50) // avoid creating a new sifter too often
.map(items => new Sifter(items))
Bacon.combineWith(sifterProp, queryStream, (sifter, q) =>
sifter.search(q, {
fields: ['title', 'content'],
sort: [{field: 'title', direction: 'asc'}]
})
newIndex = newIndex < items.length
? newIndex
: newIndex - 1;
} else {
// Get prev until reaching first item, or cycle to last item if there is no selection
newIndex = currentSelectedItem === null
? items.length - 1
: 0;
}
return items[newIndex];
}
).skipDuplicates();
let scrollTopBus = new Bacon.Bus();
let scrollTopProp = Bacon.update(0,
[scrollTopBus], R.nthArg(-1),
[searchStream], R.always(0),
[selectedItemProp.changes(), this.props.matchedItemsProp], (currentScrollTop, selectedItem, items) => {
// Adjust scrollTop for selected item
if (!selectedItem) return currentScrollTop;
let selectedScrollTop = items.indexOf(selectedItem) * this.state.rowHeight;
if (currentScrollTop > selectedScrollTop) {
// selected item is located before the visible bounds
// from: ..X..[...]..
// to: .[X..]......
return selectedScrollTop;
} else if (currentScrollTop + this.state.bodyHeight <= selectedScrollTop) {
// selected item is located after the visible bounds
// from: ..[...]..X..
// to: ......[..X].
return selectedScrollTop - this.state.bodyHeight + this.state.rowHeight;
state: (counter, {increment, reset}) => {
const resetAsync = reset.flatMap(([timeout, value]) => Bacon.later(timeout, value))
return Bacon.update(counter,
[increment], (state, delta) => state + delta,
[resetAsync], (_, value) => value
)
}
})
tagsStream = Bacon.mergeAll(
addFilesStream,
changedFileStream
)
.flatMap(({path}) => {
return Bacon
.fromNodeCallback(this.darwin.getTags, path)
.map(([tags]) => ({
path: path,
tags: tags.join(' ')
}))
})
.filter(R.is(Object))
}
const filesProp = this.filesProp = Bacon
.update(
[],
[addFilesStream], (files, d) => {
files.push(this._newFile(d))
return files
},
[changedFileStream], (files, d) => {
const file = this._newFile(d)
return files.map(prev => {
return prev.path === file.path
? file
: prev
})
},
[removedFilesStream], (files, relPath) => {
const path = Path.join(rootPath, relPath)
)
this._queryTask = new Task(require.resolve('./query-task.js'))
this._queryTask.start()
this._unsubscribes.push(newItemsStream.onValue(item => this._queryTask.send({type: 'add', item: item})))
this._unsubscribes.push(removedItemsStream.onValue(path => this._queryTask.send({tyquerpe: 'rm', path: path})))
const queryStream = this.queryBus.map(R.trim).skipDuplicates()
const emptyQueryStream = queryStream.filter(R.isEmpty)
const queryProp = queryStream.toProperty('')
this._unsubscribes.push(queryProp
.filter(q => q !== '')
.onValue(q => this._queryTask.send({type: 'query', query: q})))
const queryResultsStream = Bacon.fromEvent(this._queryTask, 'results')
this.itemsProp = Bacon.update(
[],
[queryResultsStream, allItemsProp], (_, results, allItems) => results.items.map(({id}) => allItems[id]),
[emptyQueryStream, allItemsProp], R.nthArg(-1),
[allItemsProp.changes(), queryProp], (items, allItems, q) => R.isEmpty(q) ? allItems : items
)
}
})
.filter(R.identity)
})
const readyStream = newWatchStream.flatMap(w => {
return Bacon.fromEvent(w, 'ready', () => w.options.cwd)
})
const removedItemsStream = newWatchStream.flatMap(w => {
return Bacon.fromEvent(w, 'unlink', relPath => {
return {
projectPath: w.options.cwd,
relPath: relPath
}
})
})
const itemsProp = Bacon.update(
[],
[addItemsStream], (items, item) => {
const fullPath = Path.join(item.projectPath, item.relPath)
const parsedPath = Path.parse(item.relPath)
const stat = item.stat || fs.statSync(fullPath)
items.push({
projectPath: item.projectPath,
relPath: item.relPath,
dirPath: parsedPath.dir,
basename: parsedPath.base,
ext: parsedPath.ext,
stat: stat,
mtimestamp: stat.mtime.getTime(),
content: fs.readFileSync(fullPath, 'utf8')
})
return items
const fileReaderResultS = Bacon
.mergeAll(
createFileReaderResultsStream(newFileS, notesCache),
createFileReaderResultsStream(createFileStream('change'))
)
const updateNoteProps = (note: Object, fields: Array, filename: string) => {
note.id = process.hrtime().toString()
fields.forEach(field => {
if (field.value) {
note[field.notePropName] = field.value(note, filename)
}
})
}
const sifterP = Bacon
.update(
new Sifter(notesCache),
[newFileS, this._fieldsP, this._fileReadersP], (sifter: Sifter, file: FileType, fields: Array, fileReaders: Array) => {
let note = sifter.items[file.filename]
if (note) {
note.ready = fileReaders.every(fileReader => note[fileReader.notePropName] !== undefined)
} else {
note = sifter.items[file.filename] = {
stats: file.stats,
ready: false
}
}
updateNoteProps(note, fields, file.filename)
return sifter
},
[fileReaderResultS, this._fieldsP, this._fileReadersP], (sifter: Sifter, readResult: FileReaderResultType, fields: Array, fileReaders: Array) => {
constructor () {
this.queryBus = new Bacon.Bus()
this._unsubscribes = []
const {openStream, closeStream} = atoms.createProjectsPathsStreams()
const watchedPathTaskStream = openStream.map(this._createWatchPathTask)
const newItemsStream = watchedPathTaskStream.flatMap(task => Bacon.fromEvent(task, 'add'))
const removedItemsStream = watchedPathTaskStream.flatMap(task => Bacon.fromEvent(task, 'unlink'))
const tasksProp = Bacon.update(
[],
[watchedPathTaskStream], (tasks, task) => tasks.concat(task),
[closeStream], this._removetask
)
this._unsubscribes.push(tasksProp.onEnd(R.forEach(this._disposeTask)))
const allItemsProp = Bacon.update(
[],
[newItemsStream], (items, newItem) => items.concat(newItem),
[removedItemsStream], (items, path) => items.filter(item => item.path !== path),
[closeStream], (items, path) => items.filter(item => !item.path.startsWith(path))
)
this._queryTask = new Task(require.resolve('./query-task.js'))
this._queryTask.start()
this._unsubscribes.push(newItemsStream.onValue(item => this._queryTask.send({type: 'add', item: item})))