diff --git a/server/src/graph.ts b/server/src/graph.ts index 20687d4..5745fb9 100644 --- a/server/src/graph.ts +++ b/server/src/graph.ts @@ -23,4 +23,9 @@ export class Graph { } this.nodes.set(parent, par) } + + public get(uri: string): Node { + if (!this.nodes.has(uri)) this.nodes.set(uri, {parents: new Map(), children: new Map()}) + return this.nodes.get(uri) + } } \ No newline at end of file diff --git a/server/src/linter.ts b/server/src/linter.ts index 60e612c..360b00d 100644 --- a/server/src/linter.ts +++ b/server/src/linter.ts @@ -2,7 +2,7 @@ import { Diagnostic, DiagnosticSeverity, Range } from 'vscode-languageserver' import { connection, documents } from './server' import { execSync } from 'child_process' import * as path from 'path' -import { readFileSync, existsSync } from 'fs' +import { readFileSync, existsSync, statSync, Stats } from 'fs' import { conf } from './config' import { postError, formatURI, getDocumentContents } from './utils' import { platform } from 'os' @@ -11,7 +11,7 @@ import { Comment } from './comment' const reDiag = /^(ERROR|WARNING): ([^?<>*|"]+?):(\d+): (?:'.*?' : )?(.+)\r?/ const reVersion = /#version [\d]{3}/ -const reInclude = /^(?:\s)*?(?:#include) "((?:\/?[^?<>:*|"]+?)+?\.(?:[a-zA-Z]+?))"\r?/ +const reInclude = /^(?:\s)*?(?:#include) "(.+)"\r?/ const reIncludeExt = /#extension GL_GOOGLE_include_directive ?: ?require/ const include = '#extension GL_GOOGLE_include_directive : require' const win = platform() === 'win32' @@ -25,7 +25,6 @@ const filters = [ export const includeGraph = new Graph() -export const includeToParent = new Map>() export const allFiles = new Set() type IncludeObj = { @@ -99,17 +98,14 @@ export function preprocess(lines: string[], docURI: string) { lint(docURI, lines, includeMap, diagnostics) } -function buildIncludeTree(inc: IncludeObj) { - if (!includeToParent.has(inc.path)) includeToParent.set(inc.path, new Set([inc.parent])) - else includeToParent.get(inc.path).add(inc.parent) -} +const buildIncludeGraph = (inc: IncludeObj) => includeGraph.setParent(inc.path, inc.parent) function processIncludes(lines: string[], incStack: string[], allIncludes: IncludeObj[], diagnostics: Map, hasDirective: boolean) { const includes = getIncludes(incStack[0], lines) allIncludes.push(...includes) if (includes.length > 0) { includes.reverse().forEach(inc => { - buildIncludeTree(inc) + buildIncludeGraph(inc) mergeInclude(inc, lines, incStack, diagnostics, hasDirective) }) // recursively check for more includes to be merged @@ -162,24 +158,40 @@ export function getIncludes(uri: string, lines: string[]) { }, []) } +function ifInvalidFile(inc: IncludeObj, lines: string[], incStack: string[], diagnostics: Map) { + const range = calcRange(inc.lineNumTopLevel - (win ? 1 : 0), incStack[0]) + diagnostics.set( + inc.parent, + [ + ...(diagnostics.get(inc.parent) || []), + { + severity: DiagnosticSeverity.Error, + range, + message: `${inc.path.replace(conf.shaderpacksPath, '')} is missing or an invalid file.`, + source: 'mc-glsl' + } + ] + ) + lines[inc.lineNumTopLevel] = '' +} + function mergeInclude(inc: IncludeObj, lines: string[], incStack: string[], diagnostics: Map, hasDirective: boolean) { - if (!existsSync(inc.path)) { - const range = calcRange(inc.lineNumTopLevel - (win ? 1 : 0), incStack[0]) - diagnostics.set( - inc.parent, - [ - ...(diagnostics.get(inc.parent) || []), - { - severity: DiagnosticSeverity.Error, - range, - message: `${inc.path.replace(conf.shaderpacksPath, '')} is missing.`, - source: 'mc-glsl' - } - ] - ) - lines[inc.lineNumTopLevel] = '' + let stats: Stats + try { + stats = statSync(inc.path) + } catch (e) { + if (e.code === 'ENOENT') { + ifInvalidFile(inc, lines, incStack, diagnostics) + return + } + throw e + } + + if (!stats.isFile()) { + ifInvalidFile(inc, lines, incStack, diagnostics) return } + const dataLines = readFileSync(inc.path).toString().split('\n') // if the includes parent is the top level (aka where the include directive is placed) @@ -230,7 +242,6 @@ function lint(uri: string, lines: string[], includes: Map, d // TODO what if we dont know the top level parent? Feel like thats a non-issue given that we have uri diag = { severity: errorType(type), - // TODO put -1 back when #11 is sorted range: calcRange(includes.get(nextFile).lineNum, includes.get(nextFile).parent), message: `Line ${line} ${includes.get(file).path.replace(conf.shaderpacksPath, '')} ${replaceWords(msg)}`, source: 'mc-glsl' diff --git a/server/src/server.ts b/server/src/server.ts index c37601c..4d2f6e7 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -1,7 +1,7 @@ import * as vsclang from 'vscode-languageserver' import * as vsclangproto from 'vscode-languageserver-protocol' import { completions } from './completionProvider' -import { preprocess, ext, includeToParent } from './linter' +import { preprocess, ext, includeToParent, includeGraph } from './linter' import { extname } from 'path' const reVersion = /#version [\d]{3}/ @@ -39,7 +39,7 @@ documents.onDidClose((event) => connection.sendDiagnostics({uri: event.document. export function onEvent(document: vsclangproto.TextDocument) { const uri = formatURI(document.uri) - if (includeToParent.has(uri)) { + if (includeGraph.get(uri).parents.size > 0) { lintBubbleDown(uri, document) return } @@ -54,13 +54,15 @@ export function onEvent(document: vsclangproto.TextDocument) { } function lintBubbleDown(uri: string, document: vsclangproto.TextDocument) { - includeToParent.get(uri).forEach(parent => { - if (includeToParent.has(parent)) { - lintBubbleDown(parent, document) + includeGraph.get(uri).parents.forEach((parent, parentURI) => { + console.log(parentURI) + console.log(parent.parents) + if (parent.parents.size > 0) { + lintBubbleDown(parentURI, document) } else { - const lines = getDocumentContents(parent).split('\n') + const lines = getDocumentContents(parentURI).split('\n') if (lines.filter(l => reVersion.test(l)).length > 0) { - preprocess(lines, parent) + preprocess(lines, parentURI) } } })