Unicode Support / Cursor issues Fix (#740)

* Add container with a fixed width around unicode characters

* Make container width dynamic and fix wide chars clipping issue

* Finer control on the creation of containers for text inserted in the terminal
This commit is contained in:
Benoit Averty 2016-11-18 02:12:23 +01:00 committed by Guillermo Rauch
parent 27a20e0cfc
commit da4858a76e
2 changed files with 102 additions and 3 deletions

View file

@ -1,10 +1,16 @@
import {hterm, lib} from 'hterm-umdjs';
import fromCharCode from './utils/key-code';
import runes from 'runes';
const selection = require('./utils/selection');
import fromCharCode from './utils/key-code';
import selection from './utils/selection';
hterm.defaultStorage = new lib.Storage.Memory();
// The current width of characters rendered in hterm
let charWidth;
// Containers to resize when char width changes
const containers = [];
// Provide selectAll to terminal viewport
hterm.Terminal.prototype.selectAll = function () {
// We need to clear the DOM range to reset anchorNode
@ -12,6 +18,25 @@ hterm.Terminal.prototype.selectAll = function () {
selection.all(this);
};
const oldSetFontSize = hterm.Terminal.prototype.setFontSize;
hterm.Terminal.prototype.setFontSize = function (px) {
oldSetFontSize.call(this, px);
charWidth = this.scrollPort_.characterSize.width;
// @TODO Maybe clear old spans from the list of spans to resize ?
// Resize all containers to match the new whar width.
containers.forEach(container => {
if (container && container.style) {
container.style.width = `${container.wcNode ? charWidth * 2 : charWidth}px`;
}
});
};
const oldSyncFontFamily = hterm.Terminal.prototype.syncFontFamily;
hterm.Terminal.prototype.syncFontFamily = function () {
oldSyncFontFamily.call(this);
this.setFontSize();
};
// override double click behavior to copy
const oldMouse = hterm.Terminal.prototype.onMouse_;
hterm.Terminal.prototype.onMouse_ = function (e) {
@ -23,6 +48,79 @@ hterm.Terminal.prototype.onMouse_ = function (e) {
return oldMouse.call(this, e);
};
function containsNonLatinCodepoints(s) {
return /[^\u0000-\u00ff]/.test(s);
}
hterm.Terminal.IO.prototype.writeUTF8 = function (string) {
if (this.terminal_.io !== this) {
throw new Error('Attempt to print from inactive IO object.');
}
if (containsNonLatinCodepoints(string)) {
const splitString = runes(string);
const length = splitString.length;
this.terminal_.getTextAttributes().hasUnicode = true;
for (let curChar = 0; curChar <= length; curChar++) {
this.terminal_.interpret(splitString[curChar]);
}
this.terminal_.getTextAttributes().hasUnicode = false;
} else {
this.terminal_.interpret(string);
}
};
const oldCreateContainer = hterm.TextAttributes.prototype.createContainer;
hterm.TextAttributes.prototype.createContainer = function (text) {
const container = oldCreateContainer.call(this, text);
if (container.style && text.length === 1 && containsNonLatinCodepoints(text)) {
container.style.width = `${container.wcNode ? charWidth * 2 : charWidth}px`;
container.style.display = 'inline-block';
// If the container has unicode text, the char can overlap neigbouring containers. We need
// to ensure that the text is not hidden behind other containers.
container.style.overflow = 'visible';
container.style.position = 'relative';
// Remember this container to resize it later when font size changes.
containers.push(container);
}
return container;
};
// Do not match containers when one of them has unicode text (unicode chars need to be alone in their containers)
const oldMatchesContainer = hterm.TextAttributes.prototype.matchesContainer;
hterm.TextAttributes.prototype.matchesContainer = function (obj) {
const content = typeof obj === 'string' ? obj : obj.textContent;
if (containsNonLatinCodepoints(content)) {
return false;
}
if (this.hasUnicode) {
return false;
}
return oldMatchesContainer.call(this, obj);
};
/**
* Override 'containersMatch' so that containers with unicode do not match anything.
*/
const oldContainersMatch = hterm.TextAttributes.containersMatch;
hterm.TextAttributes.containersMatch = function (obj1, obj2) {
const content1 = typeof obj1 === 'string' ? obj1 : obj1.textContent;
const content2 = typeof obj2 === 'string' ? obj2 : obj2.textContent;
if (containsNonLatinCodepoints(content1) || containsNonLatinCodepoints(content2)) {
return false;
}
return oldContainersMatch(obj1, obj2);
};
// there's no option to turn off the size overlay
hterm.Terminal.prototype.overlaySize = function () {};

View file

@ -87,7 +87,8 @@
"reselect": "2.5.4",
"seamless-immutable": "6.1.3",
"semver": "5.3.0",
"uuid": "2.0.2"
"uuid": "2.0.2",
"runes": "^0.3.0"
},
"devDependencies": {
"ava": "^0.16.0",