From 54a11418f6a4588d52b22f93324aaf84315787ad Mon Sep 17 00:00:00 2001 From: Alice Date: Wed, 28 Jun 2023 20:08:57 +0200 Subject: [PATCH 01/17] sketch --- src/editor.js | 2 + src/keyboard/textinput.js | 125 +++++++++++++++++++++++--------------- 2 files changed, 79 insertions(+), 48 deletions(-) diff --git a/src/editor.js b/src/editor.js index 141306425c..7fb035779b 100644 --- a/src/editor.js +++ b/src/editor.js @@ -2896,6 +2896,7 @@ config.defineOptions(Editor.prototype, "editor", { this.renderer.keyboardFocusClassName = "ace_keyboard-focus"; this.textInput.getElement().setAttribute("tabindex", -1); + this.textInput.setNumExtraLines(3); this.renderer.scroller.setAttribute("tabindex", 0); this.renderer.scroller.setAttribute("role", "group"); this.renderer.scroller.setAttribute("aria-roledescription", nls("editor")); @@ -2926,6 +2927,7 @@ config.defineOptions(Editor.prototype, "editor", { this.renderer.enableKeyboardAccessibility = false; this.textInput.getElement().setAttribute("tabindex", 0); + this.textInput.setNumExtraLines(0); this.renderer.scroller.setAttribute("tabindex", -1); this.renderer.scroller.removeAttribute("role"); this.renderer.scroller.removeAttribute("aria-roledescription"); diff --git a/src/keyboard/textinput.js b/src/keyboard/textinput.js index b3bdd04c64..5dd79e8e5d 100644 --- a/src/keyboard/textinput.js +++ b/src/keyboard/textinput.js @@ -10,6 +10,7 @@ var BROKEN_SETDATA = useragent.isChrome < 18; var USE_IE_MIME_TYPE = useragent.isIE; var HAS_FOCUS_ARGS = useragent.isChrome > 63; var MAX_LINE_LENGTH = 400; +var NUM_EXTRA_LINES = 0; var KEYS = require("../lib/keys"); var MODS = KEYS.KEY_MODS; @@ -45,11 +46,16 @@ var TextInput = function(parentNode, host) { var lastSelectionStart = 0; var lastSelectionEnd = 0; var lastRestoreEnd = 0; + var rowStart = 0; + var rowEnd = 0; // FOCUS // ie9 throws error if document.activeElement is accessed too soon try { var isFocused = document.activeElement === text; } catch(e) {} + this.setNumExtraLines = function(number) { + NUM_EXTRA_LINES = number; + } this.setAriaOptions = function(options) { if (options.activeDescendant) { text.setAttribute("aria-haspopup", "true"); @@ -63,21 +69,16 @@ var TextInput = function(parentNode, host) { if (options.role) { text.setAttribute("role", options.role); } - }; - this.setAriaLabel = function() { - if(host.session && host.renderer.enableKeyboardAccessibility) { - var row = host.session.selection.cursor.row; - + if (options.setAriaLabel) { text.setAttribute("aria-roledescription", nls("editor")); - text.setAttribute("aria-label", nls("Cursor at row $0", [row + 1])); - } else { - text.removeAttribute("aria-roledescription"); - text.removeAttribute("aria-label"); + if(host.session) { + var row = host.session.selection.cursor.row; + text.setAttribute("aria-label", nls("Cursor at row $0", [row + 1])); + } } }; - this.setAriaOptions({role: "textbox"}); - this.setAriaLabel(); + this.setAriaOptions({role: "textbox"}); event.addListener(text, "blur", function(e) { if (ignoreFocusEvents) return; @@ -107,7 +108,9 @@ var TextInput = function(parentNode, host) { this.$focusScroll = false; this.focus = function() { // On focusing on the textarea, read active row number to assistive tech. - this.setAriaLabel(); + this.setAriaOptions({ + setAriaLabel: host.renderer.enableKeyboardAccessibility + }); if (tempStyle || HAS_FOCUS_ARGS || this.$focusScroll == "browser") return text.focus({ preventScroll: true }); @@ -205,48 +208,74 @@ var TextInput = function(parentNode, host) { var row = selection.cursor.row; selectionStart = range.start.column; selectionEnd = range.end.column; - line = host.session.getLine(row); + + // Check whether the selection is within the lines currently in the textarea. + if (row >= rowStart && row <= rowEnd){ + for (var i = 1; i <= row - rowStart; i++) { + selectionStart += host.session.getLine(row - i).length + 1; + selectionEnd += host.session.getLine(row - i).length + 1; + } + } else { + // If the new cursor position is one row above or below current rows + // in textarea, move page up or down. If not, set textarea to fresh + // set of rows around the cursor. + if (row === rowEnd + 1) { + rowStart = rowEnd + 1; + rowEnd = rowStart + NUM_EXTRA_LINES - 1; + } else if (row === rowStart - 1) { + rowEnd = rowStart - 1; + rowStart = rowEnd - NUM_EXTRA_LINES + 1; + } else { + rowStart = row > Math.floor(NUM_EXTRA_LINES / 2) ? row - Math.floor(NUM_EXTRA_LINES / 2) : 0; + rowEnd = row + Math.floor(NUM_EXTRA_LINES / 2); + } + + var prevalue = ""; + var value = host.session.getLine(row); + var postvalue = ""; - if (range.start.row != row) { - var prevLine = host.session.getLine(row - 1); - selectionStart = range.start.row < row - 1 ? 0 : selectionStart; - selectionEnd += prevLine.length + 1; - line = prevLine + "\n" + line; - } - else if (range.end.row != row) { - var nextLine = host.session.getLine(row + 1); - selectionEnd = range.end.row > row + 1 ? nextLine.length : selectionEnd; - selectionEnd += line.length + 1; - line = line + "\n" + nextLine; - } - else if (isMobile && row > 0) { - line = "\n" + line; - selectionEnd += 1; - selectionStart += 1; - } + for (var i = rowStart; i < row; i++) { + prevalue += host.session.getLine(i) + '\n'; + } + for (var i = row + 1; i <= rowEnd; i++) { + postvalue += host.session.getLine(i) + '\n'; + } - if (line.length > MAX_LINE_LENGTH) { - if (selectionStart < MAX_LINE_LENGTH && selectionEnd < MAX_LINE_LENGTH) { - line = line.slice(0, MAX_LINE_LENGTH); - } else { - line = "\n"; - if (selectionStart == selectionEnd) { - selectionStart = selectionEnd = 0; - } - else { - selectionStart = 0; - selectionEnd = 1; + line = prevalue + value + postvalue; + + for (var i = 1; i <= row - rowStart; i++) { + selectionStart += host.session.getLine(row - i).length + 1; + selectionEnd += host.session.getLine(row - i).length + 1; + } + + if (isMobile && row > 0) { + line = "\n" + line; + selectionEnd += 1; + selectionStart += 1; + } + + if (line.length > MAX_LINE_LENGTH) { + if (selectionStart < MAX_LINE_LENGTH && selectionEnd < MAX_LINE_LENGTH) { + line = line.slice(0, MAX_LINE_LENGTH); + } else { + line = "\n"; + if (selectionStart == selectionEnd) { + selectionStart = selectionEnd = 0; + } + else { + selectionStart = 0; + selectionEnd = 1; + } } } + + var newValue = line + "\n\n"; + if (newValue != lastValue) { + text.value = lastValue = newValue; + lastSelectionStart = lastSelectionEnd = newValue.length; + } } } - - var newValue = line + "\n\n"; - if (newValue != lastValue) { - text.value = lastValue = newValue; - lastSelectionStart = lastSelectionEnd = newValue.length; - } - // contextmenu on mac may change the selection if (afterContextMenu) { lastSelectionStart = text.selectionStart; From be61bb9a2c9726e91598e7542e69d330483dce83 Mon Sep 17 00:00:00 2001 From: Alice Date: Wed, 28 Jun 2023 20:24:55 +0200 Subject: [PATCH 02/17] tweaks --- src/editor.js | 4 ++-- src/keyboard/textinput.js | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/editor.js b/src/editor.js index 7fb035779b..1860620f49 100644 --- a/src/editor.js +++ b/src/editor.js @@ -2896,7 +2896,7 @@ config.defineOptions(Editor.prototype, "editor", { this.renderer.keyboardFocusClassName = "ace_keyboard-focus"; this.textInput.getElement().setAttribute("tabindex", -1); - this.textInput.setNumExtraLines(3); + this.textInput.setNumberLines(3); this.renderer.scroller.setAttribute("tabindex", 0); this.renderer.scroller.setAttribute("role", "group"); this.renderer.scroller.setAttribute("aria-roledescription", nls("editor")); @@ -2927,7 +2927,7 @@ config.defineOptions(Editor.prototype, "editor", { this.renderer.enableKeyboardAccessibility = false; this.textInput.getElement().setAttribute("tabindex", 0); - this.textInput.setNumExtraLines(0); + this.textInput.setNumberLines(1); this.renderer.scroller.setAttribute("tabindex", -1); this.renderer.scroller.removeAttribute("role"); this.renderer.scroller.removeAttribute("aria-roledescription"); diff --git a/src/keyboard/textinput.js b/src/keyboard/textinput.js index 5dd79e8e5d..de15e6e059 100644 --- a/src/keyboard/textinput.js +++ b/src/keyboard/textinput.js @@ -10,7 +10,7 @@ var BROKEN_SETDATA = useragent.isChrome < 18; var USE_IE_MIME_TYPE = useragent.isIE; var HAS_FOCUS_ARGS = useragent.isChrome > 63; var MAX_LINE_LENGTH = 400; -var NUM_EXTRA_LINES = 0; +var NUM_LINES = 0; var KEYS = require("../lib/keys"); var MODS = KEYS.KEY_MODS; @@ -53,8 +53,8 @@ var TextInput = function(parentNode, host) { // ie9 throws error if document.activeElement is accessed too soon try { var isFocused = document.activeElement === text; } catch(e) {} - this.setNumExtraLines = function(number) { - NUM_EXTRA_LINES = number; + this.setNumberLines = function(number) { + NUM_LINES = number; } this.setAriaOptions = function(options) { if (options.activeDescendant) { @@ -208,7 +208,7 @@ var TextInput = function(parentNode, host) { var row = selection.cursor.row; selectionStart = range.start.column; selectionEnd = range.end.column; - + // Check whether the selection is within the lines currently in the textarea. if (row >= rowStart && row <= rowEnd){ for (var i = 1; i <= row - rowStart; i++) { @@ -221,17 +221,17 @@ var TextInput = function(parentNode, host) { // set of rows around the cursor. if (row === rowEnd + 1) { rowStart = rowEnd + 1; - rowEnd = rowStart + NUM_EXTRA_LINES - 1; + rowEnd = rowStart + NUM_LINES - 1; } else if (row === rowStart - 1) { rowEnd = rowStart - 1; - rowStart = rowEnd - NUM_EXTRA_LINES + 1; + rowStart = rowEnd - NUM_LINES + 1; } else { - rowStart = row > Math.floor(NUM_EXTRA_LINES / 2) ? row - Math.floor(NUM_EXTRA_LINES / 2) : 0; - rowEnd = row + Math.floor(NUM_EXTRA_LINES / 2); + rowStart = row > Math.floor(NUM_LINES / 2) ? row - Math.floor(NUM_LINES / 2) : 0; + rowEnd = row + Math.floor(NUM_LINES / 2); } var prevalue = ""; - var value = host.session.getLine(row); + var value = host.session.getLine(row) + '\n'; var postvalue = ""; for (var i = rowStart; i < row; i++) { From d6e606e4a0a90165a951cf0a7a2d1e5434530b41 Mon Sep 17 00:00:00 2001 From: Alice Date: Wed, 28 Jun 2023 21:27:59 +0200 Subject: [PATCH 03/17] fix failing test --- src/keyboard/textinput.js | 45 ++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/src/keyboard/textinput.js b/src/keyboard/textinput.js index de15e6e059..b83dfe2ba3 100644 --- a/src/keyboard/textinput.js +++ b/src/keyboard/textinput.js @@ -10,7 +10,6 @@ var BROKEN_SETDATA = useragent.isChrome < 18; var USE_IE_MIME_TYPE = useragent.isIE; var HAS_FOCUS_ARGS = useragent.isChrome > 63; var MAX_LINE_LENGTH = 400; -var NUM_LINES = 0; var KEYS = require("../lib/keys"); var MODS = KEYS.KEY_MODS; @@ -46,15 +45,21 @@ var TextInput = function(parentNode, host) { var lastSelectionStart = 0; var lastSelectionEnd = 0; var lastRestoreEnd = 0; - var rowStart = 0; - var rowEnd = 0; + var rowStart = -1; + var rowEnd = -1; + var numberOfLines = 1; // FOCUS // ie9 throws error if document.activeElement is accessed too soon try { var isFocused = document.activeElement === text; } catch(e) {} + // Set number of lines, needs to be 1 or greater and odd. this.setNumberLines = function(number) { - NUM_LINES = number; + console.log(`lines set to ${number}`) + if (number < 1 || number % 2 === 0) { + return; + } + numberOfLines = number; } this.setAriaOptions = function(options) { if (options.activeDescendant) { @@ -210,7 +215,7 @@ var TextInput = function(parentNode, host) { selectionEnd = range.end.column; // Check whether the selection is within the lines currently in the textarea. - if (row >= rowStart && row <= rowEnd){ + if (numberOfLines > 1 && row >= rowStart && row <= rowEnd){ for (var i = 1; i <= row - rowStart; i++) { selectionStart += host.session.getLine(row - i).length + 1; selectionEnd += host.session.getLine(row - i).length + 1; @@ -221,17 +226,17 @@ var TextInput = function(parentNode, host) { // set of rows around the cursor. if (row === rowEnd + 1) { rowStart = rowEnd + 1; - rowEnd = rowStart + NUM_LINES - 1; + rowEnd = rowStart + numberOfLines - 1; } else if (row === rowStart - 1) { rowEnd = rowStart - 1; - rowStart = rowEnd - NUM_LINES + 1; + rowStart = rowEnd - numberOfLines + 1; } else { - rowStart = row > Math.floor(NUM_LINES / 2) ? row - Math.floor(NUM_LINES / 2) : 0; - rowEnd = row + Math.floor(NUM_LINES / 2); + rowStart = row > Math.floor(numberOfLines / 2) ? row - Math.floor(numberOfLines / 2) : 0; + rowEnd = row + Math.floor(numberOfLines / 2); } var prevalue = ""; - var value = host.session.getLine(row) + '\n'; + var value = host.session.getLine(row); var postvalue = ""; for (var i = rowStart; i < row; i++) { @@ -241,14 +246,30 @@ var TextInput = function(parentNode, host) { postvalue += host.session.getLine(i) + '\n'; } - line = prevalue + value + postvalue; + if (numberOfLines > 1) { + line = prevalue + value + '\n' + postvalue; + } else { + line = value; + } for (var i = 1; i <= row - rowStart; i++) { selectionStart += host.session.getLine(row - i).length + 1; selectionEnd += host.session.getLine(row - i).length + 1; } - if (isMobile && row > 0) { + if (numberOfLines === 1 && range.start.row != row) { + var prevLine = host.session.getLine(row - 1); + selectionStart = range.start.row < row - 1 ? 0 : selectionStart; + selectionEnd += prevLine.length + 1; + line = prevLine + "\n" + line; + } + else if (numberOfLines === 1 && range.end.row != row) { + var nextLine = host.session.getLine(row + 1); + selectionEnd = range.end.row > row + 1 ? nextLine.length : selectionEnd; + selectionEnd += line.length + 1; + line = line + "\n" + nextLine; + } + else if (isMobile && row > 0) { line = "\n" + line; selectionEnd += 1; selectionStart += 1; From e988e3aebf68a71684a9082cd8cf46f8f9779379 Mon Sep 17 00:00:00 2001 From: Alice Date: Wed, 28 Jun 2023 21:30:06 +0200 Subject: [PATCH 04/17] add missing semi-colons --- src/keyboard/textinput.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/keyboard/textinput.js b/src/keyboard/textinput.js index b83dfe2ba3..4afe303388 100644 --- a/src/keyboard/textinput.js +++ b/src/keyboard/textinput.js @@ -55,12 +55,11 @@ var TextInput = function(parentNode, host) { // Set number of lines, needs to be 1 or greater and odd. this.setNumberLines = function(number) { - console.log(`lines set to ${number}`) if (number < 1 || number % 2 === 0) { return; } numberOfLines = number; - } + }; this.setAriaOptions = function(options) { if (options.activeDescendant) { text.setAttribute("aria-haspopup", "true"); From 006397aaadd0ffbdbac3ce15f1230e581af1bb81 Mon Sep 17 00:00:00 2001 From: Alice Date: Thu, 29 Jun 2023 23:18:58 +0200 Subject: [PATCH 05/17] fix paste problems --- src/keyboard/textinput.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/keyboard/textinput.js b/src/keyboard/textinput.js index 4afe303388..1b308e0c28 100644 --- a/src/keyboard/textinput.js +++ b/src/keyboard/textinput.js @@ -213,8 +213,18 @@ var TextInput = function(parentNode, host) { selectionStart = range.start.column; selectionEnd = range.end.column; + var curOp = host.curOp; + var lineUpOrDown; + + var commandArray = ["selectup", "selectdown", "golineup", "golinedown"]; + + if (curOp) { + lineUpOrDown = commandArray.includes(curOp.command.name); + } else + lineUpOrDown = false; + // Check whether the selection is within the lines currently in the textarea. - if (numberOfLines > 1 && row >= rowStart && row <= rowEnd){ + if (lineUpOrDown && numberOfLines > 1 && row >= rowStart && row <= rowEnd){ for (var i = 1; i <= row - rowStart; i++) { selectionStart += host.session.getLine(row - i).length + 1; selectionEnd += host.session.getLine(row - i).length + 1; @@ -296,6 +306,7 @@ var TextInput = function(parentNode, host) { } } } + // contextmenu on mac may change the selection if (afterContextMenu) { lastSelectionStart = text.selectionStart; From d304bbc0ea75bb8143589b175396e5caeb126d77 Mon Sep 17 00:00:00 2001 From: Alice Date: Sat, 1 Jul 2023 00:53:45 +0200 Subject: [PATCH 06/17] rewrite --- src/keyboard/textinput.js | 154 +++++++++++++++++--------------------- 1 file changed, 69 insertions(+), 85 deletions(-) diff --git a/src/keyboard/textinput.js b/src/keyboard/textinput.js index 1b308e0c28..ee16e68e73 100644 --- a/src/keyboard/textinput.js +++ b/src/keyboard/textinput.js @@ -174,6 +174,15 @@ var TextInput = function(parentNode, host) { resetSelection(); }); + var positionToSelection = function(row, column) { + var selection = column; + + for (var i = 1; i <= row - rowStart && i < numberOfLines; i++) { + selection += host.session.getLine(row - i).length + 1; + } + return selection; + } + var resetSelection = isIOS ? function(value) { if (!isFocused || (copied && !value) || sendingText) return; @@ -210,100 +219,75 @@ var TextInput = function(parentNode, host) { var selection = host.selection; var range = selection.getRange(); var row = selection.cursor.row; - selectionStart = range.start.column; - selectionEnd = range.end.column; - var curOp = host.curOp; - var lineUpOrDown; - - var commandArray = ["selectup", "selectdown", "golineup", "golinedown"]; - - if (curOp) { - lineUpOrDown = commandArray.includes(curOp.command.name); - } else - lineUpOrDown = false; + // If the new cursor position is one row above or below current rows + // in textarea, move page up or down. If not, set textarea to fresh + // set of rows around the cursor. + if (row === rowEnd + 1) { + rowStart = rowEnd + 1; + rowEnd = rowStart + numberOfLines - 1; + } else if (row === rowStart - 1) { + rowEnd = rowStart - 1; + rowStart = rowEnd - numberOfLines + 1; + } else if (row < rowStart - 1 || row > rowEnd + 1) { + rowStart = row > Math.floor(numberOfLines / 2) ? row - Math.floor(numberOfLines / 2) : 0; + rowEnd = row + Math.floor(numberOfLines / 2); + } + + var lines = []; - // Check whether the selection is within the lines currently in the textarea. - if (lineUpOrDown && numberOfLines > 1 && row >= rowStart && row <= rowEnd){ - for (var i = 1; i <= row - rowStart; i++) { - selectionStart += host.session.getLine(row - i).length + 1; - selectionEnd += host.session.getLine(row - i).length + 1; - } - } else { - // If the new cursor position is one row above or below current rows - // in textarea, move page up or down. If not, set textarea to fresh - // set of rows around the cursor. - if (row === rowEnd + 1) { - rowStart = rowEnd + 1; - rowEnd = rowStart + numberOfLines - 1; - } else if (row === rowStart - 1) { - rowEnd = rowStart - 1; - rowStart = rowEnd - numberOfLines + 1; - } else { - rowStart = row > Math.floor(numberOfLines / 2) ? row - Math.floor(numberOfLines / 2) : 0; - rowEnd = row + Math.floor(numberOfLines / 2); - } - - var prevalue = ""; - var value = host.session.getLine(row); - var postvalue = ""; + for (var i = rowStart; i < row; i++) { + lines.push(host.session.getLine(i)); + } - for (var i = rowStart; i < row; i++) { - prevalue += host.session.getLine(i) + '\n'; - } - for (var i = row + 1; i <= rowEnd; i++) { - postvalue += host.session.getLine(i) + '\n'; - } + lines.push(host.session.getLine(row)); - if (numberOfLines > 1) { - line = prevalue + value + '\n' + postvalue; - } else { - line = value; - } + for (var i = row + 1; i <= rowEnd; i++) { + lines.push(host.session.getLine(i)); + } - for (var i = 1; i <= row - rowStart; i++) { - selectionStart += host.session.getLine(row - i).length + 1; - selectionEnd += host.session.getLine(row - i).length + 1; - } + line = lines.join('\n'); - if (numberOfLines === 1 && range.start.row != row) { - var prevLine = host.session.getLine(row - 1); - selectionStart = range.start.row < row - 1 ? 0 : selectionStart; - selectionEnd += prevLine.length + 1; - line = prevLine + "\n" + line; - } - else if (numberOfLines === 1 && range.end.row != row) { - var nextLine = host.session.getLine(row + 1); - selectionEnd = range.end.row > row + 1 ? nextLine.length : selectionEnd; - selectionEnd += line.length + 1; - line = line + "\n" + nextLine; - } - else if (isMobile && row > 0) { - line = "\n" + line; - selectionEnd += 1; - selectionStart += 1; - } + selectionStart = positionToSelection(range.start.row, range.start.column); + selectionEnd = positionToSelection(range.end.row, range.end.column); + + if (range.start.row < rowStart) { + var prevLine = host.session.getLine(rowStart - 1); + selectionStart = range.start.row < rowStart - 1 ? 0 : selectionStart; + selectionEnd += prevLine.length + 1; + line = prevLine + "\n" + line; + } + else if (range.end.row > rowEnd) { + var nextLine = host.session.getLine(rowEnd + 1); + selectionEnd = range.end.row > rowEnd + 1 ? nextLine.length : selectionEnd; + selectionEnd += line.length + 1; + line = line + "\n" + nextLine; + } + else if (isMobile && row > 0) { + line = "\n" + line; + selectionEnd += 1; + selectionStart += 1; + } - if (line.length > MAX_LINE_LENGTH) { - if (selectionStart < MAX_LINE_LENGTH && selectionEnd < MAX_LINE_LENGTH) { - line = line.slice(0, MAX_LINE_LENGTH); - } else { - line = "\n"; - if (selectionStart == selectionEnd) { - selectionStart = selectionEnd = 0; - } - else { - selectionStart = 0; - selectionEnd = 1; - } + if (line.length > MAX_LINE_LENGTH) { + if (selectionStart < MAX_LINE_LENGTH && selectionEnd < MAX_LINE_LENGTH) { + line = line.slice(0, MAX_LINE_LENGTH); + } else { + line = "\n"; + if (selectionStart == selectionEnd) { + selectionStart = selectionEnd = 0; + } + else { + selectionStart = 0; + selectionEnd = 1; } } - - var newValue = line + "\n\n"; - if (newValue != lastValue) { - text.value = lastValue = newValue; - lastSelectionStart = lastSelectionEnd = newValue.length; - } + } + + var newValue = line + "\n\n"; + if (newValue != lastValue) { + text.value = lastValue = newValue; + lastSelectionStart = lastSelectionEnd = newValue.length; } } From a721b7b2fa9d61681827af955c0e10c08fe3d98d Mon Sep 17 00:00:00 2001 From: Alice Date: Sat, 1 Jul 2023 00:55:27 +0200 Subject: [PATCH 07/17] add missing semi colon --- src/keyboard/textinput.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/keyboard/textinput.js b/src/keyboard/textinput.js index ee16e68e73..2619c93110 100644 --- a/src/keyboard/textinput.js +++ b/src/keyboard/textinput.js @@ -181,7 +181,7 @@ var TextInput = function(parentNode, host) { selection += host.session.getLine(row - i).length + 1; } return selection; - } + }; var resetSelection = isIOS ? function(value) { From 5aac6e657514f48effdd2ffdc6d5dccc06b63fb7 Mon Sep 17 00:00:00 2001 From: Alice Date: Mon, 3 Jul 2023 23:38:34 +0200 Subject: [PATCH 08/17] add test --- src/keyboard/textinput.js | 6 ++++-- src/keyboard/textinput_test.js | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/keyboard/textinput.js b/src/keyboard/textinput.js index 2619c93110..01d86992a3 100644 --- a/src/keyboard/textinput.js +++ b/src/keyboard/textinput.js @@ -45,7 +45,7 @@ var TextInput = function(parentNode, host) { var lastSelectionStart = 0; var lastSelectionEnd = 0; var lastRestoreEnd = 0; - var rowStart = -1; + var rowStart = Number.MAX_VALUE; var rowEnd = -1; var numberOfLines = 1; @@ -58,6 +58,8 @@ var TextInput = function(parentNode, host) { if (number < 1 || number % 2 === 0) { return; } + rowStart = Number.MAX_VALUE; + rowEnd = -1; numberOfLines = number; }; this.setAriaOptions = function(options) { @@ -259,7 +261,7 @@ var TextInput = function(parentNode, host) { } else if (range.end.row > rowEnd) { var nextLine = host.session.getLine(rowEnd + 1); - selectionEnd = range.end.row > rowEnd + 1 ? nextLine.length : selectionEnd; + selectionEnd = range.end.row > rowEnd + 1 ? nextLine.length : range.end.column; selectionEnd += line.length + 1; line = line + "\n" + nextLine; } diff --git a/src/keyboard/textinput_test.js b/src/keyboard/textinput_test.js index 9cf3f385f1..d40e117437 100644 --- a/src/keyboard/textinput_test.js +++ b/src/keyboard/textinput_test.js @@ -498,6 +498,40 @@ module.exports = { assert.equal([textarea.value.length, textarea.selectionStart, textarea.selectionEnd].join(","), "3,0,1"); }, + "test: selection synchronization 3 lines": function() { + editor.textInput.setNumberLines(3); + editor.session.setValue("line1\nline2\nline3\nline4\nline5\nline6\n"); + [ + { _: "keydown", range: [1,1], value: "line1\nline2\nline3\n\n", key: { code: "ArrowRight", key: "ArrowRight", keyCode: 39}}, + { _: "keydown", range: [2,2], value: "line1\nline2\nline3\n\n", key: { code: "ArrowRight", key: "ArrowRight", keyCode: 39}}, + { _: "keydown", range: [2,2], value: "line1\nline2\nline3\n\n", key: { code: "ShiftLeft", key: "Shift", keyCode: 16}, modifier: "shift-"}, + { _: "keydown", range: [2,3], value: "line1\nline2\nline3\n\n", key: { code: "ArrowRight", key: "ArrowRight", keyCode: 39}, modifier: "shift-"}, + { _: "keydown", range: [2,4], value: "line1\nline2\nline3\n\n", key: { code: "ArrowRight", key: "ArrowRight", keyCode: 39}, modifier: "shift-"}, + { _: "keydown", range: [2,5], value: "line1\nline2\nline3\n\n", key: { code: "ArrowRight", key: "ArrowRight", keyCode: 39}, modifier: "shift-"}, + { _: "keydown", range: [2,6], value: "line1\nline2\nline3\n\n", key: { code: "ArrowRight", key: "ArrowRight", keyCode: 39}, modifier: "shift-"}, + { _: "keydown", range: [2,7], value: "line1\nline2\nline3\n\n", key: { code: "ArrowRight", key: "ArrowRight", keyCode: 39}, modifier: "shift-"}, + { _: "keydown", range: [2,8], value: "line1\nline2\nline3\n\n", key: { code: "ArrowRight", key: "ArrowRight", keyCode: 39}, modifier: "shift-"}, + { _: "keydown", range: [2,14], value: "line1\nline2\nline3\n\n", key: { code: "ArrowDown", key: "ArrowDown", keyCode: 40}, modifier: "shift-"}, + { _: "keydown", range: [2,2], value: "line4\nline5\nline6\n\n", key: { code: "ArrowDown", key: "ArrowDown", keyCode: 40}}, + { _: "keydown", range: [2,2], value: "line4\nline5\nline6\n\n", key: { code: "ShiftLeft", key: "Shift", keyCode: 16}, modifier: "shift-"}, + { _: "keydown", range: [14,20], value: "line1\nline2\nline3\nline4\n\n", key: { code: "ArrowUp", key: "ArrowUp", keyCode: 38}, modifier: "shift-"}, + { _: "keydown", range: [8,8], value: "line1\nline2\nline3\n\n", key: { code: "ArrowUp", key: "ArrowUp", keyCode: 38}}, + { _: "keydown", range: [14,14], value: "line1\nline2\nline3\n\n", key: { code: "ArrowDown", key: "ArrowDown", keyCode: 40}}, + { _: "keydown", range: [2,8], value: "line3\nline4\nline5\nline6\n\n", key: { code: "ArrowDown", key: "ArrowDown", keyCode: 40}, modifier: "shift-"} + ].forEach(function(data) { + sendEvent(data._, data); + }); + // test overflow + editor.session.setValue("0123456789".repeat(80)); + editor.execCommand("gotoright"); + editor.execCommand("selectright"); + assert.equal([textarea.value.length, textarea.selectionStart, textarea.selectionEnd].join(","), "402,1,2"); + editor.execCommand("gotolineend"); + assert.equal([textarea.value.length, textarea.selectionStart, textarea.selectionEnd].join(","), "3,0,0"); + editor.execCommand("selectleft"); + assert.equal([textarea.value.length, textarea.selectionStart, textarea.selectionEnd].join(","), "3,0,1"); + }, + "test: chinese ime on ie": function() { editor.setOption("useTextareaForIME", false); [ From 8ef6473e920763159bd4700ef55c34a6cd0c93eb Mon Sep 17 00:00:00 2001 From: Alice Date: Tue, 4 Jul 2023 21:32:01 +0200 Subject: [PATCH 09/17] renaming --- src/editor.js | 4 ++-- src/keyboard/textinput.js | 31 +++++++++++++++++-------------- src/keyboard/textinput_test.js | 4 ++-- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/editor.js b/src/editor.js index 1860620f49..91b8a07092 100644 --- a/src/editor.js +++ b/src/editor.js @@ -2896,7 +2896,7 @@ config.defineOptions(Editor.prototype, "editor", { this.renderer.keyboardFocusClassName = "ace_keyboard-focus"; this.textInput.getElement().setAttribute("tabindex", -1); - this.textInput.setNumberLines(3); + this.textInput.setNumberOfExtraLines(1); this.renderer.scroller.setAttribute("tabindex", 0); this.renderer.scroller.setAttribute("role", "group"); this.renderer.scroller.setAttribute("aria-roledescription", nls("editor")); @@ -2927,7 +2927,7 @@ config.defineOptions(Editor.prototype, "editor", { this.renderer.enableKeyboardAccessibility = false; this.textInput.getElement().setAttribute("tabindex", 0); - this.textInput.setNumberLines(1); + this.textInput.setNumberOfExtraLines(0); this.renderer.scroller.setAttribute("tabindex", -1); this.renderer.scroller.removeAttribute("role"); this.renderer.scroller.removeAttribute("aria-roledescription"); diff --git a/src/keyboard/textinput.js b/src/keyboard/textinput.js index 01d86992a3..200ec1cdb5 100644 --- a/src/keyboard/textinput.js +++ b/src/keyboard/textinput.js @@ -45,22 +45,25 @@ var TextInput = function(parentNode, host) { var lastSelectionStart = 0; var lastSelectionEnd = 0; var lastRestoreEnd = 0; - var rowStart = Number.MAX_VALUE; - var rowEnd = -1; - var numberOfLines = 1; + var rowStart = Number.MAX_SAFE_INTEGER; + var rowEnd = Number.MIN_SAFE_INTEGER; + var numberOfExtraLines = 0; // FOCUS // ie9 throws error if document.activeElement is accessed too soon try { var isFocused = document.activeElement === text; } catch(e) {} - // Set number of lines, needs to be 1 or greater and odd. - this.setNumberLines = function(number) { - if (number < 1 || number % 2 === 0) { + // Set number of extra lines.. + this.setNumberOfExtraLines = function(number) { + rowStart = Number.MAX_SAFE_INTEGER; + rowEnd = Number.MIN_SAFE_INTEGER; + + if (number < 0) { + numberOfExtraLines = 0; return; } - rowStart = Number.MAX_VALUE; - rowEnd = -1; - numberOfLines = number; + + numberOfExtraLines = number; }; this.setAriaOptions = function(options) { if (options.activeDescendant) { @@ -179,7 +182,7 @@ var TextInput = function(parentNode, host) { var positionToSelection = function(row, column) { var selection = column; - for (var i = 1; i <= row - rowStart && i < numberOfLines; i++) { + for (var i = 1; i <= row - rowStart && i < 2*numberOfExtraLines + 1; i++) { selection += host.session.getLine(row - i).length + 1; } return selection; @@ -227,13 +230,13 @@ var TextInput = function(parentNode, host) { // set of rows around the cursor. if (row === rowEnd + 1) { rowStart = rowEnd + 1; - rowEnd = rowStart + numberOfLines - 1; + rowEnd = rowStart + 2*numberOfExtraLines; } else if (row === rowStart - 1) { rowEnd = rowStart - 1; - rowStart = rowEnd - numberOfLines + 1; + rowStart = rowEnd - 2*numberOfExtraLines; } else if (row < rowStart - 1 || row > rowEnd + 1) { - rowStart = row > Math.floor(numberOfLines / 2) ? row - Math.floor(numberOfLines / 2) : 0; - rowEnd = row + Math.floor(numberOfLines / 2); + rowStart = row > numberOfExtraLines ? row - numberOfExtraLines : 0; + rowEnd = row > numberOfExtraLines ? row + numberOfExtraLines : 2*numberOfExtraLines; } var lines = []; diff --git a/src/keyboard/textinput_test.js b/src/keyboard/textinput_test.js index d40e117437..6929d4da21 100644 --- a/src/keyboard/textinput_test.js +++ b/src/keyboard/textinput_test.js @@ -498,8 +498,8 @@ module.exports = { assert.equal([textarea.value.length, textarea.selectionStart, textarea.selectionEnd].join(","), "3,0,1"); }, - "test: selection synchronization 3 lines": function() { - editor.textInput.setNumberLines(3); + "test: selection synchronization with extra lines enabled": function() { + editor.textInput.setNumberOfExtraLines(1); editor.session.setValue("line1\nline2\nline3\nline4\nline5\nline6\n"); [ { _: "keydown", range: [1,1], value: "line1\nline2\nline3\n\n", key: { code: "ArrowRight", key: "ArrowRight", keyCode: 39}}, From abb9d79220b29e4dfc69915c747cfd4d2005b056 Mon Sep 17 00:00:00 2001 From: Alice Date: Wed, 5 Jul 2023 23:47:31 +0200 Subject: [PATCH 10/17] tweaks --- src/editor.js | 2 +- src/keyboard/textinput.js | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/editor.js b/src/editor.js index 91b8a07092..3338cb0850 100644 --- a/src/editor.js +++ b/src/editor.js @@ -2896,7 +2896,7 @@ config.defineOptions(Editor.prototype, "editor", { this.renderer.keyboardFocusClassName = "ace_keyboard-focus"; this.textInput.getElement().setAttribute("tabindex", -1); - this.textInput.setNumberOfExtraLines(1); + this.textInput.setNumberOfExtraLines(3); this.renderer.scroller.setAttribute("tabindex", 0); this.renderer.scroller.setAttribute("role", "group"); this.renderer.scroller.setAttribute("aria-roledescription", nls("editor")); diff --git a/src/keyboard/textinput.js b/src/keyboard/textinput.js index 200ec1cdb5..8937a731b5 100644 --- a/src/keyboard/textinput.js +++ b/src/keyboard/textinput.js @@ -78,13 +78,16 @@ var TextInput = function(parentNode, host) { if (options.role) { text.setAttribute("role", options.role); } - if (options.setAriaLabel) { + if (options.label) { text.setAttribute("aria-roledescription", nls("editor")); if(host.session) { var row = host.session.selection.cursor.row; text.setAttribute("aria-label", nls("Cursor at row $0", [row + 1])); } } + if (options.multiline) { + text.setAttribute("aria-multiline", true); + } }; this.setAriaOptions({role: "textbox"}); @@ -118,7 +121,8 @@ var TextInput = function(parentNode, host) { this.focus = function() { // On focusing on the textarea, read active row number to assistive tech. this.setAriaOptions({ - setAriaLabel: host.renderer.enableKeyboardAccessibility + label: host.renderer.enableKeyboardAccessibility, + multiline: true }); if (tempStyle || HAS_FOCUS_ARGS || this.$focusScroll == "browser") From ec28aaccb6b1d0c938a51143f6b2613e64204329 Mon Sep 17 00:00:00 2001 From: Alice Date: Fri, 7 Jul 2023 23:14:48 +0200 Subject: [PATCH 11/17] only set more lines on non-mac-os --- src/editor.js | 4 +++- src/keyboard/textinput.js | 6 ++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/editor.js b/src/editor.js index 3338cb0850..fad7522a81 100644 --- a/src/editor.js +++ b/src/editor.js @@ -2896,7 +2896,9 @@ config.defineOptions(Editor.prototype, "editor", { this.renderer.keyboardFocusClassName = "ace_keyboard-focus"; this.textInput.getElement().setAttribute("tabindex", -1); - this.textInput.setNumberOfExtraLines(3); + // VoiceOver on Mac OS works best single line in the textarea, the screen readers on Windows work best + // with multiple lines in the textarea. + this.textInput.setNumberOfExtraLines(!useragent.isMac ? 3 : 0); this.renderer.scroller.setAttribute("tabindex", 0); this.renderer.scroller.setAttribute("role", "group"); this.renderer.scroller.setAttribute("aria-roledescription", nls("editor")); diff --git a/src/keyboard/textinput.js b/src/keyboard/textinput.js index 8937a731b5..70be530433 100644 --- a/src/keyboard/textinput.js +++ b/src/keyboard/textinput.js @@ -25,6 +25,7 @@ var TextInput = function(parentNode, host) { text.setAttribute("autocorrect", "off"); text.setAttribute("autocapitalize", "off"); text.setAttribute("spellcheck", false); + text.setAttribute("aria-multiline", true); text.style.opacity = "0"; parentNode.insertBefore(text, parentNode.firstChild); @@ -53,7 +54,7 @@ var TextInput = function(parentNode, host) { // ie9 throws error if document.activeElement is accessed too soon try { var isFocused = document.activeElement === text; } catch(e) {} - // Set number of extra lines.. + // Set number of extra lines. this.setNumberOfExtraLines = function(number) { rowStart = Number.MAX_SAFE_INTEGER; rowEnd = Number.MIN_SAFE_INTEGER; @@ -85,9 +86,6 @@ var TextInput = function(parentNode, host) { text.setAttribute("aria-label", nls("Cursor at row $0", [row + 1])); } } - if (options.multiline) { - text.setAttribute("aria-multiline", true); - } }; this.setAriaOptions({role: "textbox"}); From b1028c0a9ca2b9d76ce277a49d0c05d28fc651f6 Mon Sep 17 00:00:00 2001 From: Alice Date: Fri, 7 Jul 2023 23:16:28 +0200 Subject: [PATCH 12/17] typo --- src/editor.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/editor.js b/src/editor.js index fad7522a81..9be0628f1e 100644 --- a/src/editor.js +++ b/src/editor.js @@ -2896,8 +2896,8 @@ config.defineOptions(Editor.prototype, "editor", { this.renderer.keyboardFocusClassName = "ace_keyboard-focus"; this.textInput.getElement().setAttribute("tabindex", -1); - // VoiceOver on Mac OS works best single line in the textarea, the screen readers on Windows work best - // with multiple lines in the textarea. + // VoiceOver on Mac OS works best with single line in the textarea, the screen readers on + // Windows work best with multiple lines in the textarea. this.textInput.setNumberOfExtraLines(!useragent.isMac ? 3 : 0); this.renderer.scroller.setAttribute("tabindex", 0); this.renderer.scroller.setAttribute("role", "group"); From 8b0feb7944cde50d82d199bd48f856c692a4c16e Mon Sep 17 00:00:00 2001 From: Alice Date: Fri, 7 Jul 2023 23:19:08 +0200 Subject: [PATCH 13/17] comment update --- src/keyboard/textinput.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/keyboard/textinput.js b/src/keyboard/textinput.js index 70be530433..87f661355f 100644 --- a/src/keyboard/textinput.js +++ b/src/keyboard/textinput.js @@ -54,7 +54,8 @@ var TextInput = function(parentNode, host) { // ie9 throws error if document.activeElement is accessed too soon try { var isFocused = document.activeElement === text; } catch(e) {} - // Set number of extra lines. + // Set number of extra lines in textarea, some screenreaders + // perform better with extra lines above and below in the textarea. this.setNumberOfExtraLines = function(number) { rowStart = Number.MAX_SAFE_INTEGER; rowEnd = Number.MIN_SAFE_INTEGER; From 0735281cc32c741e3a86832e8130f4c95ad3f253 Mon Sep 17 00:00:00 2001 From: Alice Koreman Date: Fri, 7 Jul 2023 23:31:59 +0200 Subject: [PATCH 14/17] prevent negative --- src/editor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/editor.js b/src/editor.js index 9be0628f1e..9762f42529 100644 --- a/src/editor.js +++ b/src/editor.js @@ -2898,7 +2898,7 @@ config.defineOptions(Editor.prototype, "editor", { this.textInput.getElement().setAttribute("tabindex", -1); // VoiceOver on Mac OS works best with single line in the textarea, the screen readers on // Windows work best with multiple lines in the textarea. - this.textInput.setNumberOfExtraLines(!useragent.isMac ? 3 : 0); + this.textInput.setNumberOfExtraLines(useragent.isMac ? 0 : 3); this.renderer.scroller.setAttribute("tabindex", 0); this.renderer.scroller.setAttribute("role", "group"); this.renderer.scroller.setAttribute("aria-roledescription", nls("editor")); From a309afbd01278f181e6077e1bfdcad50946b68ad Mon Sep 17 00:00:00 2001 From: Alice Date: Fri, 7 Jul 2023 23:55:34 +0200 Subject: [PATCH 15/17] improve comments --- src/editor.js | 9 ++++++--- src/keyboard/textinput.js | 10 +++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/editor.js b/src/editor.js index 9762f42529..fbed78eb57 100644 --- a/src/editor.js +++ b/src/editor.js @@ -2889,8 +2889,11 @@ config.defineOptions(Editor.prototype, "editor", { var gutterKeyboardHandler; - // Prevent focus to be captured when tabbing through the page. When focus is set to the content div, - // press Enter key to give focus to Ace and press Esc to again allow to tab through the page. + // If keyboard a11y mode is enabled we: + // - Enable keyboard operability gutter. + // - Prevent tab-trapping. + // - Hide irrelevant elements from assistive technology. + // - On Windows, set more lines to the textarea. if (value){ this.renderer.enableKeyboardAccessibility = true; this.renderer.keyboardFocusClassName = "ace_keyboard-focus"; @@ -2898,7 +2901,7 @@ config.defineOptions(Editor.prototype, "editor", { this.textInput.getElement().setAttribute("tabindex", -1); // VoiceOver on Mac OS works best with single line in the textarea, the screen readers on // Windows work best with multiple lines in the textarea. - this.textInput.setNumberOfExtraLines(useragent.isMac ? 0 : 3); + this.textInput.setNumberOfExtraLines(useragent.isWin ? 3 : 0); this.renderer.scroller.setAttribute("tabindex", 0); this.renderer.scroller.setAttribute("role", "group"); this.renderer.scroller.setAttribute("aria-roledescription", nls("editor")); diff --git a/src/keyboard/textinput.js b/src/keyboard/textinput.js index 87f661355f..0dcfc279ad 100644 --- a/src/keyboard/textinput.js +++ b/src/keyboard/textinput.js @@ -182,6 +182,8 @@ var TextInput = function(parentNode, host) { resetSelection(); }); + // Covert from row,column position to the linear position with respect to the current + // block of lines in the textarea. var positionToSelection = function(row, column) { var selection = column; @@ -228,9 +230,11 @@ var TextInput = function(parentNode, host) { var range = selection.getRange(); var row = selection.cursor.row; - // If the new cursor position is one row above or below current rows - // in textarea, move page up or down. If not, set textarea to fresh - // set of rows around the cursor. + // We keep 2*numberOfExtraLines + 1 lines in the textarea, if the new active row + // is within the current block of lines in the textarea we do nothing. If the new row + // is one row above or below the current block, move up or down to the next block of lines. + // If the new row is further than 1 row away from the current block grab a new block centered + // around the new row. if (row === rowEnd + 1) { rowStart = rowEnd + 1; rowEnd = rowStart + 2*numberOfExtraLines; From 1ee32b9f9f24fd26befb249175c1b35f2a3b6b5c Mon Sep 17 00:00:00 2001 From: Alice Date: Fri, 7 Jul 2023 23:59:50 +0200 Subject: [PATCH 16/17] cleanup --- src/keyboard/textinput.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/keyboard/textinput.js b/src/keyboard/textinput.js index 0dcfc279ad..d302649dff 100644 --- a/src/keyboard/textinput.js +++ b/src/keyboard/textinput.js @@ -120,8 +120,7 @@ var TextInput = function(parentNode, host) { this.focus = function() { // On focusing on the textarea, read active row number to assistive tech. this.setAriaOptions({ - label: host.renderer.enableKeyboardAccessibility, - multiline: true + label: host.renderer.enableKeyboardAccessibility }); if (tempStyle || HAS_FOCUS_ARGS || this.$focusScroll == "browser") From 5e0a7e3f417faa5453a60e173593644e634c4c26 Mon Sep 17 00:00:00 2001 From: Alice Koreman Date: Wed, 12 Jul 2023 14:35:55 +0200 Subject: [PATCH 17/17] PR comment follow-up --- src/keyboard/textinput.js | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/keyboard/textinput.js b/src/keyboard/textinput.js index c7f41ede19..0dfe81dc65 100644 --- a/src/keyboard/textinput.js +++ b/src/keyboard/textinput.js @@ -25,7 +25,6 @@ var TextInput = function(parentNode, host) { text.setAttribute("autocorrect", "off"); text.setAttribute("autocapitalize", "off"); text.setAttribute("spellcheck", false); - text.setAttribute("aria-multiline", true); text.style.opacity = "0"; parentNode.insertBefore(text, parentNode.firstChild); @@ -80,7 +79,7 @@ var TextInput = function(parentNode, host) { if (options.role) { text.setAttribute("role", options.role); } - if (options.label) { + if (options.setLabel) { text.setAttribute("aria-roledescription", nls("editor")); if(host.session) { var row = host.session.selection.cursor.row; @@ -116,7 +115,7 @@ var TextInput = function(parentNode, host) { this.focus = function() { // On focusing on the textarea, read active row number to assistive tech. this.setAriaOptions({ - label: host.renderer.enableKeyboardAccessibility + setLabel: host.renderer.enableKeyboardAccessibility }); if (tempStyle || HAS_FOCUS_ARGS || this.$focusScroll == "browser") @@ -177,7 +176,7 @@ var TextInput = function(parentNode, host) { resetSelection(); }); - // Covert from row,column position to the linear position with respect to the current + // Convert from row,column position to the linear position with respect to the current // block of lines in the textarea. var positionToSelection = function(row, column) { var selection = column; @@ -243,16 +242,10 @@ var TextInput = function(parentNode, host) { var lines = []; - for (var i = rowStart; i < row; i++) { + for (var i = rowStart; i <= rowEnd; i++) { lines.push(host.session.getLine(i)); } - - lines.push(host.session.getLine(row)); - - for (var i = row + 1; i <= rowEnd; i++) { - lines.push(host.session.getLine(i)); - } - + line = lines.join('\n'); selectionStart = positionToSelection(range.start.row, range.start.column);