GithubHelp home page GithubHelp logo

rhysd / neovim-component Goto Github PK

View Code? Open in Web Editor NEW
193.0 10.0 18.0 376 KB

<neovim-editor> WebComponent to embed Neovim to your app with great ease

Home Page: https://github.com/rhysd/NyaoVim

License: MIT License

Ruby 0.47% HTML 11.59% JavaScript 36.36% TypeScript 51.24% Shell 0.35%
neovim electron webcomponents

neovim-component's Introduction

<neovim-editor> Web Component

Build Status

This component provides <neovim-editor>, an HTML custom element built on Polymer v2 and flux. It provides a frontend for the Neovim editor using Neovim's MessagePack API. It allows you to easily embed a Neovim-backed editor into your application.

This component assumes to be used in Node.js environment. (i.e. Electron)

You can use this component for modern desktop application frameworks such as Electron or NW.js. You can even use it in Electron-based editors such as Atom or VS Code.

This component is designed around the Flux architecture. You can access the UI event notifications and can call Neovim APIs directly via <neovim-editor>'s APIs.

You can install this component as an npm package.

$ npm install neovim-component

Current supported nvim version is v0.1.6 or later.

Examples

Each example only takes 100~300 lines.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <script src="/path/to/webcomponents-lite.js"></script>
    <link rel="import" href="/path/to/polymer.html" />
    <link rel="import" href="/path/to/neovim-editor.html" />
  </head>
  <body>
    <neovim-editor></neovim-editor>
  </body>
</html>

Minimal Electron app can be found in the example directory. This is a good start point to use this package and it shows how the component works.

main screenshot

How to run minimal example is:

$ git clone https://github.com/rhysd/neovim-component.git
$ cd neovim-component
$ npm start

For a more complicated and realistic example, see the markdown editor example. The markdown previewer is integrated with the Neovim GUI using the <neovim-editor> component.

markdown example screenshot

This is an image popup widget example here. The gi mapping is defined to show an image under the cursor in a tooltip.

image popup example screenshot

This example shows how to include a mini web-browser using the <webview> tag from Electron.

mini browser example screenshot

Why Did You Create This?

Vim has very powerful editing features, but Vim is an editor (see :help design-not) and unfortunately lacks support for many graphical tools that writers and programmers like. NyaoVim adds support for graphical features without losing Vim's powerful text editing abilities. Neovim's msgpack APIs provide a perfect way to add a GUI layer using HTML and CSS. NyaoVim is a GUI frontend as a proof of concept.

Architecture

data flow

<neovim-editor> has an editor property to access the internal APIs of the component.

  • editor.screen is a view of the component (using canvas). It receives user input and dispatches input actions to the data store.
  • editor.process is a process handler to interact with the backing Neovim process via msgpack-rpc APIs. You can call Neovim's APIs via the Neovim client (editor.getClient() helper).
  • editor.store is the state of this component. You can access the current state of the editor through this object.

<neovim-editor> Properties

You can customize <neovim-editor> with the following properties:

Name Description Default
width Width of the editor in pixels. null
height Height of the editor in pixels. null
font Name of the editor's monospace font. "monospace"
font-size Font-size in pixels. 12
line-height Line height rate relative to font size. 1.3
nvim-cmd Command used to start Neovim. "nvim"
argv Arguments passed with the Neovim command. []
on-quit Callback function to run when Neovim quits. null
on-error Callback function for Neovim errors. null
disable-alt-key Do not send alt key input to Neovim. false
disable-meta-key Do not send meta key input to Neovim. false
cursor-draw-delay Delay in millisec before drawing cursor. 10
no-blink-cursor Blink cursor or not. false
window-title Specify first window title. "Neovim"

<neovim-editor> APIs

Receive internal various events

You can receive various events (including UI redraw notifications) from the store. The store is a part of flux architecture. It's a global instance of EventEmitter.

You can also access the state of editor via the store. Note that all values are read only. Do not change the values of the store directly, it will break the internal state of the component.

const neovim_element = document.getElementById('neovim');
const Store = neovim_element.editor.store;

// Handle cursor movements
Store.on('cursor', () => console.log('Cursor is moved to ', Store.cursor));

// Handle mode changes
Store.on('mode', () => console.log('Mode is changed to ', Store.mode));

