devexpress-richedit
Version:
DevExpress Rich Text Editor is an advanced word-processing tool designed for working with rich text documents.
219 lines (218 loc) • 11.6 kB
JavaScript
import { FixedInterval } from '@devexpress/utils/lib/intervals/fixed';
import { Field } from '../model/fields/field';
import { JSONCheckSpellingCommand } from '../model/json/enums/json-general-enums';
import { ModelIterator } from '../model/model-iterator';
import { RunType } from '../model/runs/run-type';
import { ModelWordPositionHelper } from './helpers';
import { IgnoredInterval, MisspelledInterval, UncheckedInterval } from './intervals';
import { IgnoredIntervalCollection, IgnoredWordsCollection, MisspelledIntervalCollection, UncheckedIntervalCollection } from './intervals-collections';
import { SpellingErrorInfo, SpellingErrorType } from './spell-checker';
export class SpellCheckerIntervalsManager {
constructor(subDocument) {
this.subDocument = subDocument;
this.uncheckedIntervalCollection = new UncheckedIntervalCollection();
this.misspelledIntervalCollection = new MisspelledIntervalCollection();
this.ignoredIntervalCollection = new IgnoredIntervalCollection();
this.ignoredWordsCollection = new IgnoredWordsCollection();
this.isInitialize = false;
}
initializeUncheckedIntervals() {
if (!this.isInitialize) {
this.createUncheckedIntervals(0, this.subDocument.getDocumentEndPosition());
this.isInitialize = true;
}
}
reset() {
this.uncheckedIntervalCollection.clear();
this.misspelledIntervalCollection.clear();
this.ignoredIntervalCollection.clear();
this.ignoredWordsCollection.clear();
this.isInitialize = false;
}
getIntervalsToCheck() {
this.splitUncheckedIntervals();
let results = [];
let totalLength = 0;
let uncheckedIntervals = this.uncheckedIntervalCollection.getIntervals();
const maxRequestLength = this.subDocument.documentModel.modelOptions.maxSpellRequestLength;
for (let i = 0, uncheckedInterval; uncheckedInterval = uncheckedIntervals[i]; i++) {
if (uncheckedInterval.info.isChecking || totalLength > maxRequestLength) {
this.removeEmptyUncheckedIntervals(i);
return results;
}
this.expandUncheckedInterval(uncheckedInterval);
if (uncheckedInterval.length > 0) {
this.prepareUncheckedInterval(uncheckedInterval);
uncheckedInterval.info.isChecking = true;
totalLength += uncheckedInterval.length;
results.push(uncheckedInterval);
}
}
this.removeEmptyUncheckedIntervals();
return results;
}
applyCheckResults(checkedIntervals) {
for (let i = 0, checkedInterval; checkedInterval = checkedIntervals[i]; i++) {
const startPosition = checkedInterval[JSONCheckSpellingCommand.StartPosition];
const endPosition = checkedInterval[JSONCheckSpellingCommand.EndPosition];
const checkingInterval = this.uncheckedIntervalCollection.findCheckingIntervalByPositions(startPosition, endPosition);
if (checkingInterval) {
let isIntervalStartWithParagraph = startPosition === ModelWordPositionHelper.getPrevWordStartPosition(this.subDocument, startPosition);
this.misspelledIntervalCollection.deleteOldIntervals(checkingInterval, isIntervalStartWithParagraph);
this.createMisspelledIntervals(checkedInterval[JSONCheckSpellingCommand.SpellingErrors], checkingInterval);
this.uncheckedIntervalCollection.deleteIntervalsByPositions(startPosition, endPosition, true);
}
}
}
onModelIntervalChanged(start, length, isSeparator) {
this.uncheckedIntervalCollection.onModelIntervalChanged(start, length, isSeparator);
this.misspelledIntervalCollection.onModelIntervalChanged(start, length, isSeparator);
this.ignoredIntervalCollection.onModelIntervalChanged(start, length, isSeparator);
}
getMisspelledIntervals() {
return this.misspelledIntervalCollection.getIntervals();
}
getUncheckedIntervalsCount() {
return this.uncheckedIntervalCollection.getIntervals().length;
}
getSelectedMisspelledInterval(selectionIntervals) {
let selectedInterval = null;
this.misspelledIntervalCollection.forEach(interval => {
const expandedInterval = new FixedInterval(interval.start, interval.length);
Field.correctIntervalDueToFieldsWithoutUiChecks(this.subDocument, expandedInterval);
if (expandedInterval.containsInterval(selectionIntervals[0]))
selectedInterval = interval;
});
return selectedInterval;
}
findNextMisspelledInterval(position) {
return this.misspelledIntervalCollection.findNext(position);
}
addIgnoredInterval(start, end, word) {
this.ignoredIntervalCollection.add(new IgnoredInterval(start, end, word));
}
deleteMisspelledIntervalsByPositions(start, end) {
this.misspelledIntervalCollection.deleteIntervalsByPositions(start, end);
}
ignoreAll(word) {
this.ignoredWordsCollection.add(word);
this.misspelledIntervalCollection.forEach((interval, index) => {
if (interval.errorInfo.word == word && interval.errorInfo.errorType == SpellingErrorType.Misspelling) {
this.misspelledIntervalCollection.remove(index);
}
});
}
removeIntervalsWithErrorByWord(word) {
this.misspelledIntervalCollection.forEach((interval, index) => {
if (interval.errorInfo.word == word && interval.errorInfo.errorType == SpellingErrorType.Misspelling)
this.misspelledIntervalCollection.remove(index);
});
}
getIntervalsWithErrorByWord(word) {
let intervals = [];
this.misspelledIntervalCollection.forEach(interval => {
if (interval.errorInfo.word == word && interval.errorInfo.errorType == SpellingErrorType.Misspelling) {
intervals.push(new FixedInterval(interval.start, interval.length));
}
});
return intervals;
}
expandUncheckedInterval(interval) {
let expandIntervalStart = ModelWordPositionHelper.getPrevWordStartPosition(this.subDocument, interval.start);
let expandIntervalEnd = ModelWordPositionHelper.getNextWordEndPosition(this.subDocument, interval.end);
interval.start = expandIntervalStart;
interval.length = Math.max(0, expandIntervalEnd - expandIntervalStart);
}
removeEmptyUncheckedIntervals(maxIndex = this.uncheckedIntervalCollection.getIntervals().length) {
this.uncheckedIntervalCollection.forEach((interval, index) => {
if (maxIndex < index && interval.length == 0)
this.uncheckedIntervalCollection.remove(index);
});
}
prepareUncheckedInterval(interval) {
let iterator = new ModelIterator(this.subDocument, false);
let textToCheck = "";
let hiddenIntervals = [];
let ignoredLength = 0;
let ignoredStart = 0;
let fieldLevel = 0;
let isPreviouslyHiddeh = false;
iterator.setPosition(interval.start);
do {
if (!iterator.run.getCharacterMergedProperties().hidden && iterator.run.getType() !== RunType.FieldCodeStartRun && fieldLevel === 0) {
textToCheck += iterator.getCurrentChar();
if (isPreviouslyHiddeh) {
hiddenIntervals.push(new FixedInterval(ignoredStart, ignoredLength));
ignoredLength = 0;
isPreviouslyHiddeh = false;
}
ignoredStart++;
}
else {
ignoredLength++;
isPreviouslyHiddeh = true;
if (iterator.run.getType() == RunType.FieldCodeStartRun)
fieldLevel++;
if (iterator.run.getType() == RunType.FieldResultEndRun)
fieldLevel--;
}
} while (iterator.getAbsolutePosition() < interval.end && iterator.moveToNextChar());
interval.info.textToCheck = textToCheck;
interval.info.hiddenIntervals = hiddenIntervals;
}
createUncheckedIntervals(start, end) {
let intervalCount = 0;
let iterator = new ModelIterator(this.subDocument, true);
iterator.setPosition(start);
do {
if (iterator.run.getType() == RunType.ParagraphRun) {
let currentPosition = iterator.getAbsolutePosition();
let length = currentPosition - start;
if (length > 0) {
this.uncheckedIntervalCollection.add(new UncheckedInterval(start, length));
intervalCount++;
}
start = currentPosition + 1;
}
} while (iterator.getAbsolutePosition() < end && iterator.moveToNextRun());
if (intervalCount == 0 && end > start)
this.uncheckedIntervalCollection.add(new UncheckedInterval(start, end - start));
}
splitUncheckedIntervals() {
let uncheckedIntervals = this.uncheckedIntervalCollection.getIntervals();
const maxRequestLength = this.subDocument.documentModel.modelOptions.maxSpellRequestLength;
for (let i = 0, uncheckedInterval; uncheckedInterval = uncheckedIntervals[i]; i++) {
if (uncheckedInterval.info.isChecking)
return;
if (!uncheckedInterval.info.isSplitted) {
if (uncheckedInterval.length > maxRequestLength) {
const start = uncheckedInterval.start;
const end = uncheckedInterval.end;
this.uncheckedIntervalCollection.remove(i);
this.createUncheckedIntervals(start, end);
}
else
uncheckedInterval.info.isSplitted = true;
}
}
}
createMisspelledIntervals(spellingErrors, checkingInterval) {
for (let i = 0, error; error = spellingErrors[i]; i++) {
let errorStart = error[JSONCheckSpellingCommand.ErrorStart] + checkingInterval.start;
let errorLength = error[JSONCheckSpellingCommand.ErrorLength];
for (let j = 0, hiddenInterval; hiddenInterval = checkingInterval.info.hiddenIntervals[j]; j++) {
if (error[JSONCheckSpellingCommand.ErrorStart] > hiddenInterval.start)
errorStart += hiddenInterval.length;
else if (hiddenInterval.start < error[JSONCheckSpellingCommand.ErrorStart] + error[JSONCheckSpellingCommand.ErrorLength])
errorLength += hiddenInterval.length;
}
const word = error[JSONCheckSpellingCommand.ErrorWord];
if (!this.ignoredIntervalCollection.contains(errorStart, errorLength, word) &&
(!this.ignoredWordsCollection.contains(word) || error[JSONCheckSpellingCommand.ErrorType] != SpellingErrorType.Misspelling)) {
const spellingErrorInfo = new SpellingErrorInfo(error[JSONCheckSpellingCommand.ErrorType], error[JSONCheckSpellingCommand.Suggestions], word);
const misspelledInterval = new MisspelledInterval(errorStart, errorLength, spellingErrorInfo);
this.misspelledIntervalCollection.addIfNotExists(misspelledInterval);
}
}
}
}