Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
5203c96
WIP: add text width measurement and cursor positioning based on pixel…
mkslanc Jul 24, 2025
982731e
text width calculation based on createRange
mkslanc Jul 25, 2025
3c0e0c0
refactor
nightwing Feb 22, 2026
30ee806
test
nightwing Feb 22, 2026
66b07cb
address review comments
nightwing Feb 25, 2026
108fe73
fix most of failing tests
nightwing Mar 23, 2026
685d2af
fix rendering of cjk spaces with show invisibles
nightwing Mar 24, 2026
d5684d1
fix textinput tests
nightwing Mar 25, 2026
037318f
css transform support in mockdom
nightwing Apr 9, 2026
6952ce9
support css transform in new measurement method
nightwing Apr 11, 2026
086b165
fix failing test
nightwing Apr 30, 2026
d314446
fix types
nightwing May 2, 2026
dc36092
improve coverage
nightwing May 2, 2026
a71a0a0
fix position calculation for surrogate pairs
nightwing May 4, 2026
17cae98
fix selection rendering in RTL mode
nightwing May 22, 2026
0f32ef2
fix: add width to fallback; remove unnesseccary check
mkslanc May 19, 2026
0fbd29a
fix: improve text-to-screen coordinate calculation
mkslanc May 21, 2026
6fb71cf
workaround for slow pixelToColumn on long tokens
nightwing Jun 15, 2026
62bf8ce
fix: remove unused `offsetX` parameter from `screenToDocumentPosition…
mkslanc May 28, 2026
45108d6
fix: enhance `$pixelToColumn` with `isTextWidthCoordinate` for improv…
mkslanc May 26, 2026
23ffa01
revert binary search changes as it doesn't work well with bidi text
nightwing Jun 20, 2026
c6c9f87
improve handling of bidi text
nightwing Jul 2, 2026
7317a8f
partial fix for horizontal scroll
nightwing Jul 8, 2026
32ae297
update node in ci, since new typescript doesn't work with node 16
nightwing Jul 10, 2026
40e5bdc
fix multiline selection marker on wrapped bidi lines
nightwing Jul 10, 2026
8f9018c
cleanup
nightwing Jul 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:

strategy:
matrix:
node-version: [16.x]
node-version: [22.x]

steps:
- uses: actions/checkout@v2
Expand Down
11 changes: 11 additions & 0 deletions ace-internal.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,16 @@ export namespace Ace {
offset: number,
height: number,
gutterOffset: number
fontMetrics: {
textWidth: (row: number, column: number) => number,
getRects: (start: Position, end: Position) => Rect[]
}
}
interface Rect {
left: number,
top: number,
width: number,
height: number,
}

interface HardWrapOptions {
Expand Down Expand Up @@ -1449,6 +1459,7 @@ declare module "./src/edit_session" {
_changedWidgets?: any,
$options: any,
$wrapMethod?: any,
$fontMetrics?: FontMetrics|null,
$enableVarChar?: any,
$wrap?: any,
$navigateWithinSoftTabs?: boolean,
Expand Down
10 changes: 10 additions & 0 deletions ace.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,16 @@ declare module "ace-code" {
offset: number;
height: number;
gutterOffset: number;
fontMetrics: {
textWidth: (row: number, column: number) => number;
getRects: (start: Position, end: Position) => Rect[];
};
}
interface Rect {
left: number;
top: number;
width: number;
height: number;
}
interface HardWrapOptions {
/** First row of the range to process */
Expand Down
10 changes: 10 additions & 0 deletions demo/kitchen-sink/demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,16 @@ optionsPanelContainer.insertBefore(
"Open Dialog ",
["button", {onclick: openTestDialog.bind(null, false)}, "Scale"],
["button", {onclick: openTestDialog.bind(null, true)}, "Height"]
],
["div", {},
["button", {onclick: function() {
editor.setOption("fontFamily", "cursive");
session.setValue( session.getValue() + "שלום עולם בעברית123" +"\n" + "ジャパン + 八洲\n" + "𒐫𒈙⸻ဪ", 1);
}}, "cursive"],
["button", {onclick: function() {
editor.setOption("fontFamily", "Tahoma");
session.setValue( session.getValue() + "שלום עולם בעברית123" +"\n" + "ジャパン + 八洲", 1);
}}, "Tahoma"],
]
]),
optionsPanelContainer.children[1]
Expand Down
2 changes: 1 addition & 1 deletion demo/kitchen-sink/inline_editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ require("ace/commands/default_commands").commands.push({
return;
}

var rowCount = 10;
var rowCount = 5.5;
var w = {
row: row,
// rowCount: rowCount,
Expand Down
146 changes: 146 additions & 0 deletions experiments/bidi.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>


<script type="text/javascript" charset="utf-8">

function renderDomPointsPlot(text) {
// 1. Create measurement container
var measureDiv = document.createElement('div');
measureDiv.style.font = '24px monospace';
measureDiv.style.position = 'absolute';
measureDiv.style.visibility = 'hidden';
measureDiv.innerText = text;
document.body.appendChild(measureDiv);

var range = document.createRange();
var textNode = measureDiv.firstChild;
var data = [];

// 2. Measure each character's X position
for (let i = 0; i <= text.length; i++) {
// Skip measuring the invisible control characters
// if (text[i].charCodeAt(0) > 0x2000) continue;

range.setStart(textNode, i);
range.setEnd(textNode, i);
data.push({
char: text[i],
x: range.getBoundingClientRect().left
});
}
document.body.removeChild(measureDiv);

// 3. Create a container for the DOM plot
var plotArea = document.createElement('div');
plotArea.style.position = 'relative';
plotArea.style.width = '100%';
plotArea.style.height = '500px';
plotArea.style.border = '2px solid #333';
plotArea.style.backgroundColor = '#f4f4f9';
plotArea.style.margin = '20px 0';
plotArea.style.fontFamily = 'sans-serif';
plotArea.style.overflow = 'hidden';

// Add informative header
// var header = document.createElement('div');
// header.style.padding = '10px';
// header.style.fontSize = '13px';
// header.style.color = '#333';
// header.textContent = text
// plotArea.appendChild(header);

// Scaling variables to fit points nicely inside the 650x250 container
var spacingX = 35;
var scaleY = 0.8;

// 4. Create DOM elements for each point
data.forEach((item, index) => {
var point = document.createElement('div');

// Style as a dot
point.style.position = 'absolute';
point.style.width = '12px';
point.style.height = '12px';
point.style.borderRadius = '50%';
point.style.backgroundColor = '#ff5722';
point.style.boxShadow = '0 2px 4px rgba(0,0,0,0.2)';

// Tooltip showing data
point.title = `Character: '${item.char}' | Index: ${index} | Visual X: ${item.x.toFixed(1)}px`;

// Position: left corresponds to index, bottom corresponds to visual coordinate
var leftPos = (index * spacingX) + 25;
var bottomPos = (item.x * scaleY) + 30;

point.style.left = `${leftPos}px`;
point.style.bottom = `${bottomPos}px`;

// Add the character label directly beneath the point
var charLabel = document.createElement('span');
charLabel.innerText = item.char;
charLabel.style.position = 'absolute';
charLabel.style.top = '15px';
charLabel.style.left = '20px';
charLabel.style.fontSize = '14px';
charLabel.style.fontWeight = 'bold';
charLabel.style.color = '#333';
point.appendChild(charLabel);

point.className = "point"


vline = document.createElement('div');
vline.style.cssText = `
position: absolute;
width: 100vw;
height: 1px;
background-color: #007bff;
left: -${point.style.left};
top: 5px;
`;
point.appendChild(vline)
hline = document.createElement('div');
hline.style.cssText = `
position: absolute;
width: 1px;
height: 100vh;
background-color: #007bff;
top: -${point.style.top};
left: 5px;
`;
point.appendChild(hline)

plotArea.appendChild(point);
});

// Append the whole plot to the main document body
document.body.appendChild(plotArea);
}

// Execute function with your nested isolate string
var nestedString = "abc\u2067d123ef\u2066ghk\u2067lmn\u2069\u2069\u2069opqrst";
var nestedString= 'a 八洲שלום ע1ולם בעברית124e443ジ'

document.body.innerHTML=``
var ta = document.createElement('textarea');
ta.id = 'input';
ta.style.width = '100%';
document.documentElement.insertBefore(ta, document.body);


ta.value = nestedString;
ta.oninput = () => {
document.body.innerHTML=``
document.querySelectorAll('.point').forEach(p => p.remove());
renderDomPointsPlot(ta.value);
}
renderDomPointsPlot(nestedString);

</script>

</body>
</html>
Loading
Loading