Skip to content

feat(analysis): add diagnostics syntax #457

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

Merged
merged 13 commits into from
Jun 29, 2022
Merged
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
7 changes: 7 additions & 0 deletions analysis/src/Cli.ml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ API examples:
./rescript-editor-analysis.exe hover src/MyFile.res 10 2
./rescript-editor-analysis.exe references src/MyFile.res 10 2
./rescript-editor-analysis.exe rename src/MyFile.res 10 2 foo
./rescript-editor-analysis.exe diagnosticSyntax src/MyFile.res

Dev-time examples:
./rescript-editor-analysis.exe dump src/MyFile.res src/MyFile2.res
Expand Down Expand Up @@ -60,6 +61,10 @@ Options:

./rescript-editor-analysis.exe format src/MyFile.res

diagnosticSyntax: print to stdout diagnostic for syntax

./rescript-editor-analysis.exe diagnosticSyntax src/MyFile.res

test: run tests specified by special comments in file src/MyFile.res

./rescript-editor-analysis.exe test src/src/MyFile.res
Expand Down Expand Up @@ -88,6 +93,8 @@ let main () =
Commands.codeAction ~path
~pos:(int_of_string line, int_of_string col)
~currentFile ~debug:false
| [_; "diagnosticSyntax"; path;] ->
Commands.diagnosticSyntax ~path
| _ :: "reanalyze" :: _ ->
let len = Array.length Sys.argv in
for i = 1 to len - 2 do
Expand Down
5 changes: 5 additions & 0 deletions analysis/src/Commands.ml
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,10 @@ let format ~path =
signature
else ""

let diagnosticSyntax ~path =
print_endline
(Diagnostics.document_syntax ~path |> Protocol.array)

let test ~path =
Uri.stripPath := true;
match Files.readFile path with
Expand Down Expand Up @@ -378,6 +382,7 @@ let test ~path =
Printf.printf "%s\nnewText:\n%s<--here\n%s%s\n"
(Protocol.stringifyRange range)
indent indent newText)))
| "dia" -> diagnosticSyntax ~path
| _ -> ());
print_newline ())
in
Expand Down
33 changes: 33 additions & 0 deletions analysis/src/Diagnostics.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
let document_syntax ~path =
let get_diagnostics diagnostics =
diagnostics
|> List.map (fun diagnostic ->
let _, startline, startcol =
Location.get_pos_info (Res_diagnostics.getStartPos diagnostic)
in
let _, endline, endcol =
Location.get_pos_info (Res_diagnostics.getEndPos diagnostic)
in
Protocol.stringifyDiagnostic
{
range =
{
start = {line = startline - 1; character = startcol};
end_ = {line = endline - 1; character = endcol};
};
message = Res_diagnostics.explain diagnostic;
severity = 1;
})
in
if FindFiles.isImplementation path then
let parseImplementation =
Res_driver.parsingEngine.parseImplementation ~forPrinter:false
~filename:path
in
get_diagnostics parseImplementation.diagnostics
else if FindFiles.isInterface path then
let parseInterface =
Res_driver.parsingEngine.parseInterface ~forPrinter:false ~filename:path
in
get_diagnostics parseInterface.diagnostics
else []
28 changes: 22 additions & 6 deletions analysis/src/Protocol.ml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,16 @@ type completionItem = {
documentation: markupContent option;
}

type hover = string
type location = {uri: string; range: range}
type documentSymbolItem = {name: string; kind: int; location: location}
type renameFile = {oldUri: string; newUri: string}
type textEdit = {range: range; newText: string}
type location = {uri : string; range : range}
type documentSymbolItem = {name : string; kind : int; location : location}
type renameFile = {oldUri : string; newUri : string}
type textEdit = {range : range; newText : string}

type diagnostic = {
range : range;
message : string;
severity : int;
}

type optionalVersionedTextDocumentIdentifier = {
version: int option;
Expand Down Expand Up @@ -89,7 +94,7 @@ let stringifyRenameFile {oldUri; newUri} =
}|}
(Json.escape oldUri) (Json.escape newUri)

