overleaf/services/spelling/app/coffee/ASpell.coffee

88 lines
2.7 KiB
CoffeeScript
Raw Normal View History

2014-08-15 07:13:35 -04:00
async = require "async"
_ = require "underscore"
2015-03-04 11:43:59 -05:00
ASpellWorkerPool = require "./ASpellWorkerPool"
LRU = require "lru-cache"
logger = require 'logger-sharelatex'
2014-08-15 07:13:35 -04:00
2015-03-04 11:43:59 -05:00
cache = LRU(10000)
2014-08-15 07:13:35 -04:00
class ASpellRunner
checkWords: (language, words, callback = (error, result) ->) ->
@runAspellOnWords language, words, (error, output) =>
return callback(error) if error?
2015-03-04 11:43:59 -05:00
#output = @removeAspellHeader(output)
2014-08-15 07:13:35 -04:00
suggestions = @getSuggestions(output)
results = []
hits = 0
2014-08-15 07:13:35 -04:00
for word, i in words
2015-03-04 12:00:19 -05:00
key = language + ':' + word
cached = cache.get(key)
if cached?
hits++
if cached == true
# valid word, no need to do anything
continue
else
results.push index: i, suggestions: cached
else
if suggestions[word]?
cache.set(key, suggestions[word])
results.push index: i, suggestions: suggestions[word]
else
# a valid word, but uncached
cache.set(key, true)
logger.log hits: hits, total: words.length, hitrate: hits/words.length, "cache hit rate"
2014-08-15 07:13:35 -04:00
callback null, results
getSuggestions: (output) ->
lines = output.split("\n")
suggestions = {}
for line in lines
if line[0] == "&" # Suggestions found
parts = line.split(" ")
if parts.length > 1
word = parts[1]
suggestionsString = line.slice(line.indexOf(":") + 2)
suggestions[word] = suggestionsString.split(", ")
else if line[0] == "#" # No suggestions
parts = line.split(" ")
if parts.length > 1
word = parts[1]
suggestions[word] = []
return suggestions
2015-03-04 11:43:59 -05:00
#removeAspellHeader: (output) -> output.slice(1)
2014-08-15 07:13:35 -04:00
2015-03-04 11:43:59 -05:00
runAspellOnWords: (language, words, callback = (error, output) ->) ->
# send words to aspell, get back string output for those words
# find a free pipe for the language (or start one)
# send the words down the pipe
# send an END marker that will generate a "*" line in the output
# when the output pipe receives the "*" return the data sofar and reset the pipe to be available
#
# @open(language)
# @captureOutput(callback)
# @setTerseMode()
# start = new Date()
2015-03-04 11:43:59 -05:00
newWord = {}
for word in words
2015-03-04 12:00:19 -05:00
newWord[word] = true if !newWord[word] && !cache.has(language + ':' + word)
2015-03-04 11:43:59 -05:00
words = Object.keys(newWord)
2015-03-04 11:43:59 -05:00
if words.length
WorkerPool.check(language, words, ASpell.ASPELL_TIMEOUT, callback)
else
2015-03-04 12:00:19 -05:00
callback null, ""
2014-08-15 07:13:35 -04:00
module.exports = ASpell =
# The description of how to call aspell from another program can be found here:
# http://aspell.net/man-html/Through-A-Pipe.html
checkWords: (language, words, callback = (error, result) ->) ->
runner = new ASpellRunner()
callback = _.once callback
runner.checkWords language, words, callback
ASPELL_TIMEOUT : 4000
2015-03-04 11:43:59 -05:00
WorkerPool = new ASpellWorkerPool()