Skip to content
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

feat(api): Implement Extmark API #199

Closed
wants to merge 5 commits into from
Closed

feat(api): Implement Extmark API #199

wants to merge 5 commits into from

Conversation

rhysd
Copy link
Member

@rhysd rhysd commented Oct 12, 2022

Fix #170

This PR adds Extmark API binding to Buffer. I confirmed it worked on my local:

const cp = require('child_process');
const { attach } = require('./packages/neovim');

const proc = cp.spawn('nvim', ['-u', 'NONE', '-N', '--embed'], {});

(async function () {
  const nvim = await attach({ proc });

  const buf = await nvim.buffer;
  await buf.replace(['this is test'], 0);

  const ns = await nvim.createNamespace('test');
  console.log('namespace:', ns);

  const id = await buf.setExtmark(ns, 0, 4);
  console.log('Mark ID:', id);

  const mark = await buf.getExtmarkById(ns, id);
  console.log('Mark:', mark);

  const marks = await buf.getExtmarks(ns, [0, 0], [0, 8]);
  console.log('Marks:', marks);

  const deleted = await buf.deleteExtmark(ns, id);
  console.log('Deleted:', deleted);

  const mark2 = await buf.getExtmarkById(ns, id);
  console.log('Mark after delete:', mark2);

  const marks2 = await buf.getExtmarks(ns, [0, 0], [0, 8]);
  console.log('Marks after delete:', marks2);

  nvim.quit();
  proc.kill();
})().catch(console.error);

Output:

namespace: 1
Mark ID: 1
Mark: [ 0, 4 ]
Marks: [ [ 1, 0, 4 ] ]
Deleted: true
Mark after delete: []
Marks after delete: []

@justinmk
Copy link
Member

Is this generated? We should avoid manually writing interfaces that just map to nvim --api-info.

@rhysd
Copy link
Member Author

rhysd commented Oct 13, 2022

Is this generated?

No, it was hand-written. I agree that generating APIs from nvim_api_info. This time, I prioritized Extmark API since it was requested by the issue.

I think it is a bit challenging to generate APIs. For example,

  • Some parameter type is just an Array. Its element type is unknown
  • Object property is snake_case in Vim script but it is lowerCamel in TypeScript. I'm not sure it is ok to convert cases of all properties automatically

@justinmk
Copy link
Member

  • Object property is snake_case in Vim script but it is lowerCamel in TypeScript. I'm not sure it is ok to convert cases of all properties automatically

Why don't we stick to the API names exactly? TypeScript "conventions" are not that important IMO.

@rhysd
Copy link
Member Author

rhysd commented Oct 13, 2022

Why don't we stick to the API names exactly?

Since using snake_case looks weird. Actually no browser APIs and no Node.js APIs are using snake_case. Yes, it is just a convention, but I don't think it is not important.

@rhysd
Copy link
Member Author

rhysd commented Oct 13, 2022

OK, I'll make a new issue for generating sources. Can we merge this PR at first? Then let me try to make some mechanism of code generation. (Though I'm not sure when I can take time to do it)

@justinmk
Copy link
Member

Some parameter type is just an Array. Its element type is unknown

This doesn't sound like a blocker. Just type the items as any.

I think we should strictly stick with auto-generation if we're going to provide anything beyond request('nvim_foo', ...).

@justinmk
Copy link
Member

justinmk commented Oct 13, 2022

Can we merge this PR at first?

Will we be able to remove this later once auto-generation is in place? That might be a breaking change.

@rhysd
Copy link
Member Author

rhysd commented Oct 13, 2022

Will we be able to remove this later once auto-generation is in place? That might be a breaking change.

I think yes. Because the auto generated sources will break many other existing APIs. So taking care about only the Extmark API doesn't make much sense.

@justinmk
Copy link
Member

Because the auto generated sources will break many other existing APIs.

why? we already were generating stuff in the past: https://github.com/neovim/node-client/blob/master/packages/neovim/bin/generate-typescript-interfaces.js

@rhysd
Copy link
Member Author

rhysd commented Oct 13, 2022

why? we already were generating stuff in the past:

If my understanding is correct, this script is no longer used. The script was used before large changes by @billyvg.

Actually, as far as I ran the script on my local machine now, it only outputs 24 lines:

$ node ./bin/generate-typescript-interfaces.js                                                                                                                                                                                                                                               [Husky.local][10/13 21:56]
interface AttachOptions {
  writer?: NodeJS.WritableStream,
  reader?: NodeJS.ReadableStream,
  proc?: NodeJS.ChildProcess,
  socket?: String,
}
export default function attach(options: AttachOptions): Neovim;

export interface Neovim extends NodeJS.EventEmitter {
  quit(): void;
  isApiReady(): Boolean;
  requestApi(): Promise<[integer, any]>;
  equals(rhs: Neovim): boolean;
}
export interface Buffer {
  equals(rhs: Buffer): boolean;
}
export interface Window {
  equals(rhs: Window): boolean;
}
export interface Tabpage {
  equals(rhs: Tabpage): boolean;
}

