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

feat(analysis): add diagnostics syntax #457

Merged
merged 13 commits into from
Jun 29, 2022
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
7 changes: 7 additions & 0 deletions analysis/src/Commands.ml
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,12 @@ let format ~path =
signature
else ""

let diagnosticSyntax ~path =
print_endline
(match Diagnostics.document_syntax ~path with
| [] -> "[]"
| d -> Protocol.array d)

let test ~path =
Uri.stripPath := true;
match Files.readFile path with
Expand Down Expand Up @@ -378,6 +384,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
26 changes: 26 additions & 0 deletions analysis/src/Diagnostics.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
let document_syntax ~path =
let parse =
Res_driver.parsingEngine.parseImplementation ~forPrinter:false
~filename:path
in
match parse.diagnostics with
| [] -> []
| diagnostics ->
diagnostics
|> List.rev_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 = Error;
})
32 changes: 26 additions & 6 deletions analysis/src/Protocol.ml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,17 @@ 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 diagnosticSeverity = Error | Warning | Information | Hint
type diagnostic = {
range : range;
message : string;
severity : diagnosticSeverity;
}

type optionalVersionedTextDocumentIdentifier = {
version: int option;
Expand Down Expand Up @@ -89,7 +95,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 +132,17 @@ let stringifyCodeAction ca =
Printf.sprintf {|{"title": "%s", "kind": "%s", "edit": %s}|} ca.title
(codeActionKindToString ca.codeActionKind)
(ca.edit |> stringifyCodeActionEdit)

let stringifyDiagnostic d =
Printf.sprintf {|{
"range": %s,
"message": "%s",
"severity": %d,
"source": "ReScript"
}|}
(stringifyRange d.range) (Json.escape d.message)
(match d.severity with
| Error -> 1
| Warning -> 2
| Information -> 3
| Hint -> 4)
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": 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"
}, {
"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": 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"
}]

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
31 changes: 30 additions & 1 deletion server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,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 @@ -588,6 +588,33 @@ function format(msg: p.RequestMessage): Array<p.Message> {
}
}

const updateDiagnosticSyntax = (fileUri: string, fileContent: string) => {
const filePath = fileURLToPath(fileUri);
const tmpname = utils.createFileInTempDir();
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 @@ -774,6 +801,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 @@ -787,6 +815,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