From 961e9cc508be154a85bf9c4e4cb84f0de17dd8f5 Mon Sep 17 00:00:00 2001
From: David Gileadi
Date: Wed, 8 Nov 2017 15:08:19 -0700
Subject: [PATCH 01/10] Create an initial spellcheck implementation
---
BUILD_FOR_MAC.command | 2 +-
BUILD_FOR_WINDOWS.bat | 1 +
BUILD_PACKAGE.command | 2 +-
INSTALL_AND_RUN.command | 2 +-
app/package.json | 4 +-
app/renderer/contextmenu.js | 49 ++++++++++++---
app/renderer/controller.js | 5 +-
app/renderer/editorView.js | 25 ++++----
app/renderer/main.css | 8 ++-
app/renderer/spellChecker.js | 118 +++++++++++++++++++++++++++++++++++
10 files changed, 191 insertions(+), 25 deletions(-)
create mode 100644 app/renderer/spellChecker.js
diff --git a/BUILD_FOR_MAC.command b/BUILD_FOR_MAC.command
index 8d927506..4a7a251a 100755
--- a/BUILD_FOR_MAC.command
+++ b/BUILD_FOR_MAC.command
@@ -11,7 +11,7 @@ rm -rf Inky-darwin-x64/
rm ReleaseUpload/Inky_mac.dmg
# Ensure it's correctly/fully installed first
-( cd app && npm install )
+( cd app && npm install && ./node_modules/.bin/electron-rebuild )
# Create icon from PNG
./resources/makeIcns.command
diff --git a/BUILD_FOR_WINDOWS.bat b/BUILD_FOR_WINDOWS.bat
index 6dc105c8..51370ef2 100644
--- a/BUILD_FOR_WINDOWS.bat
+++ b/BUILD_FOR_WINDOWS.bat
@@ -1,6 +1,7 @@
# Ensure it's correctly/fully installed first
cd app
npm install
+.\node_modules\.bin\electron-rebuild.cmd
cd ..
electron-packager app Inky --platform=win32 --arch=x64 --icon=resources/Icon1024.png.ico --prune --asar.unpackDir="main-process/ink" --ignore="inklecate_mac" --win32metadata.ProductName="Inky" --win32metadata.CompanyName="inkle Ltd" --win32metadata.FileDescription="Inky" --win32metadata.OriginalFilename="Inky" --win32metadata.InternalName="Inky"
\ No newline at end of file
diff --git a/BUILD_PACKAGE.command b/BUILD_PACKAGE.command
index 399b8aaa..8bdc25d3 100755
--- a/BUILD_PACKAGE.command
+++ b/BUILD_PACKAGE.command
@@ -17,7 +17,7 @@ rm -rf ReleaseUpload
./resources/makeIcns.command
# Ensure it's correctly/fully installed first
-( cd app && npm install )
+( cd app && npm install && ./node_modules/.bin/electron-rebuild )
# Mac
electron-packager app Inky --platform=darwin --arch=x64 --icon=resources/Icon.icns --extend-info=resources/info.plist --app-bundle-id=com.inkle.inky --prune --asar.unpackDir="main-process/ink" --ignore="inklecate_win.exe"
diff --git a/INSTALL_AND_RUN.command b/INSTALL_AND_RUN.command
index a3e16fd3..e7f3dbbe 100755
--- a/INSTALL_AND_RUN.command
+++ b/INSTALL_AND_RUN.command
@@ -1,3 +1,3 @@
cd "`dirname "$0"`"
cd app
-npm install && npm start
\ No newline at end of file
+npm install && ./node_modules/.bin/electron-rebuild && npm start
\ No newline at end of file
diff --git a/app/package.json b/app/package.json
index 04c0d0a3..5c2293b9 100644
--- a/app/package.json
+++ b/app/package.json
@@ -28,6 +28,7 @@
"chai": "^4.1.2",
"chai-as-promised": "^7.1.1",
"electron": "^1.4.3",
+ "electron-rebuild": "^1.6.0",
"markdown-html": "^0.0.8",
"mocha": "^4.0.1",
"spectron": "^3.2.6"
@@ -38,6 +39,7 @@
"inkjs": "^1.6.0",
"lodash": "^4.13.1",
"mkdirp": "^0.5.1",
- "randomstring": "^1.1.5"
+ "randomstring": "^1.1.5",
+ "spellchecker": "^3.4.4"
}
}
diff --git a/app/renderer/contextmenu.js b/app/renderer/contextmenu.js
index 6aa47652..cbe857ca 100644
--- a/app/renderer/contextmenu.js
+++ b/app/renderer/contextmenu.js
@@ -1,13 +1,48 @@
const {remote} = require('electron')
const {Menu, MenuItem} = remote
+const SpellChecker = require("./spellChecker.js")
-const menu = new Menu()
-menu.append(new MenuItem({ role: 'cut' }))
-menu.append(new MenuItem({ role: 'copy' }))
-menu.append(new MenuItem({ role: 'paste' }))
-menu.append(new MenuItem({ role: 'selectall' }))
+const playerViewMenu = new Menu()
+playerViewMenu.append(new MenuItem({ role: 'copy' }))
+playerViewMenu.append(new MenuItem({ role: 'selectall' }))
window.addEventListener('contextmenu', (e) => {
e.preventDefault()
- menu.popup(remote.getCurrentWindow())
-}, false);
\ No newline at end of file
+
+ const editor = ace.edit("editor")
+ if (e.target == editor.textInput.getElement()) {
+ var template = [
+ { role: 'cut' },
+ { role: 'copy' },
+ { role: 'paste' },
+ { role: 'selectall' }
+ ];
+
+ template = withSpellingSuggestions(template, editor)
+
+ const menu = Menu.buildFromTemplate(template)
+ menu.popup(remote.getCurrentWindow())
+ } else {
+ playerViewMenu.popup(remote.getCurrentWindow())
+ }
+}, false);
+
+function withSpellingSuggestions(template, editor) {
+ const pos = editor.getCursorPosition()
+ var suggestions = SpellChecker.getSuggestions(pos)
+ if (suggestions instanceof Array) {
+ suggestions = suggestions.map(suggestion => {
+ return {
+ label: suggestion.word,
+ click() {
+ editor.getSession().getDocument().replace(suggestion.where, suggestion.word)
+ }
+ }
+ })
+ if (!suggestions.length) {
+ suggestions = [{ label: 'No Guesses Found' }]
+ }
+ template = suggestions.slice(0, 3).concat({ type: 'separator' }, template)
+ }
+ return template
+}
diff --git a/app/renderer/controller.js b/app/renderer/controller.js
index d43fdea8..5b50d8cb 100644
--- a/app/renderer/controller.js
+++ b/app/renderer/controller.js
@@ -25,6 +25,7 @@ const LiveCompiler = require("./liveCompiler.js").LiveCompiler;
const InkProject = require("./inkProject.js").InkProject;
const NavHistory = require("./navHistory.js").NavHistory;
const GotoAnything = require("./goto.js").GotoAnything;
+const SpellChecker = require("./spellChecker.js");
InkProject.setEvents({
"newProject": (project) => {
@@ -37,6 +38,7 @@ InkProject.setEvents({
NavView.setMainInkFilename(filename);
NavHistory.reset();
NavHistory.addStep();
+ SpellChecker.spellCheck(undefined, 0);
},
"didSave": () => {
var activeInk = InkProject.currentProject.activeInkFile;
@@ -171,8 +173,9 @@ LiveCompiler.setEvents({
});
EditorView.setEvents({
- "change": () => {
+ "change": (e) => {
LiveCompiler.setEdited();
+ SpellChecker.spellCheck(e);
},
"jumpToSymbol": (symbolName, contextPos) => {
var foundSymbol = InkProject.currentProject.findSymbol(symbolName, contextPos);
diff --git a/app/renderer/editorView.js b/app/renderer/editorView.js
index e08bf090..7adad8aa 100644
--- a/app/renderer/editorView.js
+++ b/app/renderer/editorView.js
@@ -16,7 +16,8 @@ var savedScrollRow = null;
var events = {
change: () => {},
jumpToInclude: () => {},
- jumpToSymbol: () => {}
+ jumpToSymbol: () => {},
+ navigate: () => {}
};
editor.setShowPrintMargin(false);
@@ -24,8 +25,8 @@ editor.setOptions({
enableBasicAutocompletion: true,
enableLiveAutocompletion: true,
});
-editor.on("change", () => {
- events.change();
+editor.on("change", (e) => {
+ events.change(e);
});
// Exclude language_tools.textCompleter but add the Ink completer
@@ -33,7 +34,7 @@ editor.completers = editor.completers.filter(
(completer) => completer !== language_tools.textCompleter);
editor.completers.push(inkCompleter);
-// Unfortunately standard jquery events don't work since
+// Unfortunately standard jquery events don't work since
// Ace turns pointer events off
editor.on("click", function(e){
@@ -89,7 +90,7 @@ editor.on("mousemove", function (e) {
}
}
}
-
+
editor.renderer.setCursorStyle("default");
});
@@ -117,7 +118,7 @@ function addError(error) {
var aceClass = "ace-error";
var markerId = editor.session.addMarker(
new Range(error.lineNumber-1, 0, error.lineNumber, 0),
- editorClass,
+ editorClass,
"line",
false
);
@@ -157,14 +158,14 @@ exports.EditorView = {
editor.focus();
},
focus: () => { editor.focus(); },
- saveCursorPos: () => {
- savedCursorPos = editor.getCursorPosition();
- savedScrollRow = editor.getFirstVisibleRow();
+ saveCursorPos: () => {
+ savedCursorPos = editor.getCursorPosition();
+ savedScrollRow = editor.getFirstVisibleRow();
},
- restoreCursorPos: () => {
+ restoreCursorPos: () => {
if( savedCursorPos ) {
- editor.moveCursorToPosition(savedCursorPos);
+ editor.moveCursorToPosition(savedCursorPos);
editor.scrollToRow(savedScrollRow);
- }
+ }
}
};
\ No newline at end of file
diff --git a/app/renderer/main.css b/app/renderer/main.css
index d71720c5..cd230f72 100644
--- a/app/renderer/main.css
+++ b/app/renderer/main.css
@@ -536,6 +536,12 @@ img.issue-icon.warning {
background: #bffff1;
}
+#editor .ace-misspelled {
+ border-bottom: dotted 2px red;
+ margin-bottom: -1px;
+ position:absolute;
+}
+
#editor .ace_gutter-layer {
background: white;
border-right: none;
@@ -731,7 +737,7 @@ img.issue-icon.warning {
}
#player td.expressionInput:focus {
- outline: none;
+ outline: none;
}
#player .evaluationResult {
diff --git a/app/renderer/spellChecker.js b/app/renderer/spellChecker.js
new file mode 100644
index 00000000..e61e4621
--- /dev/null
+++ b/app/renderer/spellChecker.js
@@ -0,0 +1,118 @@
+const SpellChecker = require("spellchecker");
+const Range = ace.require('ace/range').Range;
+
+const defaultDelay = 500;
+
+var range;
+var previousCursor;
+var spellcheckTimerID;
+var markers = {};
+
+exports.spellCheck = function (scope, delay) {
+ if (spellcheckTimerID) {
+ clearTimeout(spellcheckTimerID);
+ }
+
+ if (range) {
+ delay = Math.max(delay, defaultDelay);
+ }
+ setRange(scope);
+
+ spellcheckTimerID = setTimeout(doSpellCheck, delay);
+}
+
+function setRange(scope) {
+ const document = ace.edit("editor").getSession().getDocument();
+
+ if (scope && scope.start && scope.end) {
+ if (range) {
+ range = range.extend(scope.start.row, scope.start.column);
+ range = range.extend(scope.end.row, scope.end.column);
+ } else {
+ range = new Range(scope.start.row, scope.start.column, scope.end.row, scope.end.column);
+ }
+ } else if (!range) {
+ const lastRow = document.getLength() - 1;
+ const lastColumn = Math.max(0, document.getLine(lastRow).length - 1);
+ range = new Range(0, 0, lastRow, lastColumn);
+ }
+
+ while (range.end.row > 0 && range.end.column === 0) {
+ const endRow = range.end.row - 1;
+ range.setEnd(endRow, Math.max(0, document.getLine(endRow).length - 1));
+ }
+}
+
+function doSpellCheck() {
+ try {
+ const editor = ace.edit("editor");
+ const session = editor.getSession();
+ const cursor = editor.getCursorPosition();
+ const lines = session.getDocument().getLines(range.start.row, range.end.row);
+ var promises = [];
+ for (var i in lines) {
+ if (isNaN(Number(i))) {
+ continue;
+ }
+
+ const row = range.start.row + Number(i);
+
+ // remove any previous markers for this row
+ for (var markerID in markers) {
+ if (markers[markerID].where.start.row === row) {
+ session.removeMarker(markerID);
+ delete markers[markerID];
+ }
+ }
+
+ const line = lines[i];
+
+ promises.push(SpellChecker.checkSpellingAsync(line).then(misspellings => {
+ for (var j = 0; j < misspellings.length; j++) {
+ var where = new Range(row, misspellings[j].start, row, misspellings[j].end);
+
+ // don't mark in-progress words as misspelled
+ if (isTypingWord(cursor, where)) {
+ continue;
+ }
+
+ // anchor the marker in the document
+ where.start = session.doc.createAnchor(where.start);
+ where.end = session.doc.createAnchor(where.end);
+
+ var markerID = session.addMarker(where, "ace-misspelled", "typo", false);
+ const word = line.substring(misspellings[j].start, misspellings[j].end);
+ markers[markerID] = { where: where, word: word };
+ }
+ }));
+
+ Promise.all(promises).then(_ => previousCursor = cursor);
+ }
+ } finally {
+ range = undefined;
+ spellcheckTimerID = undefined;
+ }
+}
+
+function isTypingWord(cursor, where) {
+ return where.isEnd(cursor.row, cursor.column) &&
+ (!previousCursor ||
+ (previousCursor.row === cursor.row && previousCursor.column === cursor.column - 1));
+}
+
+exports.getSuggestions = function(pos) {
+ var suggestions;
+ for (var markerID in markers) {
+ if (markers[markerID].where.contains(pos.row, pos.column)) {
+ suggestions = SpellChecker.getCorrectionsForMisspelling(markers[markerID].word);
+ suggestions = suggestions.map(word => {
+ return {
+ word: word,
+ where: markers[markerID].where
+ }
+ });
+ break;
+ }
+ }
+ return suggestions;
+}
\ No newline at end of file
From 4fd816373b9efeab317f1ad35e7d5d29f088710f Mon Sep 17 00:00:00 2001
From: David Gileadi
Date: Wed, 8 Nov 2017 17:29:35 -0700
Subject: [PATCH 02/10] Only spell check text that's on the screen
---
app/renderer/spellChecker.js | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/app/renderer/spellChecker.js b/app/renderer/spellChecker.js
index e61e4621..74b95714 100644
--- a/app/renderer/spellChecker.js
+++ b/app/renderer/spellChecker.js
@@ -2,17 +2,28 @@ const SpellChecker = require("spellchecker");
const Range = ace.require('ace/range').Range;
const defaultDelay = 500;
+const scrollCheckDelay = 2000;
var range;
var previousCursor;
+var previousVisibleRow;
var spellcheckTimerID;
var markers = {};
+setInterval(() => {
+ const firstVisibleRow = ace.edit("editor").getFirstVisibleRow();
+ if (firstVisibleRow !== previousVisibleRow) {
+ exports.spellCheck();
+ }
+ previousVisibleRow = firstVisibleRow;
+}, scrollCheckDelay);
+
exports.spellCheck = function (scope, delay) {
if (spellcheckTimerID) {
clearTimeout(spellcheckTimerID);
}
+ delay = delay || defaultDelay;
if (range) {
delay = Math.max(delay, defaultDelay);
}
@@ -22,7 +33,8 @@ exports.spellCheck = function (scope, delay) {
}
function setRange(scope) {
- const document = ace.edit("editor").getSession().getDocument();
+ const editor = ace.edit("editor");
+ const document = editor.getSession().getDocument();
if (scope && scope.start && scope.end) {
if (range) {
@@ -37,6 +49,8 @@ function setRange(scope) {
range = new Range(0, 0, lastRow, lastColumn);
}
+ range = range.clipRows(editor.getFirstVisibleRow(), editor.getLastVisibleRow());
+
while (range.end.row > 0 && range.end.column === 0) {
const endRow = range.end.row - 1;
range.setEnd(endRow, Math.max(0, document.getLine(endRow).length - 1));
From da02701b480290709a22f73563b96da22720717e Mon Sep 17 00:00:00 2001
From: David Gileadi
Date: Wed, 8 Nov 2017 17:57:14 -0700
Subject: [PATCH 03/10] Only spellcheck text
---
app/renderer/spellChecker.js | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/app/renderer/spellChecker.js b/app/renderer/spellChecker.js
index 74b95714..ff5c7785 100644
--- a/app/renderer/spellChecker.js
+++ b/app/renderer/spellChecker.js
@@ -3,6 +3,18 @@ const Range = ace.require('ace/range').Range;
const defaultDelay = 500;
const scrollCheckDelay = 2000;
+const checkableTypes = [
+ "text",
+ "choice.weaveInsideBrackets",
+ "todo",
+ "comment.block.json",
+ "comment.block.documentation.json",
+ "comment.line.double-slash.js",
+ "list-decl.item",
+ "logic.inline.innerContent",
+ "logic.sequence.innerContent",
+ "logic.multiline.innerContent"
+];
var range;
var previousCursor;
@@ -90,6 +102,12 @@ function doSpellCheck() {
continue;
}
+ // only check text
+ const cursorToken = session.getTokenAt(where.start.row, where.start.column + 1);
+ if (!cursorToken || checkableTypes.indexOf(cursorToken.type) === -1) {
+ continue;
+ }
+
// anchor the marker in the document
where.start = session.doc.createAnchor(where.start);
where.end = session.doc.createAnchor(where.end);
From 1796aae8c9da597504356c74484cd6b507f2d273 Mon Sep 17 00:00:00 2001
From: David Gileadi
Date: Thu, 9 Nov 2017 11:32:50 -0700
Subject: [PATCH 04/10] Add support for: - Ignore Spelling/Learn Spelling -
Educating hyphens and dashes - Enabling/disabling spelling and quotes/dashes
- Saving that enabled state between app launches
---
app/main-process/appmenus.js | 22 ++++++++++-
app/main-process/main.js | 6 +++
app/package.json | 1 +
app/renderer/contextmenu.js | 14 +++++--
app/renderer/controller.js | 13 ++++++-
app/renderer/preferences.js | 20 ++++++++++
app/renderer/quotesAndDashes.js | 66 +++++++++++++++++++++++++++++++++
app/renderer/spellChecker.js | 60 +++++++++++++++++++++++++++++-
8 files changed, 192 insertions(+), 10 deletions(-)
create mode 100644 app/renderer/preferences.js
create mode 100644 app/renderer/quotesAndDashes.js
diff --git a/app/main-process/appmenus.js b/app/main-process/appmenus.js
index 55afc9cc..eea71a94 100644
--- a/app/main-process/appmenus.js
+++ b/app/main-process/appmenus.js
@@ -4,6 +4,7 @@ const ipc = electron.ipcMain;
const dialog = electron.dialog;
const _ = require("lodash");
const Menu = electron.Menu;
+const preferences = require("../renderer/preferences.js");
function setupMenus(callbacks) {
const template = [
@@ -102,6 +103,23 @@ function setupMenus(callbacks) {
accelerator: 'CmdOrCtrl+A',
role: 'selectall'
},
+ {
+ type: 'separator'
+ },
+ {
+ label: 'Spell Checking',
+ type: "checkbox",
+ checked: preferences.checkSpelling,
+ click: callbacks.toggleSpelling
+ },
+ {
+ label: 'Smart Quotes && Dashes',
+ type: "checkbox",
+ checked: preferences.smartQuotesAndDashes,
+ click(item) {
+ preferences.smartQuotesAndDashes = item.checked;
+ }
+ },
]
},
{
@@ -140,7 +158,7 @@ function setupMenus(callbacks) {
{
label: 'Tags visible',
type: "checkbox",
- checked: true,
+ checked: preferences.tagsVisible,
click: callbacks.toggleTags
},
{
@@ -260,7 +278,7 @@ function setupMenus(callbacks) {
},
]
});
-
+
var windowMenu = _.find(template, menu => menu.role == "window");
windowMenu.submenu.push(
{
diff --git a/app/main-process/main.js b/app/main-process/main.js
index 00888a2d..43542eff 100644
--- a/app/main-process/main.js
+++ b/app/main-process/main.js
@@ -2,6 +2,7 @@ const electron = require('electron')
const app = electron.app
const ipc = electron.ipcMain;
const dialog = electron.dialog;
+const preferences = require("../renderer/preferences.js");
const ProjectWindow = require("./projectWindow.js").ProjectWindow;
const DocumentationWindow = require("./documentationWindow.js").DocumentationWindow;
const AboutWindow = require("./aboutWindow.js").AboutWindow;
@@ -81,8 +82,13 @@ app.on('ready', function () {
if (win) win.exportJSOnly();
},
toggleTags: (item, focusedWindow, event) => {
+ preferences.tagsVisible = item.checked;
focusedWindow.webContents.send("set-tags-visible", item.checked);
},
+ toggleSpelling: (item, focusedWindow) => {
+ preferences.checkSpelling = item.checked;
+ focusedWindow.webContents.send("toggle-spell-check");
+ },
nextIssue: (item, focusedWindow) => {
focusedWindow.webContents.send("next-issue");
},
diff --git a/app/package.json b/app/package.json
index 5c2293b9..1a358b09 100644
--- a/app/package.json
+++ b/app/package.json
@@ -36,6 +36,7 @@
"dependencies": {
"chokidar": "^1.6.0",
"fuzzaldrin-plus": "^0.5",
+ "electron-settings": "^3.1.4",
"inkjs": "^1.6.0",
"lodash": "^4.13.1",
"mkdirp": "^0.5.1",
diff --git a/app/renderer/contextmenu.js b/app/renderer/contextmenu.js
index cbe857ca..6d254b46 100644
--- a/app/renderer/contextmenu.js
+++ b/app/renderer/contextmenu.js
@@ -1,6 +1,8 @@
const {remote} = require('electron')
const {Menu, MenuItem} = remote
-const SpellChecker = require("./spellChecker.js")
+const SpellChecker = require('./spellChecker.js')
+
+const maxSpellingSuggestions = 4;
const playerViewMenu = new Menu()
playerViewMenu.append(new MenuItem({ role: 'copy' }))
@@ -31,7 +33,7 @@ function withSpellingSuggestions(template, editor) {
const pos = editor.getCursorPosition()
var suggestions = SpellChecker.getSuggestions(pos)
if (suggestions instanceof Array) {
- suggestions = suggestions.map(suggestion => {
+ suggestions = suggestions.slice(0, maxSpellingSuggestions).map(suggestion => {
return {
label: suggestion.word,
click() {
@@ -39,10 +41,14 @@ function withSpellingSuggestions(template, editor) {
}
}
})
- if (!suggestions.length) {
+ if (suggestions.length) {
+ suggestions.push({ type: 'separator' });
+ suggestions.push({ label: 'Ignore Spelling', click: () => SpellChecker.ignoreWordAt(pos) });
+ suggestions.push({ label: 'Learn Spelling', click: () => SpellChecker.learnWordAt(pos) });
+ } else {
suggestions = [{ label: 'No Guesses Found' }]
}
- template = suggestions.slice(0, 3).concat({ type: 'separator' }, template)
+ template = suggestions.concat({ type: 'separator' }, template)
}
return template
}
diff --git a/app/renderer/controller.js b/app/renderer/controller.js
index 5b50d8cb..43305a06 100644
--- a/app/renderer/controller.js
+++ b/app/renderer/controller.js
@@ -3,6 +3,7 @@ const ipc = electron.ipcRenderer;
const remote = electron.remote;
const path = require("path");
const $ = window.jQuery = require('./jquery-2.2.3.min.js');
+const preferences = require('./preferences.js')
// Debug
const loadTestInk = false;
@@ -26,6 +27,7 @@ const InkProject = require("./inkProject.js").InkProject;
const NavHistory = require("./navHistory.js").NavHistory;
const GotoAnything = require("./goto.js").GotoAnything;
const SpellChecker = require("./spellChecker.js");
+const QuotesAndDashes = require("./quotesAndDashes.js");
InkProject.setEvents({
"newProject": (project) => {
@@ -68,6 +70,8 @@ $(document).ready(() => {
var testInk = require("fs").readFileSync(path.join(__dirname, "test.ink"), "utf8");
InkProject.currentProject.mainInk.setValue(testInk);
}
+
+ setTagsVisible(preferences.tagsVisible);
}
});
@@ -176,6 +180,7 @@ EditorView.setEvents({
"change": (e) => {
LiveCompiler.setEdited();
SpellChecker.spellCheck(e);
+ QuotesAndDashes.smarten(e);
},
"jumpToSymbol": (symbolName, contextPos) => {
var foundSymbol = InkProject.currentProject.findSymbol(symbolName, contextPos);
@@ -249,8 +254,12 @@ GotoAnything.setEvents({
});
ipc.on("set-tags-visible", (event, visible) => {
- if( visible )
+ setTagsVisible(visible);
+});
+
+function setTagsVisible(visible) {
+ if (visible)
$("#main").removeClass("hideTags");
else
$("#main").addClass("hideTags");
-});
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/app/renderer/preferences.js b/app/renderer/preferences.js
new file mode 100644
index 00000000..8b7ebd46
--- /dev/null
+++ b/app/renderer/preferences.js
@@ -0,0 +1,20 @@
+const settings = require('electron-settings');
+
+const tagsVisibleKey = 'story.tagsVisible';
+const smartQuotesAndDashesKey = 'editor.smartQuotesAndDashes';
+const checkSpellingKey = 'editor.spelling.check';
+
+Object.defineProperty(exports, 'tagsVisible', {
+ get: function () { return settings.get(tagsVisibleKey, true); },
+ set: function (value) { settings.set(tagsVisibleKey, value); }
+});
+
+Object.defineProperty(exports, 'smartQuotesAndDashes', {
+ get: function () { return settings.get(smartQuotesAndDashesKey, true); },
+ set: function (value) { settings.set(smartQuotesAndDashesKey, value); }
+});
+
+Object.defineProperty(exports, 'checkSpelling', {
+ get: function () { return settings.get(checkSpellingKey, true); },
+ set: function (value) { settings.set(checkSpellingKey, value); }
+});
diff --git a/app/renderer/quotesAndDashes.js b/app/renderer/quotesAndDashes.js
new file mode 100644
index 00000000..d9e550bf
--- /dev/null
+++ b/app/renderer/quotesAndDashes.js
@@ -0,0 +1,66 @@
+const Range = ace.require('ace/range').Range;
+const preferences = require("./preferences.js");
+const checkableTypes = require("./spellChecker.js").checkableTypes;
+
+// TODO: support non en-US quotes?
+
+exports.smarten = function (e) {
+ // only continue if enabled and exactly one character or newline was added
+ if (!preferences.smartQuotesAndDashes ||
+ e.action !== 'insert' ||
+ e.lines.length > 2 ||
+ (e.lines.length === 2 && e.lines[0].length !== 0 && e.lines[1].length !== 0) ||
+ (e.lines.length === 1 && e.lines[0].length !== 1)) {
+ return;
+ }
+
+ // ...and only continue if we're editing text
+ const session = ace.edit("editor").getSession();
+ const cursorToken = session.getTokenAt(e.end.row, e.end.column);
+ if (cursorToken && checkableTypes.indexOf(cursorToken.type) === -1) {
+ return;
+ }
+
+ const document = session.getDocument();
+ var range = new Range(e.start.row, e.start.column, e.end.row, e.end.column);
+ const prevRange = new Range(e.start.row, e.start.column - 1, e.start.row, e.start.column);
+ const char = document.getTextRange(range);
+ const prevChar = prevRange.start.column < 0 ? '' : document.getTextRange(prevRange);
+
+ if (prevChar === '-' && char !== '-') {
+ document.replace(prevRange, '–'); // en dash
+ } else if (prevChar === '.' && prevRange.start.column > 1) {
+ const ellipsisRange = range.extend(prevRange.start.row, prevRange.start.column - 2);
+ if (document.getTextRange(ellipsisRange) === '...' + char) {
+ document.replace(ellipsisRange, '…' + char);
+ range = new Range(ellipsisRange.start.row, ellipsisRange.start.column + 1, ellipsisRange.start.row, ellipsisRange.start.column + 2);
+ }
+ }
+
+ switch (char) {
+ case "'":
+ if (prevChar === ' ' || prevChar === '') {
+ document.replace(range, '‘');
+ } else {
+ document.replace(range, '’');
+ }
+ break;
+ case '"':
+ if (prevChar === ' ' || prevChar === '') {
+ document.replace(range, '“');
+ } else {
+ document.replace(range, '”');
+ }
+ break;
+ case '-':
+ if (prevChar === '-') {
+ range = range.extend(range.start.row, prevRange.start.column);
+ document.replace(range, '—'); // em dash
+ }
+ break;
+ }
+}
+
+function replace(document, range, replacement) {
+ document.replace(suggestion.where, suggestion.word)
+}
\ No newline at end of file
diff --git a/app/renderer/spellChecker.js b/app/renderer/spellChecker.js
index ff5c7785..6d04e322 100644
--- a/app/renderer/spellChecker.js
+++ b/app/renderer/spellChecker.js
@@ -1,5 +1,7 @@
+const ipc = require("electron").ipcRenderer;
const SpellChecker = require("spellchecker");
const Range = ace.require('ace/range').Range;
+const preferences = require('./preferences.js');
const defaultDelay = 500;
const scrollCheckDelay = 2000;
@@ -15,12 +17,14 @@ const checkableTypes = [
"logic.sequence.innerContent",
"logic.multiline.innerContent"
];
+exports.checkableTypes = checkableTypes;
var range;
var previousCursor;
var previousVisibleRow;
var spellcheckTimerID;
var markers = {};
+var ignoredWords = [];
setInterval(() => {
const firstVisibleRow = ace.edit("editor").getFirstVisibleRow();
@@ -30,7 +34,23 @@ setInterval(() => {
previousVisibleRow = firstVisibleRow;
}, scrollCheckDelay);
+ipc.on("toggle-spell-check", () => {
+ if (preferences.checkSpelling) {
+ exports.spellCheck(null, 0);
+ } else {
+ const session = ace.edit("editor").getSession();
+ for (var markerID in markers) {
+ session.removeMarker(markerID);
+ }
+ markers = {};
+ }
+});
+
exports.spellCheck = function (scope, delay) {
+ if (!preferences.checkSpelling) {
+ return;
+ }
+
if (spellcheckTimerID) {
clearTimeout(spellcheckTimerID);
}
@@ -108,12 +128,16 @@ function doSpellCheck() {
continue;
}
+ const word = line.substring(misspellings[j].start, misspellings[j].end);
+ if (ignoredWords.indexOf(word) !== -1) {
+ continue;
+ }
+
// anchor the marker in the document
where.start = session.doc.createAnchor(where.start);
where.end = session.doc.createAnchor(where.end);
var markerID = session.addMarker(where, "ace-misspelled", "typo", false);
- const word = line.substring(misspellings[j].start, misspellings[j].end);
markers[markerID] = { where: where, word: word };
}
}));
@@ -132,7 +156,11 @@ function isTypingWord(cursor, where) {
(previousCursor.row === cursor.row && previousCursor.column === cursor.column - 1));
}
-exports.getSuggestions = function(pos) {
+exports.getSuggestions = function (pos) {
+ if (!preferences.checkSpelling) {
+ return;
+ }
+
var suggestions;
for (var markerID in markers) {
if (markers[markerID].where.contains(pos.row, pos.column)) {
@@ -147,4 +175,32 @@ exports.getSuggestions = function(pos) {
}
}
return suggestions;
+}
+
+exports.ignoreWordAt = function (pos) {
+ const word = getWordAt(pos);
+ ignoredWords.push(word);
+ removeMarkersForWord(word);
+}
+
+exports.learnWordAt = function (pos) {
+ const word = getWordAt(pos);
+ SpellChecker.add(word);
+ removeMarkersForWord(word);
+}
+
+function getWordAt(pos) {
+ const session = ace.edit("editor").getSession();
+ const range = session.getWordRange(pos.row, pos.column);
+ return session.getDocument().getTextRange(range);
+}
+
+function removeMarkersForWord(word) {
+ const session = ace.edit("editor").getSession();
+ for (var markerID in markers) {
+ if (markers[markerID].word === word) {
+ session.removeMarker(markerID);
+ delete markers[markerID];
+ }
+ }
}
\ No newline at end of file
From bb6257ec9f44bffdd117151a7c2844edd7816a8e Mon Sep 17 00:00:00 2001
From: David Gileadi
Date: Thu, 9 Nov 2017 17:05:33 -0700
Subject: [PATCH 05/10] Avoid reentry and stepping forward when quoting an
existing word
---
app/renderer/quotesAndDashes.js | 89 +++++++++++++++++++--------------
1 file changed, 52 insertions(+), 37 deletions(-)
diff --git a/app/renderer/quotesAndDashes.js b/app/renderer/quotesAndDashes.js
index d9e550bf..549df7c6 100644
--- a/app/renderer/quotesAndDashes.js
+++ b/app/renderer/quotesAndDashes.js
@@ -4,9 +4,12 @@ const checkableTypes = require("./spellChecker.js").checkableTypes;
// TODO: support non en-US quotes?
+var smartening = false;
+
exports.smarten = function (e) {
// only continue if enabled and exactly one character or newline was added
- if (!preferences.smartQuotesAndDashes ||
+ if (smartening ||
+ !preferences.smartQuotesAndDashes ||
e.action !== 'insert' ||
e.lines.length > 2 ||
(e.lines.length === 2 && e.lines[0].length !== 0 && e.lines[1].length !== 0) ||
@@ -21,46 +24,58 @@ exports.smarten = function (e) {
return;
}
- const document = session.getDocument();
- var range = new Range(e.start.row, e.start.column, e.end.row, e.end.column);
- const prevRange = new Range(e.start.row, e.start.column - 1, e.start.row, e.start.column);
- const char = document.getTextRange(range);
- const prevChar = prevRange.start.column < 0 ? '' : document.getTextRange(prevRange);
+ smartening = true;
- if (prevChar === '-' && char !== '-') {
- document.replace(prevRange, '–'); // en dash
- } else if (prevChar === '.' && prevRange.start.column > 1) {
- const ellipsisRange = range.extend(prevRange.start.row, prevRange.start.column - 2);
- if (document.getTextRange(ellipsisRange) === '...' + char) {
- document.replace(ellipsisRange, '…' + char);
- range = new Range(ellipsisRange.start.row, ellipsisRange.start.column + 1, ellipsisRange.start.row, ellipsisRange.start.column + 2);
- }
- }
+ try {
+ const document = session.getDocument();
+ var range = new Range(e.start.row, e.start.column, e.end.row, e.end.column);
+ const prevRange = new Range(e.start.row, e.start.column - 1, e.start.row, e.start.column);
+ const char = document.getTextRange(range);
+ const prevChar = prevRange.start.column < 0 ? '' : document.getTextRange(prevRange);
- switch (char) {
- case "'":
- if (prevChar === ' ' || prevChar === '') {
- document.replace(range, '‘');
- } else {
- document.replace(range, '’');
- }
- break;
- case '"':
- if (prevChar === ' ' || prevChar === '') {
- document.replace(range, '“');
- } else {
- document.replace(range, '”');
- }
- break;
- case '-':
- if (prevChar === '-') {
- range = range.extend(range.start.row, prevRange.start.column);
- document.replace(range, '—'); // em dash
+ if (prevChar === '-' && char !== '-') {
+ document.replace(prevRange, '–'); // en dash
+ } else if (prevChar === '.' && prevRange.start.column > 1) {
+ const ellipsisRange = range.extend(prevRange.start.row, prevRange.start.column - 2);
+ if (document.getTextRange(ellipsisRange) === '...' + char) {
+ document.replace(ellipsisRange, '…' + char);
+ range = new Range(ellipsisRange.start.row, ellipsisRange.start.column + 1, ellipsisRange.start.row, ellipsisRange.start.column + 2);
}
- break;
+ }
+
+ switch (char) {
+ case "'":
+ if (prevChar === ' ' || prevChar === '') {
+ document.replace(range, '‘');
+ } else {
+ document.replace(range, '’');
+ }
+ avoidSteppingForward(e, range, document);
+ break;
+ case '"':
+ if (prevChar === ' ' || prevChar === '') {
+ document.replace(range, '“');
+ } else {
+ document.replace(range, '”');
+ }
+ avoidSteppingForward(e, range, document);
+ break;
+ case '-':
+ if (prevChar === '-') {
+ range = range.extend(range.start.row, prevRange.start.column);
+ document.replace(range, '—'); // em dash
+ avoidSteppingForward(e, range, document);
+ }
+ break;
+ }
+ } finally {
+ smartening = false;
}
}
-function replace(document, range, replacement) {
- document.replace(suggestion.where, suggestion.word)
+function avoidSteppingForward(e, range, document) {
+ const line = document.getLine(range.end.row);
+ if (line.length > range.end.column) {
+ e.end.column = e.end.column - 1;
+ }
}
\ No newline at end of file
From 910024094148d8dd21506d9111c4a9904a2065e1 Mon Sep 17 00:00:00 2001
From: David Gileadi
Date: Sat, 11 Nov 2017 14:59:48 -0700
Subject: [PATCH 06/10] Avoid "smartening" diverts
---
app/renderer/quotesAndDashes.js | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/app/renderer/quotesAndDashes.js b/app/renderer/quotesAndDashes.js
index 549df7c6..19c78731 100644
--- a/app/renderer/quotesAndDashes.js
+++ b/app/renderer/quotesAndDashes.js
@@ -33,7 +33,7 @@ exports.smarten = function (e) {
const char = document.getTextRange(range);
const prevChar = prevRange.start.column < 0 ? '' : document.getTextRange(prevRange);
- if (prevChar === '-' && char !== '-') {
+ if (prevChar === '-' && char !== '-' && char !== '>') {
document.replace(prevRange, '–'); // en dash
} else if (prevChar === '.' && prevRange.start.column > 1) {
const ellipsisRange = range.extend(prevRange.start.row, prevRange.start.column - 2);
@@ -66,6 +66,12 @@ exports.smarten = function (e) {
document.replace(range, '—'); // em dash
avoidSteppingForward(e, range, document);
}
+ case '>':
+ if (prevChar === '–' || prevChar === '—') { // en or em dash
+ range = range.extend(range.start.row, prevRange.start.column);
+ document.replace(range, '->');
+ avoidSteppingForward(e, range, document);
+ }
break;
}
} finally {
From 971283ba228755282c2d72d5715737c63a21009d Mon Sep 17 00:00:00 2001
From: David Gileadi
Date: Sat, 11 Nov 2017 16:05:05 -0700
Subject: [PATCH 07/10] Support building for all platforms
Thanks to Bartosz Antosik's vscode-spellright
---
BUILD_FOR_MAC.command | 2 +-
BUILD_FOR_WINDOWS.bat | 1 -
BUILD_PACKAGE.command | 2 +-
INSTALL_AND_RUN.command | 2 +-
app/package-lock.json | 113 ++++++++------
app/package.json | 7 +-
app/renderer/spellChecker.js | 2 +-
app/renderer/spellchecker/bindings.js | 146 ++++++++++++++++++
.../spellchecker-darwin-1.7.4-x64.node | Bin 0 -> 388616 bytes
.../spellchecker-deb-1.7.4-ia32.node | Bin 0 -> 465756 bytes
.../spellchecker-deb-1.7.4-x64.node | Bin 0 -> 416608 bytes
.../spellchecker-rpm-1.7.3-ia32.node | Bin 0 -> 461452 bytes
.../spellchecker-rpm-1.7.3-x64.node | Bin 0 -> 408128 bytes
.../spellchecker-rpm-1.7.7-ia32.node | Bin 0 -> 461452 bytes
.../spellchecker-rpm-1.7.7-x64.node | Bin 0 -> 408128 bytes
.../spellchecker-win32-1.7.4-ia32.node | Bin 0 -> 405504 bytes
.../spellchecker-win32-1.7.4-x64.node | Bin 0 -> 491008 bytes
17 files changed, 219 insertions(+), 56 deletions(-)
create mode 100755 app/renderer/spellchecker/bindings.js
create mode 100755 app/renderer/spellchecker/spellchecker-darwin-1.7.4-x64.node
create mode 100755 app/renderer/spellchecker/spellchecker-deb-1.7.4-ia32.node
create mode 100755 app/renderer/spellchecker/spellchecker-deb-1.7.4-x64.node
create mode 100755 app/renderer/spellchecker/spellchecker-rpm-1.7.3-ia32.node
create mode 100755 app/renderer/spellchecker/spellchecker-rpm-1.7.3-x64.node
create mode 100755 app/renderer/spellchecker/spellchecker-rpm-1.7.7-ia32.node
create mode 100755 app/renderer/spellchecker/spellchecker-rpm-1.7.7-x64.node
create mode 100755 app/renderer/spellchecker/spellchecker-win32-1.7.4-ia32.node
create mode 100755 app/renderer/spellchecker/spellchecker-win32-1.7.4-x64.node
diff --git a/BUILD_FOR_MAC.command b/BUILD_FOR_MAC.command
index 4a7a251a..8d927506 100755
--- a/BUILD_FOR_MAC.command
+++ b/BUILD_FOR_MAC.command
@@ -11,7 +11,7 @@ rm -rf Inky-darwin-x64/
rm ReleaseUpload/Inky_mac.dmg
# Ensure it's correctly/fully installed first
-( cd app && npm install && ./node_modules/.bin/electron-rebuild )
+( cd app && npm install )
# Create icon from PNG
./resources/makeIcns.command
diff --git a/BUILD_FOR_WINDOWS.bat b/BUILD_FOR_WINDOWS.bat
index 51370ef2..6dc105c8 100644
--- a/BUILD_FOR_WINDOWS.bat
+++ b/BUILD_FOR_WINDOWS.bat
@@ -1,7 +1,6 @@
# Ensure it's correctly/fully installed first
cd app
npm install
-.\node_modules\.bin\electron-rebuild.cmd
cd ..
electron-packager app Inky --platform=win32 --arch=x64 --icon=resources/Icon1024.png.ico --prune --asar.unpackDir="main-process/ink" --ignore="inklecate_mac" --win32metadata.ProductName="Inky" --win32metadata.CompanyName="inkle Ltd" --win32metadata.FileDescription="Inky" --win32metadata.OriginalFilename="Inky" --win32metadata.InternalName="Inky"
\ No newline at end of file
diff --git a/BUILD_PACKAGE.command b/BUILD_PACKAGE.command
index 8bdc25d3..399b8aaa 100755
--- a/BUILD_PACKAGE.command
+++ b/BUILD_PACKAGE.command
@@ -17,7 +17,7 @@ rm -rf ReleaseUpload
./resources/makeIcns.command
# Ensure it's correctly/fully installed first
-( cd app && npm install && ./node_modules/.bin/electron-rebuild )
+( cd app && npm install )
# Mac
electron-packager app Inky --platform=darwin --arch=x64 --icon=resources/Icon.icns --extend-info=resources/info.plist --app-bundle-id=com.inkle.inky --prune --asar.unpackDir="main-process/ink" --ignore="inklecate_win.exe"
diff --git a/INSTALL_AND_RUN.command b/INSTALL_AND_RUN.command
index e7f3dbbe..a3e16fd3 100755
--- a/INSTALL_AND_RUN.command
+++ b/INSTALL_AND_RUN.command
@@ -1,3 +1,3 @@
cd "`dirname "$0"`"
cd app
-npm install && ./node_modules/.bin/electron-rebuild && npm start
\ No newline at end of file
+npm install && npm start
\ No newline at end of file
diff --git a/app/package-lock.json b/app/package-lock.json
index a0fa0954..e4799dc9 100644
--- a/app/package-lock.json
+++ b/app/package-lock.json
@@ -38,22 +38,20 @@
}
},
"assertion-error": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
- "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz",
+ "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=",
"dev": true
},
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
- "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
- "dev": true
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
},
"brace-expansion": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
"integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=",
- "dev": true,
"requires": {
"balanced-match": "1.0.0",
"concat-map": "0.0.1"
@@ -71,12 +69,12 @@
"integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=",
"dev": true,
"requires": {
- "assertion-error": "1.1.0",
+ "assertion-error": "1.0.2",
"check-error": "1.0.2",
"deep-eql": "3.0.1",
"get-func-name": "2.0.0",
"pathval": "1.1.0",
- "type-detect": "4.0.7"
+ "type-detect": "4.0.5"
}
},
"chai-as-promised": {
@@ -1307,6 +1305,11 @@
}
}
},
+ "clone": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz",
+ "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs="
+ },
"co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@@ -1322,8 +1325,7 @@
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
- "dev": true
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"crc": {
"version": "3.4.4",
@@ -1331,22 +1333,13 @@
"integrity": "sha1-naHpgOO9RPxck79as9ozeNheRms=",
"dev": true
},
- "debug": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
- "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- },
"deep-eql": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
"integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
"dev": true,
"requires": {
- "type-detect": "4.0.7"
+ "type-detect": "4.0.5"
}
},
"diff": {
@@ -2610,6 +2603,15 @@
}
}
},
+ "electron-settings": {
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/electron-settings/-/electron-settings-3.1.4.tgz",
+ "integrity": "sha512-5AVJGE7+5Fwbui3PKs365vp/vGVpGGXlN1elBx/hAXVazmAtbY2jS0DOD7fBsVxzgCUiRCtR3tQvTF4cYLq/Sw==",
+ "requires": {
+ "clone": "2.1.1",
+ "jsonfile": "4.0.0"
+ }
+ },
"env-paths": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-1.0.0.tgz",
@@ -2625,8 +2627,7 @@
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
- "dev": true
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"fuzzaldrin-plus": {
"version": "0.5.0",
@@ -2652,7 +2653,6 @@
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
- "dev": true,
"requires": {
"fs.realpath": "1.0.0",
"inflight": "1.0.6",
@@ -2763,6 +2763,12 @@
}
}
},
+ "graceful-fs": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+ "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
+ "optional": true
+ },
"growl": {
"version": "1.10.3",
"resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz",
@@ -2797,7 +2803,6 @@
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
- "dev": true,
"requires": {
"once": "1.4.0",
"wrappy": "1.0.2"
@@ -2806,8 +2811,7 @@
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
- "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
- "dev": true
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"inkjs": {
"version": "1.6.0",
@@ -2820,6 +2824,14 @@
"integrity": "sha512-vE2hT1D0HLZCLLclfBSfkfTTedhVj0fubHpJBHKwwUWX0nSbhPAfk+SG9rTX95BYNmau8rGFfCeaT6T5OW1C2A==",
"dev": true
},
+ "jsonfile": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
+ "requires": {
+ "graceful-fs": "4.1.11"
+ }
+ },
"lodash": {
"version": "4.17.4",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
@@ -2875,7 +2887,6 @@
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
- "dev": true,
"requires": {
"brace-expansion": "1.1.8"
}
@@ -2896,9 +2907,9 @@
}
},
"mocha": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz",
- "integrity": "sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA==",
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.0.1.tgz",
+ "integrity": "sha512-evDmhkoA+cBNiQQQdSKZa2b9+W2mpLoj50367lhy+Klnx9OV8XlCIhigUnn1gaTFLQCa0kdNhEGDr0hCXOQFDw==",
"dev": true,
"requires": {
"browser-stdout": "1.3.0",
@@ -2911,6 +2922,26 @@
"he": "1.1.1",
"mkdirp": "0.5.1",
"supports-color": "4.4.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz",
+ "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==",
+ "dev": true,
+ "requires": {
+ "has-flag": "2.0.0"
+ }
+ }
}
},
"ms": {
@@ -2923,7 +2954,6 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
- "dev": true,
"requires": {
"wrappy": "1.0.2"
}
@@ -2931,8 +2961,7 @@
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
- "dev": true
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
},
"pathval": {
"version": "1.1.0",
@@ -5072,15 +5101,6 @@
}
}
},
- "supports-color": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz",
- "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==",
- "dev": true,
- "requires": {
- "has-flag": "2.0.0"
- }
- },
"tmp": {
"version": "0.0.31",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz",
@@ -5099,9 +5119,9 @@
}
},
"type-detect": {
- "version": "4.0.7",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.7.tgz",
- "integrity": "sha512-4Rh17pAMVdMWzktddFhISRnUnFIStObtUMNGzDwlA6w/77bmGv3aBbRdCmQR6IjzfkTo9otnW+2K/cDRhKSxDA==",
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.5.tgz",
+ "integrity": "sha512-N9IvkQslUGYGC24RkJk1ba99foK6TkwC2FHAEBlQFBP0RxQZS8ZpJuAZcwiY/w9ZJHFQb1aOXBI60OdxhTrwEQ==",
"dev": true
},
"walkdir": {
@@ -5113,8 +5133,7 @@
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
- "dev": true
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
}
}
}
diff --git a/app/package.json b/app/package.json
index 1a358b09..58b0b3e1 100644
--- a/app/package.json
+++ b/app/package.json
@@ -27,8 +27,7 @@
"devDependencies": {
"chai": "^4.1.2",
"chai-as-promised": "^7.1.1",
- "electron": "^1.4.3",
- "electron-rebuild": "^1.6.0",
+ "electron": "^1.7.6",
"markdown-html": "^0.0.8",
"mocha": "^4.0.1",
"spectron": "^3.2.6"
@@ -37,10 +36,10 @@
"chokidar": "^1.6.0",
"fuzzaldrin-plus": "^0.5",
"electron-settings": "^3.1.4",
+ "glob": "^7.1.2",
"inkjs": "^1.6.0",
"lodash": "^4.13.1",
"mkdirp": "^0.5.1",
- "randomstring": "^1.1.5",
- "spellchecker": "^3.4.4"
+ "randomstring": "^1.1.5"
}
}
diff --git a/app/renderer/spellChecker.js b/app/renderer/spellChecker.js
index 6d04e322..2425058f 100644
--- a/app/renderer/spellChecker.js
+++ b/app/renderer/spellChecker.js
@@ -1,5 +1,5 @@
const ipc = require("electron").ipcRenderer;
-const SpellChecker = require("spellchecker");
+const SpellChecker = require("./spellchecker/bindings.js");
const Range = ace.require('ace/range').Range;
const preferences = require('./preferences.js');
diff --git a/app/renderer/spellchecker/bindings.js b/app/renderer/spellchecker/bindings.js
new file mode 100755
index 00000000..2aa25295
--- /dev/null
+++ b/app/renderer/spellchecker/bindings.js
@@ -0,0 +1,146 @@
+// -----------------------------------------------------------------------------
+// Taken from https://github.com/bartosz-antosik/vscode-spellright by
+// Bartosz Antosik.
+//
+// File contains code taken from SpellChecker Node Module of NODE.js. It
+// has been modified to work with various binary bindings without the need
+// to rebuild them on target machine.
+//
+// SpellChecker Node Module: https://github.com/atom/node-spellchecker
+// -----------------------------------------------------------------------------
+
+'use strict';
+
+const path = require('path');
+const glob = require('glob');
+
+var bindings = null;
+var Spellchecker = null;
+
+const loadBinary = function (baseName) {
+ const nodeFiles = glob(path.join(__dirname, `${baseName}*${process.arch}*.node`), { sync: true });
+
+ var binding = null;
+
+ nodeFiles.forEach((file) => {
+ try {
+ if (binding == null) {
+ binding = require(file);
+ console.log('SpellRight bindings: \"' + path.basename(file) + '\".');
+ }
+ } catch (e) {
+ }
+ });
+
+ if (!binding) {
+ console.log('SpellRight found no bindings among these files:');
+ nodeFiles.forEach((file) => {
+ console.log(file);
+ });
+ }
+
+ return binding;
+};
+
+bindings = loadBinary('spellchecker');
+Spellchecker = bindings.Spellchecker;
+
+var checkSpellingAsyncCb = Spellchecker.prototype.checkSpellingAsync
+
+Spellchecker.prototype.checkSpellingAsync = function (corpus) {
+ return new Promise(function (resolve, reject) {
+ checkSpellingAsyncCb.call(this, corpus, function (err, result) {
+ if (err) {
+ reject(err);
+ } else {
+ resolve(result);
+ }
+ });
+ }.bind(this));
+};
+
+var defaultSpellcheck = null;
+
+var ensureDefaultSpellCheck = function() {
+ if (defaultSpellcheck) {
+ return;
+ }
+
+ var lang = process.env.LANG;
+ lang = lang ? lang.split('.')[0] : 'en_US';
+ defaultSpellcheck = new Spellchecker();
+
+ setDictionary(lang, getDictionaryPath());
+};
+
+var setDictionary = function(lang, dictPath) {
+ ensureDefaultSpellCheck();
+ return defaultSpellcheck.setDictionary(lang, dictPath);
+};
+
+var isMisspelled = function() {
+ ensureDefaultSpellCheck();
+
+ return defaultSpellcheck.isMisspelled.apply(defaultSpellcheck, arguments);
+};
+
+var checkSpelling = function() {
+ ensureDefaultSpellCheck();
+
+ return defaultSpellcheck.checkSpelling.apply(defaultSpellcheck, arguments);
+};
+
+var checkSpellingAsync = function(corpus) {
+ ensureDefaultSpellCheck();
+
+ return defaultSpellcheck.checkSpellingAsync.apply(defaultSpellcheck, arguments);
+};
+
+var add = function() {
+ ensureDefaultSpellCheck();
+
+ defaultSpellcheck.add.apply(defaultSpellcheck, arguments);
+};
+
+var remove = function() {
+ ensureDefaultSpellCheck();
+
+ defaultSpellcheck.remove.apply(defaultSpellcheck, arguments);
+};
+
+var getCorrectionsForMisspelling = function() {
+ ensureDefaultSpellCheck();
+
+ return defaultSpellcheck.getCorrectionsForMisspelling.apply(defaultSpellcheck, arguments);
+};
+
+var getAvailableDictionaries = function() {
+ ensureDefaultSpellCheck();
+
+ return defaultSpellcheck.getAvailableDictionaries.apply(defaultSpellcheck, arguments);
+};
+
+var getDictionaryPath = function() {
+ var dict = path.join(__dirname, '..', 'vendor', 'hunspell_dictionaries');
+ try {
+ // HACK: Special case being in an asar archive
+ var unpacked = dict.replace('.asar' + path.sep, '.asar.unpacked' + path.sep);
+ if (require('fs').statSyncNoException(unpacked)) {
+ dict = unpacked;
+ }
+ } catch (error) {
+ }
+ return dict;
+}
+
+module.exports = {
+ setDictionary: setDictionary,
+ add: add,
+ remove: remove,
+ isMisspelled: isMisspelled,
+ checkSpelling: checkSpelling,
+ checkSpellingAsync: checkSpellingAsync,
+ getAvailableDictionaries: getAvailableDictionaries,
+ getCorrectionsForMisspelling: getCorrectionsForMisspelling,
+ Spellchecker: Spellchecker
+};
diff --git a/app/renderer/spellchecker/spellchecker-darwin-1.7.4-x64.node b/app/renderer/spellchecker/spellchecker-darwin-1.7.4-x64.node
new file mode 100755
index 0000000000000000000000000000000000000000..ee2de0718459b9e8d724b714d6edad36664155c1
GIT binary patch
literal 388616
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%0AQ5#V;5OCuV^NePk2&~Z?+z|kv`*w<_(OPN0
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