Moving from maps of maps to a more sophisticated graph class for includes

This commit is contained in:
Noah Santschi-Cooney 2018-08-01 18:38:21 +01:00
parent 18c0e2ef5c
commit c95367739f
3 changed files with 49 additions and 31 deletions

View file

@ -23,4 +23,9 @@ export class Graph {
} }
this.nodes.set(parent, par) 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)
}
} }

View file

@ -2,7 +2,7 @@ import { Diagnostic, DiagnosticSeverity, Range } from 'vscode-languageserver'
import { connection, documents } from './server' import { connection, documents } from './server'
import { execSync } from 'child_process' import { execSync } from 'child_process'
import * as path from 'path' import * as path from 'path'
import { readFileSync, existsSync } from 'fs' import { readFileSync, existsSync, statSync, Stats } from 'fs'
import { conf } from './config' import { conf } from './config'
import { postError, formatURI, getDocumentContents } from './utils' import { postError, formatURI, getDocumentContents } from './utils'
import { platform } from 'os' import { platform } from 'os'
@ -11,7 +11,7 @@ import { Comment } from './comment'
const reDiag = /^(ERROR|WARNING): ([^?<>*|"]+?):(\d+): (?:'.*?' : )?(.+)\r?/ const reDiag = /^(ERROR|WARNING): ([^?<>*|"]+?):(\d+): (?:'.*?' : )?(.+)\r?/
const reVersion = /#version [\d]{3}/ 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 reIncludeExt = /#extension GL_GOOGLE_include_directive ?: ?require/
const include = '#extension GL_GOOGLE_include_directive : require' const include = '#extension GL_GOOGLE_include_directive : require'
const win = platform() === 'win32' const win = platform() === 'win32'
@ -25,7 +25,6 @@ const filters = [
export const includeGraph = new Graph() export const includeGraph = new Graph()
export const includeToParent = new Map<string, Set<string>>()
export const allFiles = new Set<string>() export const allFiles = new Set<string>()
type IncludeObj = { type IncludeObj = {
@ -99,17 +98,14 @@ export function preprocess(lines: string[], docURI: string) {
lint(docURI, lines, includeMap, diagnostics) lint(docURI, lines, includeMap, diagnostics)
} }
function buildIncludeTree(inc: IncludeObj) { const buildIncludeGraph = (inc: IncludeObj) => includeGraph.setParent(inc.path, inc.parent)
if (!includeToParent.has(inc.path)) includeToParent.set(inc.path, new Set([inc.parent]))
else includeToParent.get(inc.path).add(inc.parent)
}
function processIncludes(lines: string[], incStack: string[], allIncludes: IncludeObj[], diagnostics: Map<string, Diagnostic[]>, hasDirective: boolean) { function processIncludes(lines: string[], incStack: string[], allIncludes: IncludeObj[], diagnostics: Map<string, Diagnostic[]>, hasDirective: boolean) {
const includes = getIncludes(incStack[0], lines) const includes = getIncludes(incStack[0], lines)
allIncludes.push(...includes) allIncludes.push(...includes)
if (includes.length > 0) { if (includes.length > 0) {
includes.reverse().forEach(inc => { includes.reverse().forEach(inc => {
buildIncludeTree(inc) buildIncludeGraph(inc)
mergeInclude(inc, lines, incStack, diagnostics, hasDirective) mergeInclude(inc, lines, incStack, diagnostics, hasDirective)
}) })
// recursively check for more includes to be merged // 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<string, Diagnostic[]>) {
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<string, Diagnostic[]>, hasDirective: boolean) { function mergeInclude(inc: IncludeObj, lines: string[], incStack: string[], diagnostics: Map<string, Diagnostic[]>, hasDirective: boolean) {
if (!existsSync(inc.path)) { let stats: Stats
const range = calcRange(inc.lineNumTopLevel - (win ? 1 : 0), incStack[0]) try {
diagnostics.set( stats = statSync(inc.path)
inc.parent, } catch (e) {
[ if (e.code === 'ENOENT') {
...(diagnostics.get(inc.parent) || []), ifInvalidFile(inc, lines, incStack, diagnostics)
{ return
severity: DiagnosticSeverity.Error, }
range, throw e
message: `${inc.path.replace(conf.shaderpacksPath, '')} is missing.`, }
source: 'mc-glsl'
} if (!stats.isFile()) {
] ifInvalidFile(inc, lines, incStack, diagnostics)
)
lines[inc.lineNumTopLevel] = ''
return return
} }
const dataLines = readFileSync(inc.path).toString().split('\n') const dataLines = readFileSync(inc.path).toString().split('\n')
// if the includes parent is the top level (aka where the include directive is placed) // 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<string, IncludeObj>, d
// TODO what if we dont know the top level parent? Feel like thats a non-issue given that we have uri // TODO what if we dont know the top level parent? Feel like thats a non-issue given that we have uri
diag = { diag = {
severity: errorType(type), severity: errorType(type),
// TODO put -1 back when #11 is sorted
range: calcRange(includes.get(nextFile).lineNum, includes.get(nextFile).parent), range: calcRange(includes.get(nextFile).lineNum, includes.get(nextFile).parent),
message: `Line ${line} ${includes.get(file).path.replace(conf.shaderpacksPath, '')} ${replaceWords(msg)}`, message: `Line ${line} ${includes.get(file).path.replace(conf.shaderpacksPath, '')} ${replaceWords(msg)}`,
source: 'mc-glsl' source: 'mc-glsl'

View file

@ -1,7 +1,7 @@
import * as vsclang from 'vscode-languageserver' import * as vsclang from 'vscode-languageserver'
import * as vsclangproto from 'vscode-languageserver-protocol' import * as vsclangproto from 'vscode-languageserver-protocol'
import { completions } from './completionProvider' import { completions } from './completionProvider'
import { preprocess, ext, includeToParent } from './linter' import { preprocess, ext, includeToParent, includeGraph } from './linter'
import { extname } from 'path' import { extname } from 'path'
const reVersion = /#version [\d]{3}/ const reVersion = /#version [\d]{3}/
@ -39,7 +39,7 @@ documents.onDidClose((event) => connection.sendDiagnostics({uri: event.document.
export function onEvent(document: vsclangproto.TextDocument) { export function onEvent(document: vsclangproto.TextDocument) {
const uri = formatURI(document.uri) const uri = formatURI(document.uri)
if (includeToParent.has(uri)) { if (includeGraph.get(uri).parents.size > 0) {
lintBubbleDown(uri, document) lintBubbleDown(uri, document)
return return
} }
@ -54,13 +54,15 @@ export function onEvent(document: vsclangproto.TextDocument) {
} }
function lintBubbleDown(uri: string, document: vsclangproto.TextDocument) { function lintBubbleDown(uri: string, document: vsclangproto.TextDocument) {
includeToParent.get(uri).forEach(parent => { includeGraph.get(uri).parents.forEach((parent, parentURI) => {
if (includeToParent.has(parent)) { console.log(parentURI)
lintBubbleDown(parent, document) console.log(parent.parents)
if (parent.parents.size > 0) {
lintBubbleDown(parentURI, document)
} else { } else {
const lines = getDocumentContents(parent).split('\n') const lines = getDocumentContents(parentURI).split('\n')
if (lines.filter(l => reVersion.test(l)).length > 0) { if (lines.filter(l => reVersion.test(l)).length > 0) {
preprocess(lines, parent) preprocess(lines, parentURI)
} }
} }
}) })