Add initial spell checker package

This adds spell checking to editor's using the
text, markdown, or Git commit message grammars.
This commit is contained in:
Kevin Sawicki
2013-02-26 18:49:22 -08:00
parent ef9d4b392b
commit 65cb929b99
8 changed files with 193 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
{View} = require 'space-pen'
Range = require 'range'
module.exports =
class MisspellingView extends View
@content: ->
@div class: 'misspelling'
initialize: (range, @editor) ->
@editSession = @editor.activeEditSession
range = @editSession.screenRangeForBufferRange(Range.fromObject(range))
@startPosition = range.start
@endPosition = range.end
@marker = @editSession.markScreenRange(range, invalidationStrategy: 'between')
@editSession.observeMarker @marker, ({newHeadScreenPosition, newTailScreenPosition, valid}) =>
@startPosition = newTailScreenPosition
@endPosition = newHeadScreenPosition
@updateDisplayPosition = valid
@hide() unless valid
@editor.on 'editor:display-updated', =>
@updatePosition() if @updateDisplayPosition
@updatePosition()
updatePosition: ->
@updateDisplayPosition = false
startPixelPosition = @editor.pixelPositionForScreenPosition(@startPosition)
endPixelPosition = @editor.pixelPositionForScreenPosition(@endPosition)
@css
top: startPixelPosition.top
left: startPixelPosition.left
width: endPixelPosition.left - startPixelPosition.left
height: @editor.lineHeight
@show()
destroy: ->
@editSession.destroyMarker(@marker)
@remove()

View File

@@ -0,0 +1,14 @@
module.exports =
findMisspellings: (text) ->
wordRegex = /(?:^|\s)([a-zA-Z]+)(?=\s|\.|$)/g
row = 0
misspellings = []
for line in text.split('\n')
while matches = wordRegex.exec(line)
word = matches[1]
continue unless $native.isMisspelled(word)
startColumn = matches.index + matches[0].length - word.length
endColumn = startColumn + word.length
misspellings.push([[row, startColumn], [row, endColumn]])
row++
callTaskMethod('misspellingsFound', misspellings)

View File

@@ -0,0 +1,14 @@
Task = require 'task'
module.exports =
class SpellCheckTask extends Task
constructor: (@text, @callback) ->
super('spell-check/lib/spell-check-handler')
started: ->
@callWorkerMethod('findMisspellings', @text)
misspellingsFound: (misspellings) ->
@done()
@callback(misspellings)

View File

@@ -0,0 +1,53 @@
{View} = require 'space-pen'
$ = require 'jquery'
_ = require 'underscore'
SpellCheckTask = require './spell-check-task'
MisspellingView = require './misspelling-view'
module.exports =
class SpellCheckView extends View
@content: ->
@div class: 'spell-check'
views: []
initialize: (@editor) ->
@subscribe @editor, 'editor:path-changed', => @subscribeToBuffer()
@subscribe @editor, 'editor:grammar-changed', => @subscribeToBuffer()
@observeConfig 'editor.fontSize', => @subscribeToBuffer()
@observeConfig 'spell-check.grammars', => @subscribeToBuffer()
@subscribeToBuffer()
subscribeToBuffer: ->
@destroyViews()
@task?.abort()
return unless @spellCheckCurrentGrammar()
@buffer?.off '.spell-check'
@buffer = @editor.getBuffer()
@buffer.on 'contents-modified.spell-check', => @updateMisspellings()
@updateMisspellings()
spellCheckCurrentGrammar: ->
grammar = @editor.getGrammar().scopeName
_.contains config.get('spell-check.grammars'), grammar
destroyViews: ->
if @views
view.destroy() for view in @views
@views = []
addView: (misspelling)->
view = new MisspellingView(misspelling, @editor)
@views.push(view)
@append(view)
updateMisspellings: ->
@task?.abort()
callback = (misspellings) =>
@destroyViews()
@addView(misspelling) for misspelling in misspellings
@task = new SpellCheckTask(@buffer.getText(), callback)
@task.start()

View File

@@ -0,0 +1,20 @@
SpellCheckView = require './spell-check-view'
module.exports =
configDefaults:
grammars: [
'text.plain'
'source.gfm'
'text.git-commit'
]
activate: ->
if syntax.grammars.length > 1
@subscribeToEditors()
else
syntax.on 'grammars-loaded', => @subscribeToEditors()
subscribeToEditors: ->
rootView.eachEditor (editor) ->
if editor.attached and not editor.mini
editor.underlayer.append(new SpellCheckView(editor))

View File

@@ -0,0 +1 @@
'main': 'lib/spell-check.coffee'

View File

@@ -0,0 +1,47 @@
RootView = require 'root-view'
describe "Spell check", ->
[editor] = []
beforeEach ->
window.rootView = new RootView
rootView.open('sample.js')
config.set('spell-check.grammars', [])
window.loadPackage('spell-check')
rootView.attachToDom()
editor = rootView.getActiveEditor()
it "decorates all misspelled words", ->
editor.setText("This middle of thiss sentencts has issues.")
config.set('spell-check.grammars', ['source.js'])
waitsFor ->
editor.find('.misspelling').length > 0
runs ->
expect(editor.find('.misspelling').length).toBe 2
typo1StartPosition = editor.pixelPositionForBufferPosition([0, 15])
typo1EndPosition = editor.pixelPositionForBufferPosition([0, 20])
expect(editor.find('.misspelling:eq(0)').position()).toEqual typo1StartPosition
expect(editor.find('.misspelling:eq(0)').width()).toBe typo1EndPosition.left - typo1StartPosition.left
typo2StartPosition = editor.pixelPositionForBufferPosition([0, 21])
typo2EndPosition = editor.pixelPositionForBufferPosition([0, 30])
expect(editor.find('.misspelling:eq(1)').position()).toEqual typo2StartPosition
expect(editor.find('.misspelling:eq(1)').width()).toBe typo2EndPosition.left - typo2StartPosition.left
it "hides decorations when a misspelled word is edited", ->
editor.setText('notaword')
advanceClock(editor.getBuffer().stoppedChangingDelay)
config.set('spell-check.grammars', ['source.js'])
waitsFor ->
editor.find('.misspelling').length > 0
runs ->
expect(editor.find('.misspelling').length).toBe 1
editor.moveCursorToEndOfLine()
editor.insertText('a')
advanceClock(editor.getBuffer().stoppedChangingDelay)
expect(editor.find('.misspelling')).toBeHidden()

View File

@@ -0,0 +1,4 @@
.misspelling {
border-bottom: 1px dashed rgba(250, 128, 114, .5);
position: absolute;
}