// Handle text redraws
Store.on('put', () => console.log('UI was redrawn'));

// Accessing the state of the editor.
const bounds = [ Store.size.lines, Store.size.cols ];
const cursor_pos = [ Store.cursor.line, Store.cursor.col ];

Call Neovim APIs

You can call Neovim APIs via the client. When you call APIs via the client, it sends the call to the underlying Neovim process via MessagePack RPC and will return a Promise which resolves to the returned value.

<neovim-component> uses promised-neovim-client package. You can see the all API definitions here. If you know further about Neovim APIs, python client implementation may be helpful.

const neovim_element = document.getElementById('neovim');
const client = neovim_element.editor.getClient();

// Send a command
client.command('vsplit');

// Send input
client.input('<C-w><C-l>');

// Evaluate a Vim script expression
client.eval('"aaa" . "bbb"').then(result => console.log(result));

// Get the 'b:foo' variable
client.getCurrentBuffer()
    .then(buf => buf.getVar('foo'))
    .then(v => console.log(v));

// Query something (windows, buffers, etc.)
// Move to the neighbor window and show its information.
client.getWindows()
    .then(windows => client.secCurrentWindow(windows[1]))
    .then(() => client.getCurrentWindow())
    .then(win => console.log(win));

// Receive an RPC request from Neovim
client.on('request', (n, args, res) => console.log(`Name: ${n}, Args: ${JSON.stringify(args)}, Response: ${res}`));

Editor lifecycle

You can receive notifications related to lifecycle of the editor.

const neovim_element = document.getElementById('neovim');

// Called when the Neovim background process attaches
neovim_element.editor.on('process-attached', () => console.log('Neovim process is ready'));

// Called when the Neovim process is disconnected (usually by :quit)
neovim_element.editor.on('quit', () => console.log('Neovim process died'));

// Called when the <neovim-component> detaches
neovim_element.editor.on('detach', () => console.log('Element does not exist in DOM.'));

// Called upon experiencing an error in the internal process 
neovim_element.editor.on('error', err => alert(err.message));

View APIs

  • Resize screen
const editor = document.getElementById('neovim').editor;
editor.screen.resize(80, 100); // Resize screen to 80 lines and 100 columns
editor.screen.resizeWithPixels(1920, 1080); // Resize screen to 1920px x 1080px
  • Change font size
const editor = document.getElementById('neovim').editor;
editor.screen.changeFontSize(18); // Change font size to 18px
  • Convert pixels to lines/cols.
const editor = document.getElementById('neovim').editor;

const loc = editor.screen.convertPositionToLocation(80, 24);
console.log(loc.x, loc.y);  // Coordinates in pixels of (line, col) = (80, 24)

const pos = editor.screen.convertLocationToPosition(400, 300);
const.log(pos.col, pos.line);  // line/col of location (400px, 300px)
  • Notify of screen-size changes:

When some process has changed the screen-size you must notify the screen. The internal <canvas> element has a fixed size and must update itself if there are size changes. Call screen.checkShouldResize() if the screen size may have changed. Note that you don't need to care about resize event of <body> element. <neovim-editor> component automatically detects this particular resize event and updates automatically. screen.checkShouldResize() will simply be ignored if nothing has actually changed.

const editor = document.getElementById('neovim').editor;

function showUpSomeElementInNeovim() {
    const e = document.getElementById('some-elem');

    // New element shows up!  The screen may be resized by the change.
    // 'none' -> 'block'
    e.style.display = 'block';

    // This call tells to editor to adjust itself in the case that it has been resized
    editor.screen.checkShouldResize();
}

Other APIs

  • Setting arguments afterwards:

If your app doesn't use Polymer you can set arguments afterwards using JavaScript Note that it is better to use argv property of <neovim-element> if possible.

const editor = document.getElementById('neovim').editor;
editor.setArgv(['README.md']);
  • Focusing the editor

<neovim-editor> is just a web-component, so it can be focused just like other elements.
If it loses focus the editor won't receive any input events. The editor instance has a method to re-focus the editor in JavaScript. The store instance contains the current focus state.

const editor = document.getElementById('neovim').editor;
console.log(editor.store.focused);
editor.store.on('focus-changed', () => {
    console.log('Focus was changed: ' + editor.store.focused);
});

