quick-lint / quick-lint-js

quick-lint-js finds bugs in JavaScript programs
https://quick-lint-js.com
GNU General Public License v3.0
1.52k stars 191 forks source link

emacs plugin development track #275

Open wgrr opened 3 years ago

wgrr commented 3 years ago

This is a sentinel issue to track what arrived from the discussion in pull request #264

Flycheck

MELPA

according to the project's contributing, we are missing:

Flymake

LSP

lsp-mode integrates with flycheck, it should be already working, but it needs test and automated testing

NPM

Either detect quick-lint-js version currently installed with npm, or provide conveniences for users doing that, for example, ivy exposes some utility functions for filling its selection buffer from shell commands (https://oremacs.com/swiper/#global-key-bindings), we can do something akin allowing the user to choose which quick-lint-js finder function to run. see https://github.com/quick-lint/quick-lint-js/pull/409#issuecomment-893078974

Lint Server

currently linters just re-executes quick-lint-js to get new errors, we need a more efficient way of offering errors, our target Emacs version for that is 24.5 (ubuntu 18.04).

Emacs 25 has added dynamic modules, im not sure how well supported is Windows, I assume it has problems because some of their api have posix specifics exposed, such as struct timespec.

So we need to decice between a C ABI, or go with a custom protocol on top of either tcp/udp/unix, rtags achives this with unix socket communication, which also needs workarounds on Windows.

strager commented 3 years ago

Add :errors-explainer blocker: #???

206

We can now link to e.g. https://quick-lint-js.com/errors/#E001. Is an HTTP link the preferred error-explainer? Or should we bundle something with qljs or with the Emacs plugin?

strager commented 3 years ago

Emacs 25 has added dynamic modules

This looks cool! I'm concerned about crashes, hangs, and leaks in qljs causing Emacs to crash, hang, or leak, though.

A separate server is a safer (though less efficient and probably harder to implement) option.

strager commented 3 years ago

according to the project's contributing, we are missing:

  • [ ] Dedicated SCM repository

Are you referring to the following text?

Dedicated SCM repository

Keep each package in its own SCM (e.g., git) repository. This makes it possible to have a different version number for each package as MELPA stable looks at the SCM’s tags to assign version numbers to recipes.

My interpretation is that the package can't be in the same SCM repo as another package. I don't interpret this to mean that a package needs a dedicated SCM repo containing no other code.

wgrr commented 3 years ago

Are you referring to the following text?

Yes, i assumed that we would have multiple packages there, but actual thinking about this it's far better to have one, all included package. Removed.

Is an HTTP link the preferred error-explainer?

ESLint checker does offer it as https://eslint.org/docs/rules/N the error-explainer is function which takes the error and should return a message, a message is a string, url (lisp cons pair) or a lambda that takes a 'buffer' we should write error documentation to, maybe we want to define some costumization for this and default to URL, since it's the most used by error-explainers.

strager commented 3 years ago

So we need to decice between a C ABI

I made src/quick-lint-js/c-api.h which exposes functions for the VS Code plugin and for the website demo. We could do something similar for the Emacs plugin. However, I'm wary of putting quick-lint-js in the same process as Emacs.

or go with a custom protocol on top of either tcp/udp/unix

Do you plan on developing this yourself? Do you want help?

wgrr commented 3 years ago

Hi, sorry for the delay, somehow I missed the notification for your update here

I thought we already had some infrastructure in the codebase for doing sockets, I wrongly inferred that from the server part of LSP, I think it makes sense to use process pipes just like --lsp, we can go with c-api.h route if wish, crashing the editor would be a disaster and the only idea I can think of is every time right before we jump in quick-lint-js code we save our machine state and rely on signals to recover to a previous working state. Sounds hard to get right, very machine and OS specific and an overkill to me, pipes are possibly slower and requires a protocol but safer and easier to port outside linux, what we choose here? I feel pipes will good enough.

The primary motivation for that is to avoid recreating quick-lint-js process in the Flycheck and Flymake backends, I do plan to develop this, I'm working on getting Flymake and others working first, then if pipe route is taken I need help to decide on the protocol, for compatibility reasons I'm leaning towards generating lisp code to run or be decoded, I feel JSON would be nicer since we could use simdjson that we already depend on, but a builtin JSON decoder in Emacs is available only in latest version (27.1), we would need to maintain our own lisp decoder for a while until Emacs 27.1 is mainstream, or bring in an external decoder.

strager commented 3 years ago

I think it makes sense to use process pipes just like --lsp, we can go with c-api.h route if wish, crashing the editor would be a disaster [...], pipes are possibly slower [than in-process] and requires a protocol but safer and easier to port outside linux, what we choose here? I feel pipes will good enough.

Does emacs support shared memory? Could we share the byte buffer used to store the source code? Or share a copy of it? If so, we could do shared memory + pipes/semaphores/something, which would give us good performance (I hope) and crash tolerance.

How fast would copying the whole buffer in emacs on each buffer change be? quick-lint-js' LSP plugin already copies the whole buffer every change (https://github.com/quick-lint/quick-lint-js/blob/47447bbf3fa7b577b2c2298e8da9b898465530e9/src/document.cpp#L39-L57), so this work is necessary no matter what approach we take. (quick-lint-js' copying is a few memcpys, so it's relatively fast, but not free.) quick-lint-js's parser needs a contiguous buffer.

Maybe I should prototype a shared memory solution myself to test this idea. This mechanism might be useful for plugins for other editors too.

I'm working on getting Flymake and others working first

:+1:

then if pipe route is taken I need help to decide on the protocol, for compatibility reasons I'm leaning towards generating lisp code to run or be decoded, I feel JSON would be nicer since we could use simdjson that we already depend on, but a builtin JSON decoder in Emacs is available only in latest version (27.1), we would need to maintain our own lisp decoder for a while until Emacs 27.1 is mainstream, or bring in an external decoder.

For quick-lint-js -> emacs messages, we can easily have quick-lint-js emit elisp. We don't use simdjson for writing JSON; simdjson only parses JSON. Writing JSON is done with string concatenation: https://github.com/quick-lint/quick-lint-js/blob/47447bbf3fa7b577b2c2298e8da9b898465530e9/src/lsp-error-reporter.cpp#L53-L105 I envision us doing the same for elisp.

For emacs -> quick-lint-js messages, we could do anything. I'd be fine with a custom text or binary format, or a JSON format, or an elisp format. I'm biased toward writing as little elisp as possible because I assume elisp is slow compared to C++.