mirror of
https://github.com/atom/atom.git
synced 2026-04-06 03:02:13 -04:00
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:
40
src/packages/spell-check/lib/misspelling-view.coffee
Normal file
40
src/packages/spell-check/lib/misspelling-view.coffee
Normal 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()
|
||||
14
src/packages/spell-check/lib/spell-check-handler.coffee
Normal file
14
src/packages/spell-check/lib/spell-check-handler.coffee
Normal 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)
|
||||
14
src/packages/spell-check/lib/spell-check-task.coffee
Normal file
14
src/packages/spell-check/lib/spell-check-task.coffee
Normal 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)
|
||||
53
src/packages/spell-check/lib/spell-check-view.coffee
Normal file
53
src/packages/spell-check/lib/spell-check-view.coffee
Normal 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()
|
||||
20
src/packages/spell-check/lib/spell-check.coffee
Normal file
20
src/packages/spell-check/lib/spell-check.coffee
Normal 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))
|
||||
1
src/packages/spell-check/package.cson
Normal file
1
src/packages/spell-check/package.cson
Normal file
@@ -0,0 +1 @@
|
||||
'main': 'lib/spell-check.coffee'
|
||||
47
src/packages/spell-check/spec/spell-check-spec.coffee
Normal file
47
src/packages/spell-check/spec/spell-check-spec.coffee
Normal 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()
|
||||
4
src/packages/spell-check/stylesheets/spell-check.css
Normal file
4
src/packages/spell-check/stylesheets/spell-check.css
Normal file
@@ -0,0 +1,4 @@
|
||||
.misspelling {
|
||||
border-bottom: 1px dashed rgba(250, 128, 114, .5);
|
||||
position: absolute;
|
||||
}
|
||||
Reference in New Issue
Block a user