// Refocus the editor to ensure it receives user input.
editor.focus();

Log Levels

<neovim-component> prints logs in the browser console. The log level is controlled by the NODE_ENV environment variable:

  • NODE_ENV=debug will log everything.
  • NODE_ENV=production ignores all logs except for warnings and errors.
  • Setting NODE_ENV to empty string or some other value enables logging for info, warnings, and errors.

TODOs

neovim-component's People

Contributors

bfredl avatar chitoku-k avatar chrispenner avatar lambdalisue avatar rhysd avatar romgrk avatar yuntan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

neovim-component's Issues

Move to new client library

I'm using my fork promised-neovim-client but official neovim-client is rewritten and improved. I should move to it.

cursor won't stop blinking

I'm not sure if its a bug or I'm doing something wrong, but I have tried

<dom-module id="nyaovim-app">
  <template>
    <style>
      /* CSS configurations here */
    </style>

    <!-- Component tags here -->
    <neovim-editor id="nyaovim-editor" 
        argv$="[[argv]]" 
        font-size="14" 
        font="Fura Mono for Powerline Plus Nerd File Types,monospace"></neovim-editor>
  </template>
</dom-module>
<script>
    const neovim_element = document.getElementById('neovim');
    neovim_element.editor.blink_cursor = false;
</script>

and

<dom-module id="nyaovim-app">
  <template>
    <style>
      /* CSS configurations here */
    </style>

    <!-- Component tags here -->
    <neovim-editor id="nyaovim-editor" 
        argv$="[[argv]]" 
        font-size="14" 
        blink-cursor="false"
        font="Fura Mono for Powerline Plus Nerd File Types,monospace"></neovim-editor>
  </template>
</dom-module>

but the cursor doesn't stop blinking in normal mode.

Passthrough Control Mappings. `<C-w>`, `<C-d>`, `<C-u>`, etc.

These are the 3 most common default ctrl mappings I use. It would be great if the worked.
Here's a screen shot of the send natively using insert mode <C-v>.

screen shot 2017-12-27 at 5 15 29 am

Some system info:
Mac OS 10.11.6

NVIM v0.2.2
Build type: Release
LuaJIT 2.0.5
Compilation: /usr/local/Homebrew/Library/Homebrew/shims/super/clang -Wconversion -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -DNVIM_MSGPACK_HAS_FLOAT32 -DNVIM_UNIBI_HAS_VAR_FROM -DNDEBUG -DMIN_LOG_LEVEL=3 -Wall -Wextra -pedantic -Wno-unused-parameter -Wstrict-prototypes -std=gnu99 -Wimplicit-fallthrough -Wvla -fstack-protector-strong -fdiagnostics-color=auto -DINCLUDE_GENERATED_DECLARATIONS -I/tmp/neovim-20171118-36724-uevp4/neovim-0.2.2/build/config -I/tmp/neovim-20171118-36724-uevp4/neovim-0.2.2/src -I/usr/local/include -I/usr/local/include -I/usr/local/include -I/usr/local/include -I/usr/local/include -I/usr/local/include -I/usr/local/opt/gettext/include -I/usr/include -I/tmp/neovim-20171118-36724-uevp4/neovim-0.2.2/build/src/nvim/auto -I/tmp/neovim-20171118-36724-uevp4/neovim-0.2.2/build/include
Compiled by [email protected]

Features: +acl +iconv +jemalloc +tui
See ":help feature-compile"

   system vimrc file: "$VIM/sysinit.vim"
  fall-back for $VIM: "/usr/local/Cellar/neovim/0.2.2/share/nvim"

Various rendering artifacts

Hello, I've been testing NayoVim again after the HiDPI fix and I've been noticing a various number of artifacts in the rendering.

  1. The line heigh of text doesn't feel right, and sometimes it changes between characters
    screen shot 2016-01-08 at 12 45 24
  2. Characters are drawn even outside they cell
    screen shot 2016-01-08 at 12 46 22
  3. While scrolling parts little pieces of the text are left on the canvas in places where they where before
    screen shot 2016-01-08 at 12 46 32
  4. Spacing between character seems wrong
    screen shot 2016-01-08 at 12 46 15
  5. When resizing the window the rendering of the canvas just goes wild, part of the canvas turns white, other parts don't render etc...
    screen shot 2016-01-08 at 12 48 27

