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

WIP: Add cSpell Support #1056

Closed
wants to merge 1 commit into from
Closed

WIP: Add cSpell Support #1056

wants to merge 1 commit into from

Conversation

tinywombat765
Copy link

@mjlbach I've create the draft PR as requested. At the moment I have the cmd just pointing at where I have the LSP on my system. I figure I'd work on the installer or whatever after I got it working. I also have the file types just set to text for testing, ultimately I'd add more. Or have it run on every file type if that's possible. I'm using this version of the lsp server. Currently I can't get the nvim to recognize the lsp. Meaning that it doesn't up in LspInfo when I open a text file.

Signed-off-by: Ben Aaron Goldberg <ben@benaaron.dev>
@neovim-discourse
Copy link

This pull request has been mentioned on Neovim Discourse. There might be relevant details there:

https://neovim.discourse.group/t/help-adding-support-for-cspell/817/3

@mjlbach
Copy link
Contributor

mjlbach commented Jul 11, 2021

Is cspell actually a language server? It just looks like a spell-checking tool with a CoC extension

@tinywombat765
Copy link
Author

I thought it was because it worked through CoC and acted like other lang servers from the user's prospective but I don't actually know how it works.

@mjlbach
Copy link
Contributor

mjlbach commented Jul 11, 2021

CoC is more than a language server protocol extension , there's a lot of plugins that basically wrap non-LSP compliant servers/tools in the same interface (see typescript). You would have to do the same thing for cSpell (probably easier to PR LSP support to its core) which would be a pretty big undertaking.

@tinywombat765
Copy link
Author

Out of curiosity, ciz I'm new to LSP stuff, how to you know cspell isn't a normal LSP server?

@mjlbach
Copy link
Contributor

mjlbach commented Jul 11, 2021

There is a specification that a server must adhere to: https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#textDocument_signatureHelp

I briefly looked at the cspell codebase, and saw no mention of the protocol, none of the requisite libraries, etc.

@tinywombat765
Copy link
Author

Okay, thanks for the help. Porting this is no an undertaking I'm prepared for at this time so I'm going to close this PR for now.

@MarcelRobitaille
Copy link

I'm pretty sure it's a language server: https://medium.com/@zetavg/howto-port-the-vscode-code-spell-checker-cspell-plugin-to-sublime-6a7f71fad462

I got it working in lspconfig:

local util = require 'lspconfig.util'

local root_files = {
  '.git',
}

return {
  default_config = {
    cmd = { 'node', '/home/me/code/vscode-spell-checker/packages/_server/dist/main.cjs', '--stdio' },
    filetypes = { '*' },
    root_dir = function(fname)
      return util.root_pattern(unpack(root_files))(fname)
    end,
    single_file_support = true,
    settings = {
      cspell = {
        analysis = {
          autoSearchPaths = true,
          useLibraryCodeForTypes = true,
          diagnosticMode = 'openFilesOnly',
        },
      },
    },
  },
  commands = {
  },
  docs = {
    description = [[]],
  },
}

Still trying to get code actions working.

@quolpr
Copy link

quolpr commented Jun 26, 2024

Here is my lspconfig for cspell:

local util = require 'lspconfig.util'

-- Function to decode a URI to a file path
local function decode_uri(uri)
  return string.gsub(uri, 'file://', '')
end

-- Function to read and parse JSON from a file
local function read_json_file(path)
  local file = io.open(path, 'r')
  if not file then
    error('Failed to open file: ' .. path)
  end
  local data = file:read '*a'
  file:close()

  -- Parse JSON data into Lua table
  local decoded = vim.json.decode(data)
  return decoded
end

-- Function to write JSON data to a file
local function write_json_file(path, table)
  local encoded = vim.json.encode(table)
  local file = io.open(path, 'w')
  if not file then
    error('Failed to open file for writing: ' .. path)
  end
  file:write(encoded)
  file:close()
end

