kidd / emacs-ng

A new approach to Emacs - Including TypeScript, Threading, Async I/O, and WebRender.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Table of Contents

emacs-ng

Motivation

The goal of this fork is to explore new development approaches.

Why Emacsng

Emacs-ng is an additive native layer over emacs, bringing features like Deno's Javascript and Async I/O environment, Mozilla's Webrender (experimental opt-in feature), and other features in development. emacs-ng's approach is to utilize multiple new development approaches and tools to bring Emacs to the next level. emacs-ng is maintained by a team that loves Emacs and everything it stands for - being totally introspectable, with a fully customizable and free development environment. We want Emacs to be a editor 40+ years from now that has the flexibility and design to keep up with progressive technology.

New User Guides

If you would like to see a breakdown of what JavaScript can do, feel free to jump into our User Guides: Getting Started, Using Deno, and Advanced Features, or check out our FAQ for common questions about the project. Otherwise, you can continue reading for a project outline and build instructions.

Why JavaScript

One of emacs-ng's primary features is integrating the Deno Runtime, which allows execution of JavaScript and Typescript within Emacs. The details of that feature are listed below, however many users would ask themselves WHY JAVASCRIPT? JavaScript is an extremely dynamic language that allows for a user to inspect and control their scripting environment. The key to note is that bringing in Deno isn't JUST JavaScript - it's an ecosystem of powerful tools and approaches that Emacs just doesn't have currently.

  • TypeScript offers an extremely flexible typing system, that allows to user to have compile time control of their scripting, with the flexibility of types "getting out of the way" when not needed.
  • Deno uses Google's v8 JavaScript engine, which features an extremely powerful JIT and world-class garbage collector.
  • Usage of modern Async I/O utilizing Rust's Tokio library.
  • Emacs-ng has WebWorker support, meaning that multiple JavaScript engines can be running in parallel within the editor. The only restriction is that only the 'main' JS Engine can directly call lisp functions.
  • Emacs-ng also has WebAssembly support - compile your C module as WebAsm and distribute it to the world. Don't worry about packaging shared libraries or changing module interfaces, everything can be handled and customized by you the user, at the scripting layer. No need to be dependent on native implementation details.

Performance

v8's world-class JIT offers the potential for massive performance gains. For a simple benchmark (fibonacci), using the following implementations:

(defun fibonacci(n)
  (if (<= n 1)
      n
    (+ (fibonacci (- n 1)) (fibonacci (- n 2)))))
const fib = (n) => {
    if (n <= 1) {
        return n;
    }

    return fib(n - 1) + fib(n - 2);
};

emacs-ng's JS implementation clocks in over 50 times faster than emacs 28 without native-comp for calculating fib(40). With native-comp at level 3, JS clocks in over 15 times faster. This, along with Async I/O from Deno, WebWorkers, and WebAsm, gives you the tools to make Emacs a smoother and faster experience without having to install additional tools to launch as background processes or worry about shared library versions - full performance with EVERYTHING in the scripting layer.

Getting Started

Requirements

  1. You will need Rust installed. The file rust-toolchain indicates the version that gets installed. This happens automatically, so don't override the toolchain manually. IMPORTANT: Whenever the toolchain updates, you have to reinstall rustfmt manually.

  2. You will need a C compiler and toolchain. On Linux, you can do something like:

     apt install build-essential automake clang libclang-dev
    

    On macOS, you'll need Xcode.

  3. Linux:

     apt install texinfo libjpeg-dev libtiff-dev \
       libgif-dev libxpm-dev libgtk-3-dev gnutls-dev \
       libncurses5-dev libxml2-dev libxt-dev
    

    macOS:

     brew install gnutls texinfo autoconf
    

    To use the installed version of makeinfo instead of the built-in (/usr/bin/makeinfo) one, you'll need to make sure /usr/local/opt/texinfo/bin is before /usr/bin in PATH. Mojave install libxml2 headers with: open /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg

If you want to run doomemacs, you will need to compile with ./configure --with-nativecomp. nativecomp will also require zlib1g-dev libgccjit-8-dev

Building emacsng

$ ./autogen.sh
$ ./configure --enable-rust-debug
$ make -j 8 # or your number of cores

For a release build, don't pass --enable-rust-debug.

The Makefile obeys cargo's RUSTFLAGS variable and additional options can be passed to cargo with CARGO_FLAGS.

For example:

$ make CARGO_FLAGS="-vv" RUSTFLAGS="-Zunstable-options --cfg MARKER_DEBUG"

Running emacsng

You can now run your build:

./src/emacs

If you want to install it, just use

make install

You may need to run sudo make install depending on your system configuration.

Contributing

Contributions are welcome. We try to maintain a list of "new contributor" friendly issues tagged with "good first issue".

Features

Javascript

This code is a strictly additive layer, it changes no elisp functionality, and should be able to merge upstream patches cleanly. JS tests can be run by building the editor and executing cd test && ../src/emacs --batch -l js/bootstrap.el.

To learn more about JavaScript, it is recommended you check out Getting Started, Using Deno, and Advanced Features

Typescript

emacs-ng supports native typescript. If your typescript fails to compile, those functions will throw elisp errors that you can address within your program.