Separate Neovim process handler to remove Node.js dependency

Currently <neovim-editor> component requires child_process module to invoke Neovim process and communicate with msgpack-rpc API using stdio. However, the way to communicate with Neovim is not only stdio.
It is also available to use network for communication with Neovim process. Web applications can communicate with remote Neovim process via WebSocket. If we connect to Neovim process via WebSocket, Neovim Chrome extension is feasible. Chrome extension can't invoke process because of sandbox but can communicate with external process via websocket. Web services can also do.

So I want to separate the module depending on child_process. Current process handler will be changed to use client interface (Nvim in 'promised-neovim-client'?) and users will be able to specify their original client (which may use WebSocket, HTTP, and so on).

This change doesn't affect public API because users get client instance via editor.getClient() API.

Rendering is broken when <input> is on <canvas> with Electron 1.6

When I tried Electron 1.6, I saw rendering on <canvas> was broken.

8b2dc1e4-338e-11e7-8244-9cfb12fe0d87

But this does not occur in other environment (5K iMac) (#43). I need to investigate this issue.

  • macOS 10.12.4
  • Electron v1.6.6
  • iMac 2013 Late with 4K monitor

I think I should also try with MacBook Pro 2016 late.

Proposal: implement rendering through HTML elements

Hi there :)

I was wondering if you would consider switching the rendering implementation to HTML elements rather than a canvas?
I think this would be better because if there is one thing that browsers are optimized for, it's text rendering. Using a canvas does give good results but is still not as performant as what the browser can do (IMO), e.g. font aliasing (turning on optmizeLegibility in CSS)
Besides that, it also gives a whole lot more possibilities for future widgets, where one could use the full HTML specs (mouse hover events, etc.) to build on top of this.

If it can convince you, I did a prototype (it was for testing the external popup for neovim) which gives pretty good results: https://www.youtube.com/watch?v=TI5azVeDUDo

installation issue under Windows

portable node.js LTS 10.16.3
nyaovim 0.2.0 installed

run "npm install neovim-component" (0.10.1) and it reports following.
npm WARN deprecated [email protected]: core-js@<2.6.8 is no longer maintained. Please, upgrade to core-j
s@3 or at least to actual version of core-js@2.
npm WARN [email protected] requires a peer of react@^15.0.2 || ^16.0.0-beta || ^16.0.0 but none is installe
d. You must install peer dependencies yourself.

After successfully installed, when I run "nyaovim", its window content is blank and not interactive.

Remove default size (height/width) and make screen fit to <canvas> size

Currently inner canvas element has fixed size and user must manage width and height of <neovim-editor> component. This is because we must know lines and cols before starting nvim (otherwise it is needed to send vim_try_resize after nvim starting).

  1. <neovim-editor> (includes inner <canvas>) is created
  2. nvim process starts
  3. <neovim-editor> is attached to DOM

Now the size must be specified because we can't know the element size before attaching it to DOM.
The sequence might be:

  1. <neovim-editor> (includes inner <canvas>) is created
  2. <neovim-editor> is attached to DOM
  3. Get the size of inner <canvas> and calculate lines and cols
  4. nvim process starts with the lines and cols

Starting process will be delayed until DOM is attached. I must check the start up time change to ensure this change has small impact to it.

Canvas rendered at low-res on HiDPI displays

Hello, on HiDPI displays, like Apple retina displays the canvas is rendered at low resolution. When creating the canvas you should take into account the the devicePixelRatio to determine height and width and then properly scale when drawing onto it.

Will it ever be possible to set line-height?

Hi, first of all - thanks for your work, it's really great to see what you're building! Keep up the great work :)

