Skip to content
This repository was archived by the owner on May 8, 2019. It is now read-only.

Unicode (read: Emoji) supportΒ #6

@RLovelett

Description

@RLovelett

When auto-completing a line that contains an emoji (or any other multi-byte unicode character) the suggestions stop working.

For instance, typing let πŸ’― = Foo( does accurately bring up the suggestion box. However the suggestions are not relevant to the location of the current cursor position.

Overview

The problem is that FullTextDocument does not handle Unicode characters.

For example, say we have two Swift source documents:

ascii.swift

struct Foo {
    let bar: Int
}

let x = Foo()

unicode.swift

struct Foo {
    let bar: Int
}

let πŸ’― = Foo()

Both source documents have the same number of code points, e.g., 46, but they have a different number of bytes, e.g., 46 for ascii.swift and 49 for unicode.swift.

Therefore, if you were to ask for the byte-offset of the closing parenthesis in ascii.swift it would be 45. Compare that with unicode.swift which would have the value 48.

Example

Using the same above documents, ascii.swift and unicode.swift.

import * as fs from 'fs';
import { TextDocument, Position } from 'vscode-languageserver';

const ascii = '/path/to/ascii.swift';
const unicode = '/path/to/unicode.swift';

// Load the text documents
let asciiBuffer: Promise<Buffer> = new Promise((resolve, reject) => {
    fs.readFile(ascii, (err, data) => {
        if (err) { reject(err); }
        else { resolve(data); }
    });
});

let unicodeBuffer: Promise<Buffer> = new Promise((resolve, reject) => {
    fs.readFile(unicode, (err, data) => {
        if (err) { reject(err); }
        else { resolve(data); }
    });
});

// REMEMBER Position is zero indexed!
// https://github.com/Microsoft/vscode-languageserver-node/blob/a9f36d43a789e6fd9c16e5e50fc818eb35d097db/types/src/main.ts#L12
let position = Position.create(4, 12);

let asciiByteOffset = asciiBuffer.then((buffer) => TextDocument.create(ascii, 'swift', 1, buffer.toString('utf8')))
  .then((document) => document.offsetAt(position))
  .then(console.log); // logs 45 βœ…

let unicodeByteOffset = unicodeBuffer.then((buffer) => TextDocument.create(unicode, 'swift', 1, buffer.toString('utf8')))
  .then((document) => document.offsetAt(position))
  .then(console.log); // logs 45 ❌

Resolution?

One idea that I've been working towards is creating a new class UnicodeTextDocument that conforms to the TextDocument interface.

Which could serve as a drop-in replacement for TextDocument that transparently provides byte-offset.

Such that you could do:

let asciiByteOffset = asciiBuffer.then((buffer) => new UnicodeTextDocument(ascii, 'swift', 1, buffer.toString('utf8')))
  .then((document) => document.offsetAt(position))
  .then(console.log); // logs 45 βœ…

let unicodeByteOffset = unicodeBuffer.then((buffer) => new UnicodeTextDocument(unicode, 'swift', 1, buffer.toString('utf8')))
  .then((document) => document.offsetAt(position))
  .then(console.log); // logs 48 βœ…

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions