From d89e85531431c444566aaecc4a97fd53fac42c2a Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 4 Feb 2016 14:30:35 +0000 Subject: [PATCH] add client-side spelling cache to reduce load on server --- .../web/app/views/project/editor/editor.jade | 1 + .../ide/editor/directives/aceEditor.coffee | 7 +- .../spell-check/SpellCheckManager.coffee | 69 +++++++++++++++---- 3 files changed, 63 insertions(+), 14 deletions(-) diff --git a/services/web/app/views/project/editor/editor.jade b/services/web/app/views/project/editor/editor.jade index 59399e2aae..f79f6e3c19 100644 --- a/services/web/app/views/project/editor/editor.jade +++ b/services/web/app/views/project/editor/editor.jade @@ -20,6 +20,7 @@ div.full-size( keybindings="settings.mode", font-size="settings.fontSize", auto-complete="settings.autoComplete", + spell-check="true", spell-check-language="project.spellCheckLanguage", highlights="onlineUserCursorHighlights[editor.open_doc_id]" show-print-margin="false", diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee index ba9d69be47..596f350812 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee @@ -18,7 +18,7 @@ define [ url = ace.config._moduleUrl(args...) + "?fingerprint=#{window.aceFingerprint}" return url - App.directive "aceEditor", ($timeout, $compile, $rootScope, event_tracking, localStorage) -> + App.directive "aceEditor", ($timeout, $compile, $rootScope, event_tracking, localStorage, $cacheFactory) -> monkeyPatchSearch($rootScope, $compile) return { @@ -29,6 +29,7 @@ define [ fontSize: "=" autoComplete: "=" sharejsDoc: "=" + spellCheck: "=" spellCheckLanguage: "=" highlights: "=" text: "=" @@ -55,7 +56,9 @@ define [ scope.name = attrs.aceEditor autoCompleteManager = new AutoCompleteManager(scope, editor, element) - spellCheckManager = new SpellCheckManager(scope, editor, element) + if scope.spellCheck # only enable spellcheck when explicitly required + spellCheckCache = $cacheFactory("spellCheck-#{scope.name}", {capacity: 1000}) + spellCheckManager = new SpellCheckManager(scope, editor, element, spellCheckCache) undoManager = new UndoManager(scope, editor, element) highlightsManager = new HighlightsManager(scope, editor, element) cursorPositionManager = new CursorPositionManager(scope, editor, element, localStorage) diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/spell-check/SpellCheckManager.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/spell-check/SpellCheckManager.coffee index 68d45ac6e8..95a6519d59 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor/spell-check/SpellCheckManager.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor/spell-check/SpellCheckManager.coffee @@ -5,7 +5,7 @@ define [ Range = ace.require("ace/range").Range class SpellCheckManager - constructor: (@$scope, @editor, @element) -> + constructor: (@$scope, @editor, @element, @cache) -> $(document.body).append @element.find(".spell-check-menu") @updatedLines = [] @@ -102,6 +102,8 @@ define [ learnWord: (highlight) -> @apiRequest "/learn", word: highlight.word @highlightedWordManager.removeWord highlight.word + language = @$scope.spellCheckLanguage + @cache?.put("#{language}:#{highlight.word}", true) getHighlightedWordAtCursor: () -> cursor = @editor.getCursorPosition() @@ -143,24 +145,67 @@ define [ runSpellCheck: (linesToProcess) -> {words, positions} = @getWords(linesToProcess) language = @$scope.spellCheckLanguage - @apiRequest "/check", {language: language, words: words}, (error, result) => - if error? or !result? or !result.misspellings? - return null + highlights = [] + seen = {} + newWords = [] + newPositions = [] + + # iterate through all words, building up a list of + # newWords/newPositions not in the cache + for word, i in words + key = "#{language}:#{word}" + seen[key] ?= @cache.get(key) # avoid hitting the cache unnecessarily + cached = seen[key] + if not cached? + newWords.push words[i] + newPositions.push positions[i] + else if cached is true + # word is correct + else + highlights.push + column: positions[i].column + row: positions[i].row + word: word + suggestions: cached + words = newWords + positions = newPositions + + displayResult = (highlights) => if linesToProcess? for shouldProcess, row in linesToProcess @highlightedWordManager.clearRows(row, row) if shouldProcess else @highlightedWordManager.clearRows() + for highlight in highlights + @highlightedWordManager.addHighlight highlight - for misspelling in result.misspellings - word = words[misspelling.index] - position = positions[misspelling.index] - @highlightedWordManager.addHighlight - column: position.column - row: position.row - word: word - suggestions: misspelling.suggestions + if not words.length + displayResult highlights + else + @apiRequest "/check", {language: language, words: words}, (error, result) => + if error? or !result? or !result.misspellings? + return null + mispelled = [] + for misspelling in result.misspellings + word = words[misspelling.index] + position = positions[misspelling.index] + mispelled[misspelling.index] = true + highlights.push + column: position.column + row: position.row + word: word + suggestions: misspelling.suggestions + key = "#{language}:#{word}" + if not seen[key] + @cache.put key, misspelling.suggestions + seen[key] = true + for word, i in words when not mispelled[i] + key = "#{language}:#{word}" + if not seen[key] + @cache.put(key, true) + seen[key] = true + displayResult highlights getWords: (linesToProcess) -> lines = @editor.getValue().split("\n")