diff --git a/server/src/linter.ts b/server/src/linter.ts index 37d2a40..464473f 100644 --- a/server/src/linter.ts +++ b/server/src/linter.ts @@ -4,7 +4,7 @@ import { execSync } from 'child_process' import * as path from 'path' import { readFileSync, existsSync } from 'fs' import { conf } from './config' -import { postError, formatURI } from './utils' +import { postError, formatURI, getDocumentContents } from './utils' import { platform } from 'os' const reDiag = /^(ERROR|WARNING): ([^?<>*|"]+?):(\d+): (?:'.*?' : )?(.+)\r?/ @@ -21,7 +21,7 @@ const filters = [ /Could not process include directive for header name:/ ] -const includeToParent = new Map() +export const includeToParent = new Map>() type IncludeObj = { lineNum: number, @@ -79,7 +79,8 @@ export function isInComment(line: string, state: Comment): Comment { } export function preprocess(lines: string[], docURI: string) { - if (lines.find((value: string, i: number, obj: string[]): boolean => reIncludeExt.test(value)) == undefined) { + // wish there was an ignore keyword like Go + if (lines.find((value: string, _, __): boolean => reIncludeExt.test(value)) == undefined) { for (let i = 0; i < lines.length; i++) { const line = lines[i] if (reVersion.test(line)) { @@ -93,17 +94,19 @@ export function preprocess(lines: string[], docURI: string) { } } - const incStack = [docURI] const allIncludes: IncludeObj[] = [] const diagnostics = new Map() - processIncludes(lines, incStack, allIncludes, diagnostics) + + processIncludes(lines, [docURI], allIncludes, diagnostics) const includeMap = new Map(allIncludes.map(obj => [obj.path, obj]) as [string, IncludeObj][]) - try { - lint(docURI, lines, includeMap, diagnostics) - } catch (e) { - postError(e) - } + + 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) } function processIncludes(lines: string[], incStack: string[], allIncludes: IncludeObj[], diagnostics: Map) { @@ -111,6 +114,7 @@ function processIncludes(lines: string[], incStack: string[], allIncludes: Inclu allIncludes.push(...includes) if (includes.length > 0) { includes.reverse().forEach(inc => { + buildIncludeTree(inc) mergeInclude(inc, lines, incStack, diagnostics) }) // recursively check for more includes to be merged @@ -223,7 +227,8 @@ 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), - range: calcRange(includes.get(nextFile).lineNum - 1, includes.get(nextFile).parent), + // TODO put -1 back when #11 is sorted + range: calcRange(includes.get(nextFile).lineNum, includes.get(nextFile).parent), message: includes.get(file).path.replace(conf.shaderpacksPath, '') + replaceWords(msg), source: 'mc-glsl' } @@ -253,14 +258,10 @@ const filterMatches = (output: string) => output .filter(match => match && match.length === 5) function calcRange(lineNum: number, uri: string): Range { - let lines = [] - // TODO better error handling maybe? - if (documents.keys().includes('file://' + uri)) { - lines = documents.get('file://' + uri).getText().split('\n') - } else { - lines = readFileSync(uri).toString().split('\n') - } + const lines = getDocumentContents(uri).split('\n') + console.log(uri, lineNum, lines.length) + console.log(lines.slice(Math.max(lineNum - 3, 0), lineNum + 3).join('\n')) const line = lines[lineNum] const startOfLine = line.length - line.trimLeft().length diff --git a/server/src/server.ts b/server/src/server.ts index 22e0c2b..70eb560 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -1,14 +1,16 @@ import * as vsclang from 'vscode-languageserver' import * as vsclangproto from 'vscode-languageserver-protocol' import { completions } from './completionProvider' -import { preprocess, ext } from './linter' +import { preprocess, ext, includeToParent } from './linter' import { extname } from 'path' +const reVersion = /#version [\d]{3}/ + export let connection: vsclang.IConnection connection = vsclang.createConnection(new vsclang.IPCMessageReader(process), new vsclang.IPCMessageWriter(process)) import { onConfigChange } from './config' -import { formatURI } from './utils' +import { formatURI, postError, getDocumentContents } from './utils' export const documents = new vsclang.TextDocuments() documents.listen(connection) @@ -36,15 +38,34 @@ documents.onDidClose((event) => connection.sendDiagnostics({uri: event.document. //documents.onDidChangeContent(onEvent) export function onEvent(document: vsclangproto.TextDocument) { - if (!ext.has(extname(document.uri))) return - try { - console.log(document.uri) - console.log(formatURI(document.uri)) - preprocess(document.getText().split('\n'), formatURI(document.uri)) - } catch (e) { - connection.window.showErrorMessage(`[mc-glsl] ${e.message}`) - throw(e) + const uri = formatURI(document.uri) + if (includeToParent.has(uri)) { + lintBubbleDown(uri, document) + return } + + if (!ext.has(extname(document.uri))) return + + try { + preprocess(document.getText().split('\n'), uri) + } catch (e) { + postError(e) + } +} + +function lintBubbleDown(uri: string, document: vsclangproto.TextDocument) { + includeToParent.get(uri).forEach(parent => { + console.log(`${uri} has parent ${parent}`) + if (includeToParent.has(parent)) { + lintBubbleDown(parent, document) + } else { + const lines = getDocumentContents(parent).split('\n') + if (lines.filter(l => reVersion.test(l)).length > 0) { + console.log(`${parent} is at top, linting down`) + preprocess(lines, parent) + } + } + }) } connection.onDidChangeConfiguration(onConfigChange) diff --git a/server/src/utils.ts b/server/src/utils.ts index 57b8991..020770f 100644 --- a/server/src/utils.ts +++ b/server/src/utils.ts @@ -1,8 +1,14 @@ -import { connection } from './server' +import { connection, documents } from './server' +import { readFileSync } from 'fs' export function postError(e: Error) { connection.window.showErrorMessage(e.message) console.log(e) } -export const formatURI = (uri: string) => uri.replace(/^file:\/\//, '').replace(/^(?:\/)c%3A/, 'C:').replace(/\\/g, '/') \ No newline at end of file +export const formatURI = (uri: string) => uri.replace(/^file:\/\//, '').replace(/^(?:\/)c%3A/, 'C:').replace(/\\/g, '/') + +export function getDocumentContents(uri: string): string { + if (documents.keys().includes('file://' + uri)) return documents.get('file://' + uri).getText() + else return readFileSync(uri).toString() +} \ No newline at end of file