The high level concept here is to allow full control of emacs-ng via the javascript layer. In order to avoid conflict between the elisp and javascript layers, only one scripting engine will be running at a time. elisp is the "authoritative" layer, in that the javascript will invoke elisp functions to interact with the editor. This is done via a special object in javascript called lisp. An example of it's usage would be:

Interacting with buffers and symbols

let buffer = lisp.get_buffer_create("mybuf");
let qcname = lisp.keywords.name; // Will return an object representing :name
let myList = lisp.list(qcname, 3);
console.log(myList); // prints { nativeProxy : true }
console.log(myList.json()); // prints { name: 3 }, defaulting to the assumption this list is a plist

Almost any function callable from lisp can be invoked in JavaScript via the lisp object. So (function-name) becomes lisp.function_name. If you find a lisp function that cannot be called from JS, that is a bug.

Using Async I/O

We expose the async IO functionality included with deno. Users can fetch data async from their local file system, or the network. They can use that data to interact with the editor. An example would be:

const json = fetch("https://api.github.com/users/denoland")
.then((response) => { return response.json(); });

const txt = Deno.readTextFile("./test.json");

Promise.all([json, text])
    .then((data) => {
        let buffer = lisp.get_buffer_create('hello');
        const current = lisp.current_buffer();
        lisp.set_buffer(buffer);
        lisp.insert(JSON.stringify(data[0]));
        lisp.insert(data[1]);
        console.log(lisp.buffer_string());
        lisp.set_buffer(current);
    });

This example assumes you have a json file named test.json in your current directory.

WebWorkers and Parallel Scripting

We also support WebWorkers, meaning that you can run javascript in separate threads. Note that WebWorkers cannot interact with the lisp VM, however they can use Deno for async I/O. See Advanced Features

Web Assembly allows you to perform things normally handled by native libraries with easy distribution. Want to manipulate sqlite3? Use the deno sqlite wasm package

import { DB } from "https://deno.land/x/sqlite@v2.3.2/mod.ts";

const db = new DB("test.db");
db.query("CREATE TABLE IF NOT EXISTS people (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)");

const name = "David";
db.query("INSERT INTO people (name) VALUES (?)", [name]);
for (const [name] of db.query("SELECT name FROM people")) {
    console.log(name);
}

db.close();

Adding Hooks

Lambdas are converted automatically between the two languages via proxying. You can use this to set timers, or to add hooks. This example features the lisp.symbols object, which returns a proxy to the quoted key. lisp.setq functions similarly to (setq ....)

lisp.add_hook(lisp.symbols.text_mode_hook, () => {
    lisp.setq(lisp.symbols.foo, "3");
});

// Or set timer with arguments...
lisp.run_with_timer(lisp.symbols.t, 1, (a, b) => {
    console.log('hello ' + a);
    lisp.print(b);
    return 3;
}, 3, lisp.make.alist({x: 3}));

The user can also define functions via defun that will call back into javascript. Like in normal defun, DOCSTRING and the INTERACTIVE decl are optional. This example features both docstring and interactive

lisp.defun({
    name: "my-funcntion",
    docString: "This is my docstring",
    interactive: true,
    args: "P\nbbuffer:",
    func: (a, b) => console.log('hello buffer!')
});

Webrender

WebRender is a GPU-based 2D rendering engine written in Rust from Mozilla. Firefox, the research web browser Servo, and other GUI frameworks draw with it. emacs-ng use it as a new experimental graphic backend to leverage GPU hardware.

Webrender rendering is a opt-in feature. You can enable it by this.

$ ./configure --with-webrender

If you get "Couldn't find any available vsync extension" runtime panic, enabling 3D acceleration will fixes it.

About

A new approach to Emacs - Including TypeScript, Threading, Async I/O, and WebRender.

License:GNU General Public License v3.0


Languages

Language:Emacs Lisp 70.7%Language:C 18.8%Language:Lex 5.2%Language:M4 0.8%Language:Objective-C 0.8%Language:TeX 0.7%Language:Makefile 0.5%Language:Rust 0.4%Language:C++ 0.2%Language:Shell 0.2%Language:PostScript 0.2%Language:Mathematica 0.2%Language:Perl 0.1%Language:HTML 0.1%Language:Ruby 0.1%Language:JavaScript 0.1%Language:Pascal 0.1%Language:Prolog 0.1%Language:MATLAB 0.1%Language:NewLisp 0.1%Language:SystemVerilog 0.1%Language:GDB 0.1%Language:Slash 0.1%Language:SRecode Template 0.1%Language:Smalltalk 0.1%Language:Python 0.1%Language:Yacc 0.0%Language:Ada 0.0%Language:PHP 0.0%Language:Awk 0.0%Language:Batchfile 0.0%Language:Fortran 0.0%Language:Erlang 0.0%Language:SmPL 0.0%Language:Lua 0.0%Language:Dockerfile 0.0%Language:Java 0.0%Language:NSIS 0.0%Language:SCSS 0.0%Language:CSS 0.0%Language:Forth 0.0%Language:Modula-2 0.0%Language:Tcl 0.0%Language:Less 0.0%Language:Go 0.0%Language:Nix 0.0%Language:Scheme 0.0%Language:Raku 0.0%Language:TypeScript 0.0%