Skip to content

Touched up a few slightly awkward sentences in the README. #24

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 13, 2016
Merged
Changes from all commits
Commits
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
149 changes: 77 additions & 72 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@
[![Build Status](https://travis-ci.org/rhysd/neovim-component.svg?branch=master)](https://travis-ci.org/rhysd/neovim-component)


This component provides `<neovim-editor>` HTML custom element built on [Polymer](https://github.com/Polymer/polymer) and [flux](https://github.com/facebook/flux).
It provides a frontend of [Neovim editor](https://github.com/neovim/neovim) using Neovim's MessagePack API and you can embed Neovim editor easily to your application.
This component provides `<neovim-editor>`, an HTML custom element built on [Polymer](https://github.com/Polymer/polymer) and [flux](https://github.com/facebook/flux).
It provides a frontend for the [Neovim editor](https://github.com/neovim/neovim) using Neovim's MessagePack API. It allows you to easily embed a Neovim-backed editor
into your application.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it doesn't need to add newline before 'into'.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's markdown, so it displays the same either way, but I can't still remove it if you like. Convention here is usually to format the whole document to 80 characters wide, but it doesn't matter particularly to me.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I'll fix this in the next time I edit this file.


**This component assumes the environment where Node.js is integrated.**
So you can use this component for modern desktop application frameworks such as [Electron](https://github.com/atom/electron) or [NW.js](https://github.com/nwjs/nw.js). (Or even useful for plugin of Electron-based editors such as [Atom](http://atom.io/) or [VS Code](https://github.com/Microsoft/vscode))
**This component assumes you have Node.js installed.**

This component is designed with [Flux architecture](https://facebook.github.io/flux/docs/overview.html).
You can use this component for modern desktop application frameworks such as [Electron](https://github.com/atom/electron) or [NW.js](https://github.com/nwjs/nw.js).
You can even use it in Electron-based editors such as [Atom](http://atom.io/) or [VS Code](https://github.com/Microsoft/vscode).

This component is designed around the [Flux architecture](https://facebook.github.io/flux/docs/overview.html).
You can access the UI event notifications and can call Neovim APIs directly via `<neovim-editor>`'s APIs.

You can install this component as [npm package](https://www.npmjs.com/package/neovim-component).
You can install this component as an [npm package](https://www.npmjs.com/package/neovim-component).

```
$ npm install neovim-component
Expand All @@ -21,7 +24,7 @@ $ npm install neovim-component

## Examples

Each example is implemented within 100~300 lines.
Each example only takes 100~300 lines.

### [Minimal Example](/example/minimal)

Expand All @@ -40,12 +43,12 @@ Each example is implemented within 100~300 lines.
</html>
```

Minimal [Electron](https://github.com/atom/electron) app in [example directory](/example/minimal).
Minimal [Electron](https://github.com/atom/electron) app can be found in the [example directory](/example/minimal).

![main screenshot](https://raw.githubusercontent.com/rhysd/ss/master/neovim-component/main.gif)


`neovim-component` package also has an example executable.
The `neovim-component` package also has an example executable.

```sh
$ npm install -g neovim-component electron-prebuilt
Expand All @@ -54,93 +57,93 @@ $ simple-neovim-gui

### [Markdown Editor Example](/example/markdown)

If you want to see the more complicated and practical composable example, see [markdown editor example](/example/markdown). Markdown previewer is integrated to Neovim GUI using `<neovim-editor>` component.
For a more complicated and realistic example, see the [markdown editor example](/example/markdown). The markdown previewer is integrated with the Neovim GUI using the `<neovim-editor>` component.

![markdown example screenshot](https://raw.githubusercontent.com/rhysd/ss/master/neovim-component/markdown-example.gif)

### [Image Popup Example](/example/image-popup)

This is an image popup widget example [here](/example/image-popup). `gi` mapping is defined to show an image under the cursor in popup tooltip.
This is an image popup widget example [here](/example/image-popup). The `gi` mapping is defined to show an image under the cursor in a tooltip.

![image popup example screenshot](https://raw.githubusercontent.com/rhysd/ss/master/neovim-component/popup-image-example.gif)

### [Mini Browser Example](/example/mini-browser)

This is an embedded mini browser example implemented with [`<webview>` tag of Electron](https://github.com/atom/electron/blob/master/docs/api/web-view-tag.md).
This example shows how to include a mini web-browser using the [`<webview>` tag from Electron](https://github.com/atom/electron/blob/master/docs/api/web-view-tag.md).

![mini browser example screenshot](https://raw.githubusercontent.com/rhysd/ss/master/neovim-component/mini-browser.gif)


## Why You Created This?
## Why Did You Create This?

Vim has very powerful features for editing. But Vim is an editor (see `:help design-not`). But there are many graphical features for writers and coders. I want to support them without loosing Vim's powerful text editing. So I thought it is good to support them in GUI layer by HTML and CSS and created this using [Neovim's msgpack APIs](https://neovim.io/doc/user/msgpack_rpc.html). I'm creating [a new GUI frontend, NyaoVim](https://github.com/rhysd/NyaoVim) for proof of concept.
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](https://neovim.io/doc/user/msgpack_rpc.html) provide a perfect way to add a GUI layer using HTML and CSS. [NyaoVim](https://github.com/rhysd/NyaoVim) is a GUI frontend as a proof of concept.


## Architecture

![data flow](https://raw.githubusercontent.com/rhysd/ss/master/neovim-component/flow.png)

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

- `editor.screen` is a view of this component (using canvas). It receives user input and dispatches input action to store.
- `editor.process` is a process handler to interact with Neovim process via msgpack-rpc APIs. You can call Neovim's APIs via Neovim client (`editor.getClient()` helper).
- `editor.store` is a state of this component. You can access the current state via this object.
- `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 its properties.
You can customize `<neovim-editor>` with the following properties:

| Name | Description | Default |
| ------------------- | ---------------------------------------- | ------------- |
| `width` | Width of editor in pixel. | `null` |
| `height` | Height of editor in pixel. | `null` |
| `font` | Face name of font. | `"monospace"` |
| `font-size` | Font size in pixel. | `12` |
| `line-height` | Line height rate against font size. | `1.3` |
| `nvim-cmd` | Command to start Neovim. | `"nvim"` |
| `argv` | Arguments passed to Neovim command. | `[]` |
| `on-quit` | Callback function on Neovim quitting. | `null` |
| `on-error` | Callback function on error in Neovim. | `null` |
| `disable-alt-key` | Input `{char}` on `<A-{char}>`. | `false` |
| `cursor-draw-delay` | Delay in millisec before drawing cursor. | `10` |
| `blink-cursor` | Blink cursor or not. | `true` |
| 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 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` | Input `{char}` on `<A-{char}>`. | `false` |
| `cursor-draw-delay` | Delay in millisec before drawing cursor. | `10` |
| `blink-cursor` | Blink cursor or not. | `true` |


## `<neovim-editor>` APIs

### Receive internal various events

You can receive various events (including UI redraw notifications) from **store**.
`store` is a part of flux architecture and global instance of [EventEmitter](https://nodejs.org/api/events.html).
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](https://nodejs.org/api/events.html).

You can also access to the state of editor via `store`. Note that all values are read only.
Do not change the value of store directly because it breaks the internal state of component.
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.

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

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

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

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

// You can also access to the state of editor.
// 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](https://neovim.io/doc/user/msgpack_rpc.html#msgpack-rpc-api) via **client**.
When you call some APIs via the client, it sends the call to underlying Neovim process via MessagePack RPC and will receive the returned value.
You can get the returned value from API as a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) value.
You can call [Neovim APIs](https://neovim.io/doc/user/msgpack_rpc.html#msgpack-rpc-api) 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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) which resolves to the returned value.


`<neovim-component>` uses [promised-neovim-client](https://github.com/rhysd/promised-neovim-client) package.
You can see the all API definitions [here](https://github.com/rhysd/promised-neovim-client/blob/promisified/index.d.ts). If you know further about Neovim APIs, [python client implementation](https://github.com/neovim/python-client) may be helpful.
Expand All @@ -149,49 +152,49 @@ You can see the all API definitions [here](https://github.com/rhysd/promised-neo
const neovim_element = document.getElementById('neovim');
const client = neovim_element.editor.getClient();

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

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

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

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

// Query something (windows, buffers, and so on)
// Move to neighbor window and show the information of window.
// 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 RPC request from Neovim
// 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 editor.
You can receive notifications related to lifecycle of the editor.

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

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

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

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

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

Expand All @@ -203,7 +206,7 @@ neovim_element.editor.on('error', err => alert(err.message));
```javascript
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 1920x1080px
editor.screen.resizeWithPixels(1920, 1080); // Resize screen to 1920px x 1080px
```

- Change font size
Expand All @@ -225,41 +228,44 @@ const pos = editor.screen.convertLocationToPosition(400, 300);
const.log(pos.col, pos.line); // line/col of location (400px, 300px)
```

- Notify screen should resize
- Notify of screen-size changes:

When some process may change screen size, **you must notify it to screen**. This is because screen has `<canvas>` element and it must have fixed size. Screen must always know the size of screen by calling `screen.checkShouldResize()`.
Note that you need not care about `resize` event of `<body>` element. `<neovim-editor>` component automatically detects the resize event and update screen size. And it is OK to call `screen.checkShouldResize()` if screen is not actually resized. If screen isn't resized, `screen.checkShouldResize()` simply ignores the call.
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
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Period is missing at the end of this line.

Note that you need not 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.

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

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

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

// You need to call this to say to <neovim-editor> that 'You may be resized. Check it out!'.
// This call tells to editor to adjust itself in the case that it has been resized
editor.screen.checkShouldResize();
}
```

### Other APIs

- Set arguments after
- Setting arguments afterwards:

If your app doesn't use Polymer, you can set arguments as below after.
Note that it is better to use `argv` property of `<neovim-element>`.
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.

```javascript
const editor = document.getElementById('neovim').editor;
editor.setArgv(['README.md']);
```

- Focus on editor
- Focusing the editor

`<neovim-editor>` is just one of component in browser. So it has focus like as other elements. If focus is lost, editor can't take input event. `editor` instance has a method to focus on editor in JavaScript and `store` instance contains the focus state.
`<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.

```javascript
const editor = document.getElementById('neovim').editor;
Expand All @@ -268,21 +274,20 @@ editor.store.on('focus-changed', () => {
console.log('Focus was changed: ' + editor.store.focused);
});

// Focus on Neovim screen to ensure that user can input.
// Refocus the editor to ensure it receives user input.
editor.focus();
```

### Log Levels

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

- `NODE_ENV=debug` enables all logs.
- `NODE_ENV=production` disables all logs except for warnings and errors.
- Setting empty string or other value to `NODE_ENV` enable info log, warnings, and errors.
- `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

- [ ] WebGL rendering (using [pixi.js](http://www.pixijs.com/) or [CreateJS](http://www.createjs.com/)). [#2](https://github.com/rhysd/neovim-component/issues/2)
- [ ] Follow dynamic device pixel ratio change. [#18](https://github.com/rhysd/neovim-component/issues/18)