return {
  default_config = {
    cmd = { 'node', '/Users/quolpr/projects/quolpr/vscode-spell-checker/packages/_server/dist/main.mjs', '--stdio' },
    filetypes = { '*' },
    root_dir = util.root_pattern '.git',
    single_file_support = true,
    settings = {
      cSpell = {
        enabled = true,
        trustedWorkspace = true,
        import = { '/Users/quolpr/.config/nvim/cspell.json' },
        checkOnlyEnabledFileTypes = false,
        doNotUseCustomDecorationForScheme = true,
        useCustomDecorations = false,
      },
    },
    handlers = {
      ['_onDiagnostics'] = function(err, result, ctx, config)
        vim.lsp.diagnostic.on_publish_diagnostics(err, result[1][1], ctx, config)
      end,
      ['_onWorkspaceConfigForDocumentRequest'] = function()
        return {
          ['uri'] = nil,
          ['workspaceFile'] = nil,
          ['workspaceFolder'] = nil,
          ['words'] = {},
          ['ignoreWords'] = {},
        }
      end,
    },
    on_init = function()
      vim.lsp.commands['cSpell.editText'] = function(command, scope)
        local range = command.arguments[3][1].range
        local new_text = command.arguments[3][1].newText

        local start_line = range.start.line
        local start_ch = range.start.character
        local end_line = range['end'].line
        local end_ch = range['end'].character

        local lines = vim.api.nvim_buf_get_lines(scope.bufnr, start_line, end_line + 1, false)

        -- Adjust the line based on the provided start and end characters
        local start_line_content = lines[1]
        local end_line_content = lines[#lines]

        -- Slice the start and end lines based on character positions
        local before_range = start_line_content:sub(1, start_ch)
        local after_range = end_line_content:sub(end_ch + 1)

        -- Replace the range with the given new text
        lines[1] = before_range .. new_text .. after_range

        -- Remove intermediate lines if necessary
        if #lines > 1 then
          for i = 2, #lines do
            lines[i] = nil
          end
        end

        vim.api.nvim_buf_set_lines(scope.bufnr, start_line, start_line + 1, false, lines)
      end
      vim.lsp.commands['cSpell.addWordsToConfigFileFromServer'] = function(command)
        local words = command.arguments[1]
        local json_file_uri = command.arguments[3].uri
        local json_file_path = decode_uri(json_file_uri)

        -- Read the existing JSON data
        local json_data = read_json_file(json_file_path)

        vim.list_extend(json_data.words, words)

        -- Write the updated JSON back to the file
        write_json_file(json_file_path, json_data)
      end

      vim.lsp.commands['cSpell.addWordsToDictionaryFileFromServer'] = function()
        vim.notify 'Not supported'
      end

      vim.lsp.commands['cSpell.addWordsToVSCodeSettingsFromServer'] = function()
        vim.notify 'Not supported'
      end
    end,
  },
  docs = {
    description = [[]],
  },
}

So, I cloned vscode-spell-checker and build it locally. I also modified codeActions.mts with this patch:

diff --git a/packages/_server/src/codeActions.mts b/packages/_server/src/codeActions.mts
index 5023ca80..f8f3a603 100644
--- a/packages/_server/src/codeActions.mts
+++ b/packages/_server/src/codeActions.mts
@@ -93,7 +93,16 @@ class CodeActionHandler {
             textDocument: { uri },
         } = params;
         const { diagnostics } = context;
-        const spellCheckerDiags = diagnostics.filter((diag) => diag.source === Validator.diagSource);
+
+        const firstIntersection = diagnostics.find(
+            (diag) => diag.source === Validator.diagSource && range.intersect(diag.range, params.range),
+        );
+        if (firstIntersection) {
+            params.range = firstIntersection.range;
+        }
+        const spellCheckerDiags = diagnostics.filter(
+            (diag) => diag.source === Validator.diagSource && range.intersect(diag.range, params.range),
+        );
         const eslintSpellCheckerDiags = diagnostics.filter((diag) => diag.source === 'eslint' && diag.code == '@cspell/spellchecker');

         if (!spellCheckerDiags.length && !eslintSpellCheckerDiags.length) return [];

@quolpr
Copy link

quolpr commented Jun 28, 2024

Finally! I was able to make lsp config without modifying cspell. Here is gist with instruction https://gist.github.com/quolpr/2d9560c0ad5e77796a068061c8ea439c

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.

None yet

5 participants