Skip to content
Open
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,16 @@ public void initialize(ClientCapabilities clientCap, @Nullable List<WorkspaceFol
}
var fileOperationCapabilities = capabilities.getWorkspace().getFileOperations();
var whichFiles = new FileOperationOptions(fileFilters);
if (clientCapabilities.getFileOperations().getDidCreate().booleanValue()) {
var operations = clientCapabilities.getFileOperations();
if (operations != null && operations.getDidCreate().booleanValue()) {
fileOperationCapabilities.setDidCreate(whichFiles);
}
if (clientCapabilities.getFileOperations().getDidRename().booleanValue()) {
operations = clientCapabilities.getFileOperations();
if (operations != null && operations.getDidRename().booleanValue()) {
fileOperationCapabilities.setDidRename(whichFiles);
}
if (clientCapabilities.getFileOperations().getDidDelete().booleanValue()) {
operations = clientCapabilities.getFileOperations();
if (operations != null && operations.getDidDelete().booleanValue()) {
fileOperationCapabilities.setDidDelete(whichFiles);
}
}
Expand Down
1 change: 1 addition & 0 deletions rascal-neovim-plugin/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.tests
96 changes: 96 additions & 0 deletions rascal-neovim-plugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Rascal Neovim plugin

The Rascal Neovim plugin adds the Rascal LSP to Neovim.

## Features

- LSP
- `:RascalTerminal`, which opens a terminal with the Rascal REPL

## Requirements

For building the LSP:

- Maven
- NodeJS + NPM

During runtime:

- JDK 11

## Installation

Using lazy.nvim:

```lua
{
"usethesource/rascal-language-servers",
dependencies = { "akinsho/toggleterm.nvim" }, -- Optional
config = function(plugin)
vim.opt.rtp:append(plugin.dir .. "/rascal-neovim-plugin")
require("lazy.core.loader").packadd(plugin.dir .. "/rascal-neovim-plugin")
require("rascal").setup({...})
end,
build = "./build.sh -f",
}
```

This config will compile the LSP after downloading.

## Usage

This plugin can be configured by passing a table of options to the `setup` function.
The default configuration of this plugin is

```lua
{
jar = {
rascal = jar.get_rascal_jar(),
rascal_lsp = jar.get_rascal_lsp_jar(),
},
terminal = {
command = function ()
return "java -jar " .. utils.shell_stringify(M.config.jar.rascal)
end,
backend = terminal.neovim,
},
}
```

- `jar`: location of the JAR files of Rascal to use for the terminal.
Note: changing this option does not affect which JAR files are used by the LSP.
- `terminal`
- `command`: the command that is used to open a Rascal terminal.
- `backend`: the terminal backend that is used to render the Rascal REPL.
By default, the standard Neovim terminal is used.
This option can also be set to `require("rascal.terminal").toggleterm`
or to a custom backend.

### LSP

To enable the LSP, use

```lua
vim.lsp.enable("rascal_lsp")
```

By default, the JAR files in `rascal-lsp/target` will be used.
A different location can be specified using

```lua
vim.lsp.config("rascal_lsp", {
cmd = {...}
})
```

See [lsp/rascal_lsp.lua](lsp/rascal_lsp.lua) for details.

## Testing

This plugin can be tested by running

```sh
./tests/run
```

Before testing, make sure that `build.sh` was run in the parent directory.
1 change: 1 addition & 0 deletions rascal-neovim-plugin/ftplugin/rascal.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
vim.bo.commentstring = "// %s"
20 changes: 20 additions & 0 deletions rascal-neovim-plugin/lsp/rascal_lsp.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
local jar = require("rascal.jar")
local classpath = jar.get_classpath() or ""

return {
name = "rascal_lsp",
-- https://github.com/usethesource/rascal-language-servers/blob/4ef17204f9bc15dabc05f9a88b9fa837eb92a633/rascal-vscode-extension/src/lsp/RascalLSPConnection.ts#L160
cmd = {
"java",
"-Dlog4j2.configurationFactory=org.rascalmpl.vscode.lsp.log.LogJsonConfiguration",
"-Dlog4j2.level=DEBUG",
"-Drascal.fallbackResolver=org.rascalmpl.vscode.lsp.uri.FallbackResolver",
"-Drascal.lsp.deploy=true",
"-Drascal.compilerClasspath=" .. classpath,
"-cp",
classpath,
"org.rascalmpl.vscode.lsp.rascal.RascalLanguageServer",
},
filetypes = { "rascal" },
root_markers = { "META-INF", "pom.xml" },
}
36 changes: 36 additions & 0 deletions rascal-neovim-plugin/lua/rascal/config.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
local jar = require("rascal.jar")
local utils = require("rascal.utils")
local terminal = require("rascal.terminal")

local M = {}

---@class RascalConfig
---@field jar RascalConfigJar
---@field terminal RascalConfigTerminal

---@class RascalConfigJar
---@field rascal string
---@field rascal_lsp string

---@class RascalConfigTerminal
---@field command string|fun():string
---@field backend RascalTerminalBackend

---@type RascalConfig
M.default = {
jar = {
rascal = jar.get_rascal_jar(),
rascal_lsp = jar.get_rascal_lsp_jar(),
},
terminal = {
command = function ()
return "java -jar " .. utils.shell_stringify(M.config.jar.rascal)
end,
backend = terminal.neovim,
},
}

---@type RascalConfig
M.config = vim.tbl_deep_extend("force", M.default, {})

return M
23 changes: 23 additions & 0 deletions rascal-neovim-plugin/lua/rascal/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
local config = require("rascal.config")
local utils = require("rascal.utils")

local M = {}

---Open a Rascal terminal.
function M.terminal()
local cmd = config.config.terminal.command
if type(cmd) == "function" then
cmd = cmd()
end

local root = utils.get_project_root()
config.config.terminal.backend(cmd, root)
end

---Set configuration for this plugin.
---@param opts RascalConfig
function M.setup(opts)
config.config = vim.tbl_deep_extend("force", config.default, opts)
end

return M
53 changes: 53 additions & 0 deletions rascal-neovim-plugin/lua/rascal/jar.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
local M = {}

---Get the location of the `target` directory.
---@return string
local function get_target_dir()
local source_file = debug.getinfo(1).source:sub(2)
local plugin_root = source_file
for _ = 1, 4 do
plugin_root = vim.fs.dirname(plugin_root)
end
return plugin_root .. "/rascal-lsp/target"
end

---Get the default location of `rascal.jar.
---@return string
function M.get_rascal_jar()
return get_target_dir() .. "/lib/rascal.jar"
end

---Get the default location of `rascal-lsp*.jar`.
---@return string|nil
function M.get_rascal_lsp_jar()
local target_dir = get_target_dir()

local dir = vim.uv.fs_opendir(target_dir)
if dir == nil then
return nil
end

local dir_data = vim.uv.fs_readdir(dir)
while dir_data do
if string.match(dir_data[1].name, "rascal%-lsp.*%.jar") then
return target_dir .. "/" .. dir_data[1].name
end
dir_data = vim.uv.fs_readdir(dir)
end

return nil
end

---Get the classpath for the default JAR location.
---The classpath contains the filenames of the Rascal LSP and Rascal after building.
---@return string|nil
function M.get_classpath()
local rascal_jar = M.get_rascal_jar()
local rascal_lsp_jar = M.get_rascal_lsp_jar()
if rascal_lsp_jar == nil then
return nil
end
return rascal_lsp_jar .. ":" .. rascal_jar
end

return M
21 changes: 21 additions & 0 deletions rascal-neovim-plugin/lua/rascal/terminal.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
local utils = require("rascal.utils")

---@alias RascalTerminalBackend fun(command: string, project_root: string|nil):nil

---@type {[string]: RascalTerminalBackend}
local M = {}

function M.neovim(command, project_root)
if project_root ~= nil then
command = "cd " .. utils.shell_stringify(project_root) .. ";" .. command
end
vim.cmd.terminal(command)
end

function M.toggleterm(command, project_root)
local Terminal = require("toggleterm.terminal").Terminal
local term = Terminal:new({ cmd = command, dir = project_root })
term:open()
end

return M
20 changes: 20 additions & 0 deletions rascal-neovim-plugin/lua/rascal/utils.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
local M = {}

---Look in the parent directories for the project root.
---@return string
function M.get_project_root()
for dir in vim.fs.parents(vim.api.nvim_buf_get_name(0)) do
if vim.uv.fs_stat(dir .. "/META-INF") or vim.uv.fs_stat(dir .. "/pom.xml") then
return dir
end
end
end

---Escape a string for in a shell command.
---@param str string
---@return string
function M.shell_stringify(str)
return "'" .. str:gsub("'", "'\"'\"'") .. "'"
end

return M
3 changes: 3 additions & 0 deletions rascal-neovim-plugin/plugin/rascal_commands.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
vim.api.nvim_create_user_command("RascalTerminal", function ()
require("rascal").terminal()
end, {})
5 changes: 5 additions & 0 deletions rascal-neovim-plugin/plugin/rascal_filetype.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
vim.filetype.add({
pattern = {
[".*/.*%.rsc"] = "rascal",
},
})
64 changes: 64 additions & 0 deletions rascal-neovim-plugin/tests/helpers.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
local M = {}

M.expect = vim.deepcopy(MiniTest.expect)

M.expect.file_exists = MiniTest.new_expectation(
"file exists",
function(filename) return vim.uv.fs_stat(filename) end,
function(filename) return ("Filename: %s"):format(filename) end
)

M.expect.match = MiniTest.new_expectation(
"match",
function(str, pattern) return string.match(str, pattern) end,
function(str, pattern) return ("String: %s\nPattern: %s"):format(str, pattern) end
)

M.expect.suffix = MiniTest.new_expectation(
"has suffix",
function(str, suffix) return string.sub(str, #str - #suffix + 1) end,
function(str, suffix) return ("String: %s\nSuffix: %s"):format(str, suffix) end
)

M.expect.screenshot_match = MiniTest.new_expectation(
"screenshot match",
function(screenshot, pattern)
local concatenated = vim.iter(screenshot.text):flatten():join("")
return concatenated:match(pattern)
end,
function(screenshot, pattern)
return ("Screenshot:\n%s\nPattern: %s"):format(screenshot, pattern)
end
)

---Retry an expectation until the timeout.
---@param timeout integer How many milliseconds to wait before giving up
---@param fn fun()
---@param interval integer? How many milliseconds to wait between tries (200 by default)
---@see vim.wait
function M.expect.wait(timeout, fn, interval)
if interval == nil then
interval = 200
end
local timed_out = false
local timer = vim.uv.new_timer()
timer:start(timeout, 0, function ()
timer:stop()
timer:close()
timed_out = true
end)

local success, output
while not timed_out do
success, output = pcall(fn)
if success then
timer:stop()
timer:close()
return
end
vim.uv.sleep(interval)
end
error(output)
end

return M
10 changes: 10 additions & 0 deletions rascal-neovim-plugin/tests/minit.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
vim.env.LAZY_STDPATH = ".tests"
load(vim.fn.system("curl -s https://raw.githubusercontent.com/folke/lazy.nvim/main/bootstrap.lua"))()

require("lazy.minit").setup({
spec = {
{ dir = vim.uv.cwd() },
{ "nvim-mini/mini.test", version = "*" },
{ "akinsho/toggleterm.nvim", version = "*", config = true },
},
})
6 changes: 6 additions & 0 deletions rascal-neovim-plugin/tests/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/sh

nvim --headless --noplugin -u tests/minit.lua \
-c 'lua require("mini.test").setup()' \
-c 'lua MiniTest.run()' \
-c 'qa!'
Loading
Loading