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

Unexpected newline at the end of file when formatting it #241

Open
aiotter opened this issue Apr 29, 2023 · 8 comments
Open

Unexpected newline at the end of file when formatting it #241

aiotter opened this issue Apr 29, 2023 · 8 comments

Comments

@aiotter
Copy link

aiotter commented Apr 29, 2023

When formatting a file, if you don't have a blank line at the end, efm-langserver appends it.

For example, if you have this file (main.ts) in your neovim buffer:

import App from './App.svelte';

const app = new App({
	target: document.body,
	props: {
		name: 'world'
	}
});

export default app;

And executes the formatting command, which is { formatCommand = "/path/to/prettier --stdin-filepath ${INPUT}", formatStdin = true } }, the file will be the following:

import App from "./App.svelte";

const app = new App({
    target: document.body,
    props: {
        name: "world",
    },
});

export default app;
// <- new blank line appended here

The log shows that efm-langserver is going to write out a newline (see \n\n at the end of the line):

[ERROR][2023-04-29 18:18:58] .../vim/lsp/rpc.lua:734	"rpc"	"/nix/store/wfnmjgcrh1x0l4kg16r8n50p5bzyzqwb-efm-langserver/bin/efm-langserver"	"stderr"	'2023/04/29 18:18:58 /nix/store/1am6m1c8y7cff1zyinzyx8bb0zj9xxh3-prettier-2.8.7/bin/prettier --stdin-filepath /Users/aiotter/repo/github.com/realglobe-Inc/tilt-sensing-client/src/main.ts: import App from "./App.svelte";\n\nconst app = new App({\n    target: document.body,\n    props: {\n        name: "world",\n    },\n});\n\nexport default app;\n\n'

However, the manual invocation of the same command does not produce a newline.

$ cat main.ts | sh -c '/path/to/prettier --stdin-filepath /path/to/main.ts' | cat -e
import App from "./App.svelte";$
$
const app = new App({$
    target: document.body,$
    props: {$
        name: "world",$
    },$
});$
$
export default app;$

This issue occurs on macOS, NVIM v0.9.0.

@aiotter
Copy link
Author

aiotter commented Apr 29, 2023

#181 may or may not be related to this.

@aiotter
Copy link
Author

aiotter commented Apr 29, 2023

Oh wait, it seems the problem only occurs when the input file does not end with a newline character.
(So the file is not a text file in the sense of POSIX)

$ cat -e main.ts
import App from './App.svelte';$
$
const app = new App({$
        target: document.body,$
        props: {$
                name: 'world'$
        }$
});$
$
export default app;

When the input file has a newline character at the EOF when opening the file on neovim, it works without problem.
When the file lacks the newline character at the EOF when opening the file on neovim, the issue occurs.
Note that when I open the file and save the file immediately, the issue occurs despite the fact that the input file is now with a newline character at the EOF.

@mattn
Copy link
Owner

mattn commented Apr 29, 2023

do you set noeol ?

@aiotter
Copy link
Author

aiotter commented Apr 29, 2023

It seems it is on by default. I set nothing related to EOL.

@mattn
Copy link
Owner

mattn commented Apr 29, 2023

Hmm, formatting does not modify outputs.

@SimonEggert
Copy link

I have the same issue. Did anyone find a suitable solution yet?

@SimonEggert
Copy link

I dug a little deeper today and hope I can provide additional information:

The problem only occurs for me when Prettier needs to change the last line, e.g. with semicolons required in JS:

export const test = () => {}

results in

export const test = () => {};
<-- newline here

This is the corresponding log:

2024/01/23 08:55:40 jsonrpc2: --> notif: textDocument/didChange: {"textDocument":{"version":55,"uri":"file:test.js"},"contentChanges":[{"text":"export const test = () =\u003e {}\n"}]}
2024/01/23 08:55:40 jsonrpc2: <-- notif: textDocument/publishDiagnostics: {"uri":"file:test.js","diagnostics":[],"version":55}

2024/01/23 08:55:45 jsonrpc2: --> request #14: textDocument/formatting: {"textDocument":{"uri":"file:test.js"},"options":{"insertSpaces":true,"tabSize":2}}
2024/01/23 08:55:46 eslint_d --fix-to-stdout --stdin --stdin-filename test.js:
2024/01/23 08:55:46 jsonrpc2: <-- result #14: textDocument/formatting: [{"range":{"start":{"line":0,"character":0},"end":{"line":1,"character":0}},"newText":""},{"range":{"start":{"line":1,"character":0},"end":{"line":1,"character":0}},"newText":"export const test = () =\u003e {};\n"}]
2024/01/23 08:55:46 jsonrpc2: --> notif: textDocument/didChange: {"textDocument":{"version":56,"uri":"file:test.js"},"contentChanges":[{"text":"export const test = () =\u003e {}\nexport const test = () =\u003e {};\n\n"}]}
2024/01/23 08:55:46 jsonrpc2: --> notif: textDocument/didChange: {"textDocument":{"version":57,"uri":"file:test.js"},"contentChanges":[{"text":"export const test = () =\u003e {};\n\n"}]}
2024/01/23 08:55:46 jsonrpc2: --> notif: textDocument/didSave: {"textDocument":{"uri":"file:test.js"}}
2024/01/23 08:55:46 jsonrpc2: <-- notif: textDocument/publishDiagnostics: {"uri":"file:test.js","diagnostics":[],"version":57}
2024/01/23 08:55:46 jsonrpc2: <-- notif: textDocument/publishDiagnostics: {"uri":"file:test.js","diagnostics":[],"version":57}

On the other hand