@rhysd
Copy link
Member Author

rhysd commented Oct 13, 2022

For example, this type has information more than the output from nvim_api_info. So it must be hand-written.

export interface OpenWindowOptions {
// If set, the window becomes a floating window. The window will be placed with row,col coordinates relative to valueue
// "editor" the global editor grid
// "win" a window. Use 'win' option below to specify window id,
// or current window will be used by default.
// "cursor" the cursor position in current window.
relative?: 'editor' | 'win' | 'cursor';
// the corner of the float that the row,col position defines
anchor?: 'NW' | 'NE' | 'SW' | 'SE';
// Whether window can be focused by wincmds and mouse events. Defaults to true. Even if set to false, the window can still be entered using |nvim_set_current_win()| API call.
focusable?: boolean;
// `row`: row position. Screen cell height are used as unit. Can be floating point.
row?: number;
// `col`: column position. Screen cell width is used as unit. Can be floating point.
col?: number;
// when using relative='win', window id of the window where the position is defined.
win?: number;
// GUI should display the window as an external top-level window. Currently accepts no other positioning options together with this.
external?: boolean;
// width of the window
width: number;
// height of the window
height: number;
// Places float relative to buffer text (only when relative="win"). Takes a tuple of zero-indexed [line, column].
bufpos?: [number, number];
// Stacking order. floats with higher `zindex` go on top on floats with lower indices. Must be larger than zero
zindex?: number;
// Configure the appearance of the window.
style?: 'minimal';
// Style of (optional) window border. This can either be a string or an array.
border?:
| 'none'
| 'single'
| 'double'
| 'rounded'
| 'solid'
| 'shadow'
| string[];
// If true then no buffer-related autocommand events such as |BufEnter|, |BufLeave| or |BufWinEnter| may fire from calling this function.
noautocmd?: boolean;
}

@billyvg
Copy link
Member

billyvg commented Oct 18, 2022

Yeah I don't think we can 100% rely on autogeneration, but it'd be good to do so in order to keep up with neovim.

@rhysd did your code comments on the PR come from neovim or were they written by you?

re: camelcase vs snake, imo we should follow node/ts conventions which is generally camelCase.

Comment on lines +548 to +549
* @param {ExtmarkPosition} start Start of range: a 0-indexed (row, col) or valid extmark id
* (whose position defines the bound).
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
* @param {ExtmarkPosition} start Start of range: a 0-indexed (row, col) or valid extmark id
* (whose position defines the bound).
* @param {ExtmarkPosition} start Start of range: a 0-indexed (row, col) or valid extmark id (whose position defines the bound).

Comment on lines +550 to +551
* @param {ExtmarkPosition} end End of range (inclusive): a 0-indexed (row, col) or valid
* extmark id (whose position defines the bound).
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
* @param {ExtmarkPosition} end End of range (inclusive): a 0-indexed (row, col) or valid
* extmark id (whose position defines the bound).
* @param {ExtmarkPosition} end End of range (inclusive): a 0-indexed (row, col) or valid extmark id (whose position defines the bound).

Comment on lines +552 to +554
* @param {BufferGetExtmarksOptions} options Optional parameters. Keys:
* • limit: Maximum number of marks to return
* • details: Whether to include the details dict
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
* @param {BufferGetExtmarksOptions} options Optional parameters. Keys:
* limit: Maximum number of marks to return
* details: Whether to include the details dict
* @param {BufferGetExtmarksOptions} options Optional parameters. Keys:
* limit: Maximum number of marks to return
* details: Whether to include the details dict

* @param {Number} ns_id Namespace id from |nvim_create_namespace()|
* @param {Number} id Extmark id
* @param {BufferGetExtmarkOptions} options Optional parameters. Keys:
* • details: Whether to include the details dict
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
* details: Whether to include the details dict
* details: Whether to include the details dict

@justinmk
Copy link
Member

It's still not clear why this is needed. #170 isn't blocked because nodejs clients can do request('nvim_...", ...).

So why rush a hand-crafted API which we won't be able to revert later (breaking change)? Why not work on generating an API instead? What is blocking that? We generate all kinds of APIs in Nvim, is there a technical issue with doing that for nodejs client?

@justinmk
Copy link
Member

This repo has lacked maintainer activity for 6+ months, which supports the point I made above about wanting to aggressively minimize the maintenance needed for this project.

node-client is important (used by projects like vscode-neovim). I want to keep node-client stable and useful, but I also want to aggressively avoid unnecessary potential for bugs and feature requests.

@justinmk justinmk closed this Jul 10, 2023
@justinmk justinmk deleted the extmark branch July 10, 2023 11:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Extended marks support
3 participants