From 0326a09121ed652ec83e352580f55bce71bc59f8 Mon Sep 17 00:00:00 2001
From: SeeBasTStick <sebastian.stock@hhu.de>
Date: Fri, 29 May 2020 15:29:55 +0200
Subject: [PATCH] added new way to deal with errors

---
 package-lock.json          |   6 +-
 package.json               |   2 +-
 server/src/ErrorMatcher.ts | 247 -------------------------------------
 server/src/errorHandler.ts | 138 +++++++++++++++++++++
 server/src/server.ts       | 127 ++++++++++---------
 tsconfig.json              |   1 +
 6 files changed, 206 insertions(+), 315 deletions(-)
 delete mode 100644 server/src/ErrorMatcher.ts
 create mode 100644 server/src/errorHandler.ts

diff --git a/package-lock.json b/package-lock.json
index 2520e08..bc887e0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2206,9 +2206,9 @@
 			}
 		},
 		"vscode-uri": {
-			"version": "2.1.1",
-			"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-2.1.1.tgz",
-			"integrity": "sha512-eY9jmGoEnVf8VE8xr5znSah7Qt1P/xsCdErz+g8HYZtJ7bZqKH5E3d+6oVNm1AC/c6IHUDokbmVXKOi4qPAC9A=="
+			"version": "2.1.2",
+			"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-2.1.2.tgz",
+			"integrity": "sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A=="
 		},
 		"which": {
 			"version": "2.0.2",
diff --git a/package.json b/package.json
index 7fe80b6..6ec7be9 100644
--- a/package.json
+++ b/package.json
@@ -124,7 +124,7 @@
 		"stream-to-array": "^2.3.0",
 		"ts-xor": "^1.0.8",
 		"vscode-api": "0.0.0",
-		"vscode-uri": "^2.1.1",
+		"vscode-uri": "^2.1.2",
 		"without": "^1.2.3"
 	}
 }