My question is about line-height. I was comparing the output from a neovim-component with atom editor, and the obvious difference is that default line height in atom is 1.5. This gives a better readability to code IMHO (and it's a setting I always tweak in terminals like iTerm).

I've tried hacking something inside neovim-component but the HTML canvas specifications explicitly ignore any line-height setting (see http://www.w3.org/TR/2dcontext/, Chapter 4).

What do you think about this? Do you think there's any workaround for the drawText canvas function to allow using a different lineHeight or does this require a completely different approach (like adding text directly to the DOM, à-la Atom)? If so, is this something you considered?

Thanks again!

Andrea Schiavini

KeyDown events trigger the wrong vim key

The failing code is https://github.com/rhysd/neovim-component/blob/master/src/neovim/input.ts#L119

vim_input += (special_char || String.fromCharCode(event.keyCode).toLowerCase()) + '>';

Example:
Pressing alt + ; generates <A-º> where neovim usually gets <A-;>
screenshot from 2016-01-11 15-26-47

The input is off for all non-letter keys. I recall discussing something like this in #6, and again I wouldnt know exactly how this could be solved. The problem here is that String.fromCharCode takes a character code as argument, and what is being fed to it is a keyCode. I had already run in this exact problem when implementing a package for Atom, the only workaround I found then was to have a map of keycodes: characters, but there was no check of keyboard layout whatsoever, US layout was assumed all the way. That causes evident problems for i18n.
I'll check if there is any possibility to retrieve the user keyboard layout.

Consider WebGL rendering priority.

I already implemented all UI events from neovim. Last task is rendering using pixi.js (WebGL).
At first, I should check canvas rendering is a bottleneck of this app.

More practical example

Currently this repo contains very simple UI for demonstration and debugging. But it is unclear why making it as component is good from the example.

More practical example is needed. I think markdown preview editor is good.

Better swap file handling with 'SwapExists' autocmd event

Currently NyaoVim simply ignores swap files because message on detecting swap file is shown before Neovim opens screen and NyaoVim can't catch it yet.

But SwapExists auto command event may be available for this.

  1. Add below command as an argument of nvim
  2. Notify to NyaoVim that swap file is detected
  3. NyaoVim show dialog and user select a choice ('o', 'e', 'r', 'd', 'q', or 'a')
  4. Set v:swapchoice properly
augroup nyaovim-swap
    autocmd SwapExists * call SendSwapNotification(v:swapname) | autocmd! nyaovim-swap
augroup END

Nvim process crash maybe?(or electron?)

Hello,
I'll update the title with something more meaningful as soon as I find out what's going wrong, but I was wondering if I could have your input on a particular issue.
When I try to open certain files in the component, I get a "Inspected target disconnected" popup in DevTools (see image below), and the whole electron window becomes unresponsive.
Do you have any idea where this could come from? I'm not sure where to start debuging. (I'm not sure either that this is a neovim-component issue.)

(The window right after executing :e sass/main.scss)
screenshot from 2016-02-25 03-37-49

Please help with Copyrights/Licensing issues.

Greetings and thanks for the excellent work.

I would like to use your code and modify it substantially for a side project that I have.

In the project, you have a LICENSE.txt file containing the MIT licence. That's good. The name listed in the file is rhysd which is probably not your legal name, right? Would you mind putting your legal name on the license file?

Also noticed that the individual files do not carry any copyright notice on top. This is a problem for anybody wanting to use or contribute to such files. I assume that they were mostly written by you but there are a couple more people that seem to have made very minor contributions to some of the files. Would you mind copying and pasting this fragment to the top of each file?

/*
Copyright (c) <YEAR> <YOUR-LEGAL-NAME>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

I know this is very boring and it looks counter productive, but without it, it's very hard for anybody to make derived work and properly credit you for the work you've done.

Please help.

https://softwareengineering.stackexchange.com/questions/157968/how-to-manage-a-copyright-notice-in-an-open-source-project

https://stackoverflow.com/questions/11670368/gpl-copyright-notice-when-contributing-new-files

Meta/Alt chords input shifted variant to neovim

Steps:

  • electron example/minimal or electron example/markdown
  • press alt-char where char is any letter, e.g. Alt + j
  • console.log: Input to neovim: <A-J> where we should instead have <A-j>

No bower.json present

When I execute npm start, the terminal shows
2016-07-29 1 54 11

It seems that the bower install in package.json is not necessary

Of rendering & more

Hello,

I was just curious and had some a question about this project, because I was in the process of looking to write something like this. I saw you have planned rendering this with WebGL, any precise schedule? (No pressure at all intended, just for my information; this all looks like great work).

cmd+v to paste not working

Expected

From neovim-component 0.6.2
2016-08-10 12 36 48

Actual

From neovim-component 0.7.3
2016-08-10 12 39 00

Step

  • use visual mode to copy the text (cmd+c not working as well)
  • paste the content by cmd+v

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.