const x = 0
export const test = () => {};

correctly results in

const x = 0;
export const test = () => {};

log:

2024/01/23 08:58:42 jsonrpc2: --> notif: textDocument/didChange: {"textDocument":{"version":74,"uri":"file:test.js"},"contentChanges":[{"text":"const x = 0\nexport const test = () =\u003e {};\n"}]}
2024/01/23 08:58:42 jsonrpc2: <-- notif: textDocument/publishDiagnostics: {"uri":"file:test.js","diagnostics":[],"version":74}

2024/01/23 08:58:45 jsonrpc2: --> request #16: textDocument/formatting: {"textDocument":{"uri":"file:test.js"},"options":{"insertSpaces":true,"tabSize":2}}
2024/01/23 08:58:45 eslint_d --fix-to-stdout --stdin --stdin-filename test.js:
2024/01/23 08:58:45 jsonrpc2: <-- result #16: textDocument/formatting: [{"range":{"start":{"line":0,"character":0},"end":{"line":1,"character":0}},"newText":""},{"range":{"start":{"line":1,"character":0},"end":{"line":1,"character":0}},"newText":"const x = 0;\n"}]
2024/01/23 08:58:45 jsonrpc2: --> notif: textDocument/didChange: {"textDocument":{"version":75,"uri":"file:test.js"},"contentChanges":[{"text":"const x = 0\nconst x = 0;\nexport const test = () =\u003e {};\n"}]}
2024/01/23 08:58:45 jsonrpc2: --> notif: textDocument/didChange: {"textDocument":{"version":76,"uri":"file:test.js"},"contentChanges":[{"text":"const x = 0;\nexport const test = () =\u003e {};\n"}]}
2024/01/23 08:58:45 jsonrpc2: --> notif: textDocument/didSave: {"textDocument":{"uri":"file:test.js"}}
2024/01/23 08:58:45 jsonrpc2: <-- notif: textDocument/publishDiagnostics: {"uri":"file:test.js","diagnostics":[],"version":76}
2024/01/23 08:58:46 jsonrpc2: <-- notif: textDocument/publishDiagnostics: {"uri":"file:test.js","diagnostics":[],"version":76}

Please let me know if I can help any further in investigating.


This is the relevant part of my nvim config btw in case someone wants to try to reproduce this:

-- init.lua
local formatting_lsps = {
  'efm',
}

local filter = function(client)
  for _, lsp in pairs(formatting_lsps) do
    if client.name == lsp then
      return true
    end
  end
  return false
end

local common_on_attach = function(client, bufnr)
  if client.supports_method("textDocument/formatting") then
    vim.api.nvim_clear_autocmds({ group = augroup, buffer = bufnr })
    vim.api.nvim_create_autocmd("BufWritePre", {
      group = augroup,
      buffer = bufnr,
      callback = function()
        vim.lsp.buf.format({
          bufnr = bufnr,
          filter = filter,
        })
      end,
    })
  end
end

-- efm
local eslint = {
  lintCommand = "eslint_d --format unix --stdin --stdin-filename ${INPUT}",
  lintIgnoreExitCode = true,
  lintStdin = true,
  lintFormats = {"%f:%l:%c: %m"},
  formatCommand = "eslint_d --fix-to-stdout --stdin --stdin-filename ${INPUT}",
  formatStdin = true,
  rootMarkers = {
    '.eslintrc',
    '.eslintrc.cjs',
    '.eslintrc.js',
    '.eslintrc.json',
    '.eslintrc.yaml',
    '.eslintrc.yml',
    'package.json',
  },
}

local prettier = {
  formatCanRange = true,
  formatCommand = "prettierd ${INPUT} ${--range-start=charStart} ${--range-end=charEnd}",
  formatStdin = true,
  rootMarkers = {
    '.prettierrc',
    '.prettierrc.json',
    '.prettierrc.js',
    '.prettierrc.yml',
    '.prettierrc.yaml',
    '.prettierrc.json5',
    '.prettierrc.mjs',
    '.prettierrc.cjs',
    '.prettierrc.toml',
  },
}

nvim_lsp.efm.setup{
  cmd = { "efm-langserver", "-logfile", "efm.log", "-loglevel", "5" },
  on_attach = common_on_attach,
  init_options = {documentFormatting = true},
  settings = {
    rootMarkers = {".git/"},
    languages = {
      javascript = {
        prettier,
        eslint,
      },
  },
  filetypes = { 
    'javascript',
  }
}

I was running efm version 0.0.44 and have now updated to 0.0.49. I'll let you know if this changes anything.

@SimonEggert
Copy link

FWIW: This issue is also happening to me when using efm with helix.

This is my config:

# languages.toml

[language-server.efm]
command = "efm-langserver"

[language-server.efm.config]
documentFormatting = true

[language-server.efm.config.languages]
css = [
  { formatCommand ="prettierd --stdin-filepath ${INPUT}", formatStdin = true },
  { lintCommand = "stylelint_d --stdin --stdin-filename ${INPUT} --formatter compact", lintIgnoreExitCode = true, lintStdin = true, lintFormats = [ "%f: line %l, col %c, %tarning - %m", "%f: line %l, col %c, %trror - %m" ], formatCommand = "stylelint_d --fix --stdin-filename ${INPUT}", formatStdin = true, rootMarkers = [ ".stylelintrc", ".stylelintrc.json", "stylelint.config.js", "stylelint.config.cjs", "stylelint.config.mjs", "stylelint.config.ts" ] },
]

[[language]]
name = "css"
scope = "source.css"
language-servers = [ "efm" ]
auto-format = true

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

No branches or pull requests

3 participants