Merge pull request #3210 from matrix-org/bwindels/prevent-autocomplete-on-paste
Prevent autocomplete on paste, and verserev-ing text before and after :
This commit is contained in:
commit
7fc19e61a9
3 changed files with 38 additions and 9 deletions
|
@ -280,6 +280,7 @@ export default class MessageEditor extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
this._editorRef.removeEventListener("input", this._onInput, true);
|
||||||
const sel = document.getSelection();
|
const sel = document.getSelection();
|
||||||
const {caret} = getCaretOffsetAndText(this._editorRef, sel);
|
const {caret} = getCaretOffsetAndText(this._editorRef, sel);
|
||||||
const parts = this.model.serializeParts();
|
const parts = this.model.serializeParts();
|
||||||
|
@ -292,6 +293,9 @@ export default class MessageEditor extends React.Component {
|
||||||
this._updateEditorState();
|
this._updateEditorState();
|
||||||
// initial caret position
|
// initial caret position
|
||||||
this._initializeCaret();
|
this._initializeCaret();
|
||||||
|
// attach input listener by hand so React doesn't proxy the events,
|
||||||
|
// as the proxied event doesn't support inputType, which we need.
|
||||||
|
this._editorRef.addEventListener("input", this._onInput, true);
|
||||||
this._editorRef.focus();
|
this._editorRef.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,7 +363,6 @@ export default class MessageEditor extends React.Component {
|
||||||
className="mx_MessageEditor_editor"
|
className="mx_MessageEditor_editor"
|
||||||
contentEditable="true"
|
contentEditable="true"
|
||||||
tabIndex="1"
|
tabIndex="1"
|
||||||
onInput={this._onInput}
|
|
||||||
onKeyDown={this._onKeyDown}
|
onKeyDown={this._onKeyDown}
|
||||||
ref={ref => this._editorRef = ref}
|
ref={ref => this._editorRef = ref}
|
||||||
aria-label={_t("Edit message")}
|
aria-label={_t("Edit message")}
|
||||||
|
|
|
@ -97,24 +97,26 @@ export default class EditorModel {
|
||||||
if (diff.removed) {
|
if (diff.removed) {
|
||||||
removedOffsetDecrease = this.removeText(position, diff.removed.length);
|
removedOffsetDecrease = this.removeText(position, diff.removed.length);
|
||||||
}
|
}
|
||||||
|
const canOpenAutoComplete = inputType !== "insertFromPaste" && inputType !== "insertFromDrop";
|
||||||
let addedLen = 0;
|
let addedLen = 0;
|
||||||
if (diff.added) {
|
if (diff.added) {
|
||||||
addedLen = this._addText(position, diff.added);
|
// these shouldn't trigger auto-complete, you just want to append a piece of text
|
||||||
|
addedLen = this._addText(position, diff.added, {validate: canOpenAutoComplete});
|
||||||
}
|
}
|
||||||
this._mergeAdjacentParts();
|
this._mergeAdjacentParts();
|
||||||
const caretOffset = diff.at - removedOffsetDecrease + addedLen;
|
const caretOffset = diff.at - removedOffsetDecrease + addedLen;
|
||||||
const newPosition = this.positionForOffset(caretOffset, true);
|
const newPosition = this.positionForOffset(caretOffset, true);
|
||||||
this._setActivePart(newPosition);
|
this._setActivePart(newPosition, canOpenAutoComplete);
|
||||||
this._updateCallback(newPosition);
|
this._updateCallback(newPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
_setActivePart(pos) {
|
_setActivePart(pos, canOpenAutoComplete) {
|
||||||
const {index} = pos;
|
const {index} = pos;
|
||||||
const part = this._parts[index];
|
const part = this._parts[index];
|
||||||
if (part) {
|
if (part) {
|
||||||
if (index !== this._activePartIdx) {
|
if (index !== this._activePartIdx) {
|
||||||
this._activePartIdx = index;
|
this._activePartIdx = index;
|
||||||
if (this._activePartIdx !== this._autoCompletePartIdx) {
|
if (canOpenAutoComplete && this._activePartIdx !== this._autoCompletePartIdx) {
|
||||||
// else try to create one
|
// else try to create one
|
||||||
const ac = part.createAutoComplete(this._onAutoComplete);
|
const ac = part.createAutoComplete(this._onAutoComplete);
|
||||||
if (ac) {
|
if (ac) {
|
||||||
|
@ -217,17 +219,22 @@ export default class EditorModel {
|
||||||
* inserts `str` into the model at `pos`.
|
* inserts `str` into the model at `pos`.
|
||||||
* @param {Object} pos
|
* @param {Object} pos
|
||||||
* @param {string} str
|
* @param {string} str
|
||||||
|
* @param {Object} options
|
||||||
|
* @param {bool} options.validate Whether characters will be validated by the part.
|
||||||
|
* Validating allows the inserted text to be parsed according to the part rules.
|
||||||
* @return {Number} how far from position (in characters) the insertion ended.
|
* @return {Number} how far from position (in characters) the insertion ended.
|
||||||
* This can be more than the length of `str` when crossing non-editable parts, which are skipped.
|
* This can be more than the length of `str` when crossing non-editable parts, which are skipped.
|
||||||
*/
|
*/
|
||||||
_addText(pos, str) {
|
_addText(pos, str, {validate=true}) {
|
||||||
let {index} = pos;
|
let {index} = pos;
|
||||||
const {offset} = pos;
|
const {offset} = pos;
|
||||||
let addLen = str.length;
|
let addLen = str.length;
|
||||||
const part = this._parts[index];
|
const part = this._parts[index];
|
||||||
if (part) {
|
if (part) {
|
||||||
if (part.canEdit) {
|
if (part.canEdit) {
|
||||||
if (part.insertAll(offset, str)) {
|
if (validate && part.validateAndInsert(offset, str)) {
|
||||||
|
str = null;
|
||||||
|
} else if (!validate && part.insert(offset, str)) {
|
||||||
str = null;
|
str = null;
|
||||||
} else {
|
} else {
|
||||||
const splitPart = part.split(offset);
|
const splitPart = part.split(offset);
|
||||||
|
@ -240,10 +247,19 @@ export default class EditorModel {
|
||||||
addLen += part.text.length - offset;
|
addLen += part.text.length - offset;
|
||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
|
} else if (index < 0) {
|
||||||
|
// if position was not found (index: -1, as happens for empty editor)
|
||||||
|
// reset it to insert as first part
|
||||||
|
index = 0;
|
||||||
}
|
}
|
||||||
while (str) {
|
while (str) {
|
||||||
const newPart = this._partCreator.createPartForInput(str);
|
const newPart = this._partCreator.createPartForInput(str);
|
||||||
|
if (validate) {
|
||||||
str = newPart.appendUntilRejected(str);
|
str = newPart.appendUntilRejected(str);
|
||||||
|
} else {
|
||||||
|
newPart.insert(0, str);
|
||||||
|
str = null;
|
||||||
|
}
|
||||||
this._insertPart(index, newPart);
|
this._insertPart(index, newPart);
|
||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@ class BasePart {
|
||||||
|
|
||||||
// inserts str at offset if all the characters in str were accepted, otherwise don't do anything
|
// inserts str at offset if all the characters in str were accepted, otherwise don't do anything
|
||||||
// return whether the str was accepted or not.
|
// return whether the str was accepted or not.
|
||||||
insertAll(offset, str) {
|
validateAndInsert(offset, str) {
|
||||||
for (let i = 0; i < str.length; ++i) {
|
for (let i = 0; i < str.length; ++i) {
|
||||||
const chr = str.charAt(i);
|
const chr = str.charAt(i);
|
||||||
if (!this.acceptsInsertion(chr)) {
|
if (!this.acceptsInsertion(chr)) {
|
||||||
|
@ -82,6 +82,16 @@ class BasePart {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
insert(offset, str) {
|
||||||
|
if (this.canEdit) {
|
||||||
|
const beforeInsert = this._text.substr(0, offset);
|
||||||
|
const afterInsert = this._text.substr(offset);
|
||||||
|
this._text = beforeInsert + str + afterInsert;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
createAutoComplete() {}
|
createAutoComplete() {}
|
||||||
|
|
||||||
trim(len) {
|
trim(len) {
|
||||||
|
|
Loading…
Reference in a new issue