Skip to content

Commit

Permalink
Publish diagnostics on unsaved change, optionally
Browse files Browse the repository at this point in the history
Co-authored-by: Léo Larnack <[email protected]>

Close #11
  • Loading branch information
azdavis committed Sep 4, 2022
1 parent 3da97c1 commit 2a482f1
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 5 deletions.
10 changes: 10 additions & 0 deletions crates/analysis/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ impl Input {
pub fn iter_sources(&self) -> impl Iterator<Item = WithPath<&str>> + '_ {
self.sources.iter().map(|(&path, s)| path.wrap(s.as_str()))
}

/// Override a source file to have the given contents.
///
/// Returns whether the source was overridden. That is:
///
/// - This returns `true` if there _was_ an existing source with this `path`.
/// - This returns `false` otherwise.
pub fn override_source(&mut self, path: PathId, contents: String) -> bool {
self.sources.insert(path, contents).is_some()
}
}

/// An error when getting input.
Expand Down
2 changes: 2 additions & 0 deletions crates/config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,6 @@ pub enum ErrorLines {
pub struct Options {
/// Show information about tokens on hover.
pub show_token_hover: bool,
/// Send diagnostics when file contents change before saving.
pub diagnostics_on_change: bool,
}
53 changes: 48 additions & 5 deletions crates/lang-srv/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ impl State {
if let Err(e) = root {
ret.show_error(format!("{e:#}"));
}
let registrations = match ret.root.take() {
let mut registrations = match ret.root.take() {
Some(root) => {
let watchers = vec![lsp_types::FileSystemWatcher {
// not sure if possible to only listen to millet.toml. "nested alternate groups are not
Expand All @@ -73,7 +73,7 @@ impl State {
),
kind: None,
}];
ret.publish_diagnostics(root);
ret.publish_diagnostics(root, None);
let did_change_watched = registration::<lsp_types::notification::DidChangeWatchedFiles, _>(
lsp_types::DidChangeWatchedFilesRegistrationOptions { watchers },
);
Expand All @@ -93,6 +93,17 @@ impl State {
]
}
};
if ret.options.diagnostics_on_change {
let did_change = registration::<lsp_types::notification::DidChangeTextDocument, _>(
lsp_types::TextDocumentChangeRegistrationOptions {
document_selector: None,
// TODO lsp_types::TextDocumentSyncKind::FULL.into() doesn't work, and this field is
// i32 for some reason.
sync_kind: 1,
},
);
registrations.push(did_change);
}
ret.send_request::<lsp_types::request::RegisterCapability>(lsp_types::RegistrationParams {
registrations,
});
Expand Down Expand Up @@ -247,12 +258,37 @@ impl State {
n = try_notification::<lsp_types::notification::DidChangeWatchedFiles, _>(n, |_| {
match self.root.take() {
Some(root) => {
self.publish_diagnostics(root);
self.publish_diagnostics(root, None);
Ok(())
}
None => bail!("no root"),
}
})?;
n = try_notification::<lsp_types::notification::DidChangeTextDocument, _>(n, |params| {
let url = params.text_document.uri;
let mut changes = params.content_changes;
let change = match changes.pop() {
Some(x) => x,
None => bail!("no changes"),
};
if !changes.is_empty() {
bail!("not exactly 1 change");
}
if change.range.is_some() {
bail!("not a full document change");
}
let contents = change.text;
match self.root.take() {
Some(mut root) => {
let path = url_to_path_id(&self.file_system, &mut root, &url)?;
self.publish_diagnostics(root, Some((path, contents)));
}
None => {
self.publish_diagnostics_one(url, &contents);
}
}
Ok(())
})?;
n = try_notification::<lsp_types::notification::DidOpenTextDocument, _>(n, |params| {
if self.root.is_some() {
bail!("has root");
Expand Down Expand Up @@ -300,12 +336,16 @@ impl State {

// diagnostics //

fn publish_diagnostics(&mut self, mut root: Root) -> bool {
fn publish_diagnostics(
&mut self,
mut root: Root,
extra: Option<(paths::PathId, String)>,
) -> bool {
let mut has_diagnostics = FxHashSet::<Url>::default();
let input = elapsed::log("input::get", || {
analysis::input::get(&self.file_system, &mut root.input)
});
let input = match input {
let mut input = match input {
Ok(x) => x,
Err(e) => {
for url in root.has_diagnostics.drain() {
Expand All @@ -330,6 +370,9 @@ impl State {
return false;
}
};
if let Some((path, contents)) = extra {
input.override_source(path, contents);
}
let got_many = elapsed::log("get_many", || self.analysis.get_many(&input));
for (path_id, errors) in got_many {
let path = root.input.as_paths().get_path(path_id);
Expand Down
7 changes: 7 additions & 0 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,10 @@ Path to the `lang-srv` executable. When set to the empty string `""` (the defaul
- Default: `true`

Show information about tokens on hover.

### `millet.server.diagnostics.onChange.enable`

- Type: `boolean`
- Default: `false`

Send diagnostics when file contents change before saving.
5 changes: 5 additions & 0 deletions editors/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@
"type": "boolean",
"default": true,
"markdownDescription": "Show information about tokens on hover."
},
"millet.server.diagnostics.onChange.enable": {
"type": "boolean",
"default": false,
"markdownDescription": "Send diagnostics when file contents change before saving."
}
}
},
Expand Down
2 changes: 2 additions & 0 deletions editors/vscode/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export async function activate(cx: vscode.ExtensionContext) {
documentSelector: [{ scheme: "file", language: "sml" }],
initializationOptions: {
show_token_hover: config.get("server.hover.token.enable") ?? false,
diagnostics_on_change:
config.get("server.diagnostics.onChange.enable") ?? false,
},
};
client = new LanguageClient("millet", serverOpts, clientOpts);
Expand Down

0 comments on commit 2a482f1

Please sign in to comment.