let stringifyTextEdit te =
let stringifyTextEdit (te : textEdit) =
Printf.sprintf {|{
"range": %s,
"newText": "%s"
Expand Down Expand Up @@ -126,3 +131,14 @@ let stringifyCodeAction ca =
Printf.sprintf {|{"title": "%s", "kind": "%s", "edit": %s}|} ca.title
(codeActionKindToString ca.codeActionKind)
(ca.edit |> stringifyCodeActionEdit)

(* https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#diagnostic *)
let stringifyDiagnostic d =
Printf.sprintf {|{
"range": %s,
"message": "%s",
"severity": %d,
"source": "ReScript"
}|}
(stringifyRange d.range) (Json.escape d.message)
d.severity
5 changes: 5 additions & 0 deletions analysis/tests/not_compiled/Diagnostics.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
let = 1 + 1.0
let add = =2
lett a = 2

//^dia
17 changes: 17 additions & 0 deletions analysis/tests/not_compiled/expected/Diagnostics.res.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[{
"range": {"start": {"line": 2, "character": 4}, "end": {"line": 2, "character": 6}},
"message": "consecutive statements on a line must be separated by ';' or a newline",
"severity": 1,
"source": "ReScript"
}, {
"range": {"start": {"line": 1, "character": 9}, "end": {"line": 1, "character": 11}},
"message": "This let-binding misses an expression",
"severity": 1,
"source": "ReScript"
}, {
"range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}},
"message": "I was expecting a name for this let-binding. Example: `let message = \"hello\"`",
"severity": 1,
"source": "ReScript"
}]

9 changes: 9 additions & 0 deletions analysis/tests/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ for file in src/*.{res,resi}; do
fi
done

for file in not_compiled/*.res; do
output="$(dirname $file)/expected/$(basename $file).txt"
../rescript-editor-analysis.exe test $file &> $output
# CI. We use LF, and the CI OCaml fork prints CRLF. Convert.
if [ "$RUNNER_OS" == "Windows" ]; then
perl -pi -e 's/\r\n/\n/g' -- $output
fi
done

warningYellow='\033[0;33m'
successGreen='\033[0;32m'
reset='\033[0m'
Expand Down
32 changes: 31 additions & 1 deletion server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ let projectsFiles: Map<
let codeActionsFromDiagnostics: codeActions.filesCodeActions = {};

// will be properly defined later depending on the mode (stdio/node-rpc)
let send: (msg: p.Message) => void = (_) => {};
let send: (msg: p.Message) => void = (_) => { };

interface CreateInterfaceRequestParams {
uri: string;
Expand Down Expand Up @@ -598,6 +598,34 @@ function format(msg: p.RequestMessage): Array<p.Message> {
}
}

const updateDiagnosticSyntax = (fileUri: string, fileContent: string) => {
let filePath = fileURLToPath(fileUri);
let extension = path.extname(filePath);
let tmpname = utils.createFileInTempDir(extension);
fs.writeFileSync(tmpname, fileContent, { encoding: "utf-8" });

const items: p.Diagnostic[] | [] = utils.runAnalysisAfterSanityCheck(
filePath,
[
"diagnosticSyntax",
tmpname
],
);

const notification: p.NotificationMessage = {
jsonrpc: c.jsonrpcVersion,
method: "textDocument/publishDiagnostics",
params: {
uri: fileUri,
diagnostics: items
}
}

fs.unlink(tmpname, () => null);

send(notification)
}

function createInterface(msg: p.RequestMessage): p.Message {
let params = msg.params as CreateInterfaceRequestParams;
let extension = path.extname(params.uri);
Expand Down Expand Up @@ -784,6 +812,7 @@ function onMessage(msg: p.Message) {
} else if (msg.method === DidOpenTextDocumentNotification.method) {
let params = msg.params as p.DidOpenTextDocumentParams;
openedFile(params.textDocument.uri, params.textDocument.text);
updateDiagnosticSyntax(params.textDocument.uri, params.textDocument.text);
} else if (msg.method === DidChangeTextDocumentNotification.method) {
let params = msg.params as p.DidChangeTextDocumentParams;
let extName = path.extname(params.textDocument.uri);
Expand All @@ -797,6 +826,7 @@ function onMessage(msg: p.Message) {
params.textDocument.uri,
changes[changes.length - 1].text
);
updateDiagnosticSyntax(params.textDocument.uri, changes[changes.length - 1].text);
}
}
} else if (msg.method === DidCloseTextDocumentNotification.method) {
Expand Down
8 changes: 4 additions & 4 deletions server/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -603,9 +603,9 @@ export let parseCompilerLogOutput = (
// 10 ┆
} else if (line.startsWith(" ")) {
// part of the actual diagnostics message
parsedDiagnostics[parsedDiagnostics.length - 1].content.push(
line.slice(2)
);
parsedDiagnostics[parsedDiagnostics.length - 1].content.push(
line.slice(2)
);
} else if (line.trim() != "") {
// We'll assume that everything else is also part of the diagnostics too.
// Most of these should have been indented 2 spaces; sadly, some of them
Expand Down Expand Up @@ -635,7 +635,7 @@ export let parseCompilerLogOutput = (
range,
source: "ReScript",
// remove start and end whitespaces/newlines
message: diagnosticMessage.join("\n").trim() + "\n",
message: diagnosticMessage.join("\n").trim(),
};

// Check for potential code actions
Expand Down