Skip to content

Commit

Permalink
Add Rust analyzer support (#868)
Browse files Browse the repository at this point in the history
Fixes #520 

Only supports syntax diagnostics for now. I made a follow up for types
#881
  • Loading branch information
sonnyp authored Feb 12, 2024
1 parent deb048d commit 2514485
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 39 deletions.
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
/src/langs/vala/ @lw64
*.vala @lw64

/src/langs/rust/ @lw64
/src/langs/rust/ @Hofer-Julian
*.rs @Hofer-Julian
Cargo.toml @Hofer-Julian
Cargo.lock @Hofer-Julian
Expand Down
51 changes: 50 additions & 1 deletion src/cli/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const window = new Adw.ApplicationWindow();

function createLSPClients({ root_uri }) {
return Object.fromEntries(
["javascript", "blueprint", "css", "vala"].map((id) => {
["javascript", "blueprint", "css", "vala", "rust"].map((id) => {
const lang = languages.find((language) => language.id === id);
const lspc = createLSPClient({
lang,
Expand Down Expand Up @@ -397,6 +397,55 @@ async function ci({ filenames, current_dir }) {
});
}

const file_rust = demo_dir.get_child("code.rs");
if (file_rust.query_exists(null)) {
print(` ${file_rust.get_path()}`);

const uri = file_rust.get_uri();
const languageId = "rust";
let version = 0;

const [contents] = await file_rust.load_contents_async(null);
const text = new TextDecoder().decode(contents);

await lsp_clients.rust._notify("textDocument/didOpen", {
textDocument: {
uri,
languageId,
version: version++,
text,
},
});

// FIXME: rust analyzer doesn't publish diagnostics if there are none
// probably we should switch to pulling diagnostics but unknown if supported
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_pullDiagnostics

// const diagnostics = await waitForDiagnostics({
// uri,
// lspc: lsp_clients.rust,
// });
// if (diagnostics.length > 0) {
// printerr(serializeDiagnostics({ diagnostics }));
// return false;
// }
// print(` ✅ lints`);

const checks = await checkFile({
lspc: lsp_clients.rust,
file: file_rust,
lang: getLanguage("rust"),
uri,
});
if (!checks) return false;

await lsp_clients.rust._notify("textDocument/didClose", {
textDocument: {
uri,
},
});
}

await Promise.all(
Object.entries(lsp_clients).map(([, lspc]) => {
return lspc.stop();
Expand Down
9 changes: 7 additions & 2 deletions src/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ export const languages = [
document: null,
default_file: "code.rs",
index: 2,
language_server: ["rust-analyzer"],
formatting_options: {
...formatting_options,
tabSize: 4,
},
},
{
id: "python",
Expand All @@ -113,13 +118,13 @@ export function getLanguage(id) {
);
}

export function createLSPClient({ lang, root_uri }) {
export function createLSPClient({ lang, root_uri, quiet = true }) {
const language_id = lang.id;

const lspc = new LSPClient(lang.language_server, {
rootUri: root_uri,
languageId: language_id,
// quiet: false,
quiet,
});
lspc.connect("exit", () => {
console.debug(`${language_id} language server exit`);
Expand Down
4 changes: 2 additions & 2 deletions src/langs/blueprint/BlueprintDocument.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import Document from "../../Document.js";
import { applyTextEdits } from "../../lsp/sourceview.js";

import { setup as setupBlueprint } from "./blueprint.js";
import { setup } from "./blueprint.js";

export class BlueprintDocument extends Document {
constructor(...args) {
super(...args);

this.lspc = setupBlueprint({ document: this });
this.lspc = setup({ document: this });
}
async update() {
return this.lspc.didChange();
Expand Down
4 changes: 2 additions & 2 deletions src/langs/javascript/JavaScriptDocument.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { setup as setupJavaScript } from "./javascript.js";
import { setup } from "./javascript.js";

import Document from "../../Document.js";
import { applyTextEdits } from "../../lsp/sourceview.js";
Expand All @@ -7,7 +7,7 @@ export class JavaScriptDocument extends Document {
constructor(...args) {
super(...args);

this.lspc = setupJavaScript({ document: this });
this.lspc = setup({ document: this });
}

async format() {
Expand Down
5 changes: 1 addition & 4 deletions src/langs/rust/Compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ import Gio from "gi://Gio";
import GLib from "gi://GLib";
import dbus_previewer from "../../Previewer/DBusPreviewer.js";
import { copyDirectory, decode, encode } from "../../util.js";

const rust_template_dir = Gio.File.new_for_path(
pkg.pkgdatadir,
).resolve_relative_path("langs/rust/template");
import { rust_template_dir } from "./rust.js";

export default function Compiler({ session }) {
const { file } = session;
Expand Down
48 changes: 21 additions & 27 deletions src/langs/rust/RustDocument.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,30 @@
import Gio from "gi://Gio";
import { setup } from "./rust.js";

import Document from "../../Document.js";
import { applyTextEdits } from "../../lsp/sourceview.js";

export class RustDocument extends Document {
async format() {
const code = await formatRustCode(this.buffer.text);
this.code_view.replaceText(code, true);
}
}

function formatRustCode(text) {
const rustfmtLauncher = Gio.SubprocessLauncher.new(
Gio.SubprocessFlags.STDIN_PIPE |
Gio.SubprocessFlags.STDOUT_PIPE |
Gio.SubprocessFlags.STDERR_PIPE,
);
constructor(...args) {
super(...args);

const rustfmtProcess = rustfmtLauncher.spawnv([
"rustfmt",
"--quiet",
"--emit",
"stdout",
"--edition",
"2021",
]);
this.lspc = setup({ document: this });
}

const [success, stdout, stderr] = rustfmtProcess.communicate_utf8(text, null);
async format() {
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_formatting
const text_edits = await this.lspc.request("textDocument/formatting", {
textDocument: {
uri: this.file.get_uri(),
},
options: {
tabSize: 4,
insertSpaces: true,
trimTrailingWhitespace: true,
insertFinalNewline: true,
trimFinalNewlines: true,
},
});

if (!success || stderr !== "") {
console.error(`Error running rustfmt: ${stderr}`);
return text;
applyTextEdits(text_edits, this.buffer);
}

return stdout;
}
37 changes: 37 additions & 0 deletions src/langs/rust/rust.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import Gio from "gi://Gio";

import { createLSPClient } from "../../common.js";
import { getLanguage } from "../../util.js";

export function setup({ document }) {
const { file, buffer, code_view } = document;

const lspc = createLSPClient({
lang: getLanguage("rust"),
root_uri: file.get_parent().get_uri(),
});
lspc.buffer = buffer;
lspc.uri = file.get_uri();
lspc.connect(
"notification::textDocument/publishDiagnostics",
(_self, params) => {
if (params.uri !== file.get_uri()) {
return;
}
code_view.handleDiagnostics(params.diagnostics);
},
);

lspc.start().catch(console.error);

buffer.connect("modified-changed", () => {
if (!buffer.get_modified()) return;
lspc.didChange().catch(console.error);
});

return lspc;
}

export const rust_template_dir = Gio.File.new_for_path(
pkg.pkgdatadir,
).resolve_relative_path("langs/rust/template");
2 changes: 2 additions & 0 deletions src/sessions.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
copyDirectory,
} from "./util.js";
import { languages } from "./common.js";
import { rust_template_dir } from "./langs/rust/rust.js";

export const sessions_dir = data_dir.get_child("sessions");

Expand Down Expand Up @@ -64,6 +65,7 @@ export function createSessionFromDemo(demo) {

const { file, settings } = session;
copyDirectory(demo_dir, file);
copyDirectory(rust_template_dir, file);

settings.set_string("name", name);
settings.set_boolean("show-code", panels.includes("code"));
Expand Down
1 change: 1 addition & 0 deletions src/workbench
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

export WEBKIT_DISABLE_DMABUF_RENDERER=1
# export G_MESSAGES_DEBUG=@app_id@
export GSK_RENDERER=gl

# Required to allow pkgconfig to find pc files in /app/lib/pkgconfig
export PKG_CONFIG_PATH=/app/lib/pkgconfig/:$PKG_CONFIG_PATH
Expand Down

0 comments on commit 2514485

Please sign in to comment.