diff --git a/server/src/ErrorMatcher.ts b/server/src/ErrorMatcher.ts
deleted file mode 100644
index 4f6c94d..0000000
--- a/server/src/ErrorMatcher.ts
+++ /dev/null
@@ -1,247 +0,0 @@
-import { Diagnostic, DiagnosticSeverity } from 'vscode-languageserver'
-import { TextDocument, Position } from 'vscode-languageserver-textdocument'
-import * as fs from 'fs';
-import * as readline from 'readline'
-import { Uri } from 'vscode';
-
-export class ErrorMatcher {
-
-
-    /**
-	 * Reads the error file, casts the errors to a readable form, sorts them by origin
-	 * @param errorPath the path to the file where the ndjson errors are stored
-	 */
-	async readErrors(errorPath : string) : Promise<Map<Uri, Set<Error | Warning | Information>>> {
-		let result : Map<Uri, Set<Error | Warning | Information>> = new Map()
-
-		var stream = fs.createReadStream(errorPath)
-
-		const rl = readline.createInterface({
-			input: stream,
-			crlfDelay: Infinity
-		});
-
-		type ErrorInfromationWarning = Error | Information | Warning
-		for await (const line of rl) {
-
-				let obj: ErrorInfromationWarning = JSON.parse(line)
-
-				let serveity: DiagnosticSeverity
-				let content: JBody
-				let path : Uri
-
-			
-
-				if (isError(obj)) {
-					path = Uri.parse(obj.error.file)
-				}
-				else {
-					if (isWarning(obj)) {
-						path = Uri.parse(obj.warning.file)
-
-					} else {
-						path = Uri.parse(obj.information.file)
-
-					}
-				}
-
-				if(!result.has(path)){
-					result.set(path, new Set())
-				}
-
-				result.get(path)!.add(obj)
-		}
-
-		return result
-	}
-
-
-	matchErrors(infos : Set<Error | Information | Warning>) : Set<Diagnostic>{
-		let result : Set<Diagnostic>
-		type ErrorInfromationWarning = Error | Information | Warning
-
-		for (let info : ErrorInfromationWarning in infos){
-
-			let serveity : DiagnosticSeverity
-
-			if(isError(info)){
-				serveity = DiagnosticSeverity.Error
-		
-			}else{			
-				if(isWarning(info)){
-					serveity = DiagnosticSeverity.Warning
-				}else{
-				serveity = DiagnosticSeverity.Warning
-			}}
-					
-
-			let diagnostic = {
-				severity: serveity,
-				range: {
-					start:
-					{
-						character: in.start.col,
-						line: content.start.line - 1
-					},
-					end: {
-						character: content.end.col,
-						line: content.end.line - 1
-					}
-				},
-				message: content.message,
-				source: 'prob_prolog',
-				
-			};
-			diagnostics.add(diagnostic)
-		}
-			)
-
-	}
-
-	async matchError(target: TextDocument, errorPath: string): Promise<Set<Diagnostic>> {
-		var diagnostics: Set<Diagnostic> = new Set()
-
-		var stream = fs.createReadStream(errorPath)
-
-		const rl = readline.createInterface({
-			input: stream,
-			crlfDelay: Infinity
-		});
-
-
-		for await (const line of rl) {
-
-			try {
-				let obj: XOR3<Error, Warning, Information> = JSON.parse(line)
-
-				let diagnostic: Diagnostic
-				let serveity: DiagnosticSeverity
-				let content: JBody
-
-				if (obj.error) {
-					content = obj.error
-					serveity = DiagnosticSeverity.Error
-				}
-				else {
-					if (obj.warning) {
-						content = obj.warning
-						serveity = DiagnosticSeverity.Warning
-					} else {
-						content = obj.information
-						serveity = DiagnosticSeverity.Information
-					}
-				}
-
-				
-
-				if(content.start.line == -1 && content.start.col == -1 && content.end.line == -1 && content.end.col == -1){
-					console.log("Error without position")
-					let targetLine = getFirstOccurence(target).line
-					console.log("new pos is: " + targetLine)
-					content.start = {line : targetLine , col: 0 }
-					content.end = {line : targetLine , col: Number.MAX_VALUE }
-				}
-				
-				
-				
-				diagnostic = {
-					severity: serveity,
-					range: {
-						start:
-						{
-							character: content.start.col,
-							line: content.start.line - 1
-						},
-						end: {
-							character: content.end.col,
-							line: content.end.line - 1
-						}
-					},
-					message: content.message,
-					source: 'prob_prolog',
-					
-				};
-				diagnostics.add(diagnostic)
-			}
-			catch (e) {
-				throw new Error("unexpected syntax  while parsing _error.json")
-			}
-		}
-		return diagnostics
-
-	}
-
-
-}
-
-
-export declare type XOR3<T, U, V> = (T | U | V) extends object ? ((Without<T, U> & U) & (Without<V, U> & U)) |
-	((Without<V, T> & T) & (Without<U, T> & T)) |
-	((Without<T, V> & V) & (Without<U, V> & V)) : T | U | V;
-
-
-export declare type Without<T, U> = {
-	[P in Exclude<keyof T, keyof U>]?: never;
-};
-
-
-export interface CommonInfo{}
-
-export interface Information extends CommonInfo {
-	information : JBody
-}
-
-
-export interface Error extends CommonInfo {
-	error: JBody;
-
-}
-
-export interface Warning extends CommonInfo{
-	warning: JBody
-}
-
-export interface JBody {
-	message: string;
-	type: string;
-	file: string;
-	start: StartOrEnd;
-	end: StartOrEnd;
-}
-
-export interface StartOrEnd {
-	line: number;
-	col: number;
-}
-
-function getFirstOccurence(textdocument : TextDocument) : Position {
-	let counter : number = textdocument.getText().search(/\S|$/)
-	let pos : Position = textdocument.positionAt(counter)
-	pos.line = pos.line + 1
-	return pos
-}
-
-
-function isError (potential : CommonInfo) : potential is Error{
-	if((potential as Error).error){
-		return true
-	}else{
-		return false
-	}
-}
-
-function isWarning (potential : CommonInfo) : potential is Warning{
-	if((potential as Warning).warning){
-		return true
-	}else{
-		return false
-	}
-}
-
-function isInformation (potential : CommonInfo) : potential is Information{
-	if((potential as Information).information){
-		return true
-	}else{
-		return false
-	}
-}
\ No newline at end of file
diff --git a/server/src/errorHandler.ts b/server/src/errorHandler.ts
new file mode 100644
index 0000000..74de5c0
--- /dev/null
+++ b/server/src/errorHandler.ts
@@ -0,0 +1,138 @@
+import { Diagnostic, DiagnosticSeverity } from 'vscode-languageserver'
+import { TextDocument, Position } from 'vscode-languageserver-textdocument'
+import * as fs from 'fs';
+import * as readline from 'readline'
+import { Uri } from 'vscode';
+
+
+
+/**
+ * Reads the error file, casts the errors to a readable form, sorts them by origin
+ * @param errorPath the path to the file where the ndjson errors are stored
+ */
+export async function readErrors(errorPath: string): Promise<Map<string, Set<NDJSON>>> {
+	let result: Map<string, Set<NDJSON>> = new Map()
+
+	var stream = fs.createReadStream(errorPath)
+
+	const rl = readline.createInterface({
+		input: stream,
+		crlfDelay: Infinity
+	});
+
+	for await (const line of rl) {
+
+		let obj: NDJSON = JSON.parse(line)
+
+		let path: string = obj.details.file
+
+
+		if (!result.has(path)) {
+			result.set(path, new Set())
+		}
+
+		result.get(path)!.add(obj)
+	}
+
+	return result
+}
+
+/**
+ * Builds diagnostics for a given Set of infos
+ * @param infos the set of infos to build diagnostics with
+ */
+export function matchErrors(infos: Set<NDJSON>, file?: TextDocument | undefined): Array<Diagnostic> {
+	let result: Array<Diagnostic> = new Array()
+
+	for (var info of infos) {
+
+
+		let serveity: DiagnosticSeverity = DiagnosticSeverity.Error
+
+		if (info.type == "error") {
+			serveity = DiagnosticSeverity.Error
+		}
+
+		if (info.type == "warning") {
+			serveity = DiagnosticSeverity.Warning
+		}
+
+		if (info.type == "information") {
+			serveity = DiagnosticSeverity.Information
+		}
+
+		let content: JBody = info.details
+
+		// Managing case we get conent with no error massage
+		if (content.start.line == -1 && content.start.col == -1 && content.end.line == -1 && content.end.col == -1) {
+			if (file != undefined) {
+				//Due to server architecture we only have the most actual document
+				let targetLine = getFirstOccurence(file).line
+				content.start = { line: targetLine, col: 0 }
+				content.end = { line: targetLine, col: Number.MAX_VALUE }
+			} else {
+				//Fallback if an error occurs on  differnt document
+				content.start = { line: 1, col: 0 }
+				content.end = { line: 1, col: Number.MAX_VALUE }
+			}
+		}
+
+		let diagnostic = {
+			severity: serveity,
+			range: {
+				start:
+				{
+					character: content.start.col,
+					line: content.start.line - 1
+				},
+				end: {
+					character: content.end.col,
+					line: content.end.line - 1
+				}
+			},
+			message: content.message,
+			source: 'prob_prolog',
+
+		};
+
+		result.push(diagnostic)
+	}
+
+	return result
+
+}
+
+
+////////////// Representation of the NDJSON File
+export interface NDJSON {
+	type: string
+	details: JBody
+}
+
+export interface JBody {
+	message: string;
+	type: string;
+	file: string;
+	start: StartOrEnd;
+	end: StartOrEnd;
+}
+
+export interface StartOrEnd {
+	line: number;
+	col: number;
+}
+
+///////////////////
+
+
+/**
+ * Returns the exakt positiion of the first occurence of an non whitespace token
+ * @param textdocument the Textdocument to analzie
+ */
+function getFirstOccurence(textdocument: TextDocument): Position {
+	let counter: number = textdocument.getText().search(/\S|$/)
+	let pos: Position = textdocument.positionAt(counter)
+	pos.line = pos.line + 1
+	return pos
+}
+
diff --git a/server/src/server.ts b/server/src/server.ts
index 5953b0e..dd293b5 100644
--- a/server/src/server.ts
+++ b/server/src/server.ts
@@ -9,8 +9,7 @@ import {
 	TextDocumentPositionParams,
 	TextDocumentSyncKind,
 	InitializeResult,
-	TextDocumentItem,
-	
+
 } from 'vscode-languageserver';
 
 import {
@@ -18,14 +17,9 @@ import {
 } from 'vscode-languageserver-textdocument';
 
 
-import {Proposed} from 'vscode-languageserver-protocol'
-
-
-
-
 import { URI } from 'vscode-uri';
 import * as fs from 'fs';
-import {ErrorMatcher} from './ErrorMatcher';
+import { NDJSON, readErrors, matchErrors } from './errorHandler';
 import * as wordComplition from './wordCompletion'
 import * as path from 'path';
 
@@ -34,7 +28,6 @@ import * as path from 'path';
 // Create a connection for the server. The connection uses Node's IPC as a transport.
 // Also include all preview / proposed LSP features.
 let connection = createConnection(ProposedFeatures.all);
-let test = createConnection(ProposedFeatures.all)
 
 // Create a simple text document manager. The text document manager
 // supports full document sync only
@@ -64,9 +57,9 @@ connection.onInitialize((params: InitializeParams) => {
 		capabilities.textDocument.publishDiagnostics.relatedInformation
 	);
 
-	
 
-	
+
+
 	const result: InitializeResult = {
 		capabilities: {
 			textDocumentSync: TextDocumentSyncKind.Full,
@@ -101,21 +94,21 @@ connection.onInitialized(() => {
 // The settings
 interface Settings {
 	maxNumberOfProblems: number
-	strictChecks : boolean
-	wdChecks : boolean
-	performanceHints : boolean
+	strictChecks: boolean
+	wdChecks: boolean
+	performanceHints: boolean
 	//PYTHONSETTING
-	probHome : string
+	probHome: string
 }
 
 
-const defaultSettings: Settings = { 
-	maxNumberOfProblems: 1000, 
+const defaultSettings: Settings = {
+	maxNumberOfProblems: 1000,
 	probHome: "~/prob_prolog/probcli.sh",
-	strictChecks : false,
-	wdChecks : false,
+	strictChecks: false,
+	wdChecks: false,
 	//PYTHONDEFAULT
-	performanceHints : false 
+	performanceHints: false
 };
 
 let globalSettings: Settings = defaultSettings;
@@ -170,76 +163,82 @@ documents.onDidSave(change => {
 
 async function validateTextDocument(textDocument: TextDocument): Promise<void> {
 	let settings = await getDocumentSettings(textDocument.uri) // Waiting for correct setting; otherwise allways default
-	
 
-	let documentPath:path.ParsedPath = path.parse(URI.parse(textDocument.uri).path);
+	let documentPath: path.ParsedPath = path.parse(URI.parse(textDocument.uri).path);
+
+	let errorPath: string = documentPath.dir + '/_error.json'
 
-	let errorPath:string = documentPath.dir+'/_error.json'
+	const { exec } = require('child_process');
 
-	const {exec} = require('child_process');
-	
-	fs.writeFile(errorPath, "", () =>{}) //Insure a clean error file
+	fs.writeFile(errorPath, "", () => { }) //Insure a clean error file
 
-	let command:string = getCommand(URI.parse(textDocument.uri).path, errorPath, settings) 
-	
-	let diagnostics : Array<Diagnostic> = new Array()
-	let diagnosticsPromise : Promise<Set<Diagnostic>>
+	let command: string = getCommand(URI.parse(textDocument.uri).path, errorPath, settings)
 
-	console.log(command)
-	if(correctPath(settings.probHome))
-	{
-		exec(command, (err:string, stdout:string, stderr:string) => {
-		let bla = new ErrorMatcher()
-	    diagnosticsPromise =  bla.matchError(textDocument, errorPath)
+	//console.log(command)
+	if (correctPath(settings.probHome)) {
+		exec(command, (err: string, stdout: string, stderr: string) => {
+			let errorMap: Promise<Map<string, Set<NDJSON>>> = readErrors(errorPath)
+			errorMap.then(function (result: Map<string, Set<NDJSON>>) {
 
-		diagnosticsPromise.then(function(result:Set<Diagnostic>){
-			diagnostics = Array.from(result)
-			connection.sendDiagnostics({ uri: textDocument.uri, diagnostics});	
-		}, function(err){
-			connection.sendNotification("parse_error_prob", "there are things wrong with the parse results " + err)
+				let diagnostics: Array<Diagnostic> = new Array()
+
+				for (let entry of result) {
+					if (entry[0] == textDocument.uri) {
+						diagnostics = matchErrors(entry[1], textDocument)
+					} else {
+						diagnostics = matchErrors(entry[1])
+					}
+					console.log("sending to " + entry[0])
+					connection.sendDiagnostics({ uri: entry[0], diagnostics });
+				}
+
+			}, function (err) {
+				connection.sendNotification("parse_error_prob", "there are things wrong with the parse results " + err)
+			});
 		})
-	  });
 	}
-
 }
 
-function getCommand(documentPath : string, errorPath : string, settings: Settings) : string{
+
+
+
+function getCommand(documentPath: string, errorPath: string, settings: Settings): string {
 	let wdCmd = ""
 	let strict = ""
 	let perf = ""
-	console.log(documentPath)
-		//PYTHONVAR
+//	console.log(documentPath)
+	//PYTHONVAR
 
-	if(settings.wdChecks == true){
+	if (settings.wdChecks == true) {
 		wdCmd = " -wd-check -release_java_parser "
 	}
 
-	if(settings.strictChecks == true){
+	if (settings.strictChecks == true) {
 		strict = " -p STRICT_CLASH_CHECKING TRUE -p TYPE_CHECK_DEFINITIONS TRUE -lint "
 	}
 
-	if(settings.performanceHints == true){
+	if (settings.performanceHints == true) {
 		perf = " -p PERFORMANCE_INFO TRUE "
 	}
 
 	//PYTHONIF
-	
-	return settings.probHome + ' -p MAX_INITIALISATIONS 0 -version ' 
-							 + perf 
-							 + strict 
-							 + wdCmd 
-							 /*PYTHONCMD*/
-							 + documentPath 
-							 +" -p " 
-							 + "NDJSON_ERROR_LOG_FILE " 
-							 + errorPath
+
+	return settings.probHome + ' -p MAX_INITIALISATIONS 0 -version '
+		+ perf
+		+ strict
+		+ wdCmd
+		/*PYTHONCMD*/
+		+ documentPath
+		+ " -p "
+		+ "NDJSON_ERROR_LOG_FILE "
+		+ errorPath
 }
 
 
-function correctPath(path:string): boolean{
-	try{
+function correctPath(path: string): boolean {
+	try {
 		fs.accessSync(path)
-	}catch(e){
+	} catch (e) {
 		connection.sendNotification("path_error_prob", path + " seems to be the wrong path to ProB")
 		return false
 	}
@@ -249,7 +248,7 @@ function correctPath(path:string): boolean{
 
 // This handler provides the initial list of the completion items.
 connection.onCompletion(
-	
+
 	(textDocumentPosition: TextDocumentPositionParams): CompletionItem[] => {
 		return wordComplition.selectCompletion(textDocumentPosition)
 	}
@@ -261,7 +260,7 @@ connection.onCompletion(
 
 //Can be used to enrich completion with more infos
 connection.onCompletionResolve(
-	(item : CompletionItem) => {return item})
+	(item: CompletionItem) => { return item })
 
 
 // for open, change and close text document events
diff --git a/tsconfig.json b/tsconfig.json
index a1a9313..ed91ffa 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,5 +1,6 @@
 {
 	"compilerOptions": {
+		"esModuleInterop": true,
 		"module": "commonjs",
 		"target": "es6",
 		"outDir": "out",
-- 
GitLab