diff --git a/server/package-lock.json b/server/package-lock.json index 9b2beea..7c3f749 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -71,6 +71,14 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "error-stack-parser": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-1.3.6.tgz", + "integrity": "sha1-4Oc7k+QXE40c18C3RrGkoUhUwpI=", + "requires": { + "stackframe": "^0.3.1" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -228,6 +236,50 @@ "readable-stream": "~1.0.31" } }, + "source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=" + }, + "stack-generator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-1.1.0.tgz", + "integrity": "sha1-NvapIHUabBD0maE8Msu19RoLiyU=", + "requires": { + "stackframe": "^1.0.2" + }, + "dependencies": { + "stackframe": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.0.4.tgz", + "integrity": "sha512-to7oADIniaYwS3MhtCa/sQhrxidCCQiF/qp4/m5iN3ipf0Y7Xlri0f6eG29r08aL7JYl8n32AF3Q5GYBZ7K8vw==" + } + } + }, + "stackframe": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-0.3.1.tgz", + "integrity": "sha1-M6qE8Rd6VUjIk1Uzy/6zQgl19aQ=" + }, + "stacktrace-gps": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-2.4.4.tgz", + "integrity": "sha1-acgn6dbW9Bz0ONfxleLjy/zyjEQ=", + "requires": { + "source-map": "0.5.6", + "stackframe": "~0.3" + } + }, + "stacktrace-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-1.3.1.tgz", + "integrity": "sha1-Z8qyWJr1xBe5Yvc2mUAne7O2oYs=", + "requires": { + "error-stack-parser": "^1.3.6", + "stack-generator": "^1.0.7", + "stacktrace-gps": "^2.4.3" + } + }, "string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", @@ -238,6 +290,14 @@ "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=" }, + "typescript-logging": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/typescript-logging/-/typescript-logging-0.6.2.tgz", + "integrity": "sha512-znbrZh0bjXH2QJrKz/EOiX/FXG6U8E38qLXZPlScP0oVQr+NOxAUfyIrLPZqeoq1Ebseyo66mYAvvp9xgCvlSg==", + "requires": { + "stacktrace-js": "1.3.1" + } + }, "unzip": { "version": "0.1.11", "resolved": "https://registry.npmjs.org/unzip/-/unzip-0.1.11.tgz", diff --git a/server/package.json b/server/package.json index a84fa35..7cd66f7 100644 --- a/server/package.json +++ b/server/package.json @@ -15,6 +15,7 @@ "@types/node-fetch": "^2.1.1", "@types/unzip": "^0.1.1", "node-fetch": "^2.1.2", + "typescript-logging": "^0.6.2", "unzip": "^0.1.11", "vscode-languageserver": "^4.1.3" }, diff --git a/server/src/linter.ts b/server/src/linter.ts index 8f68a28..e0cb61f 100644 --- a/server/src/linter.ts +++ b/server/src/linter.ts @@ -4,10 +4,11 @@ import { execSync } from 'child_process' import * as path from 'path' import { readFileSync, existsSync, statSync, Stats } from 'fs' import { conf } from './config' -import { postError, formatURI, getDocumentContents } from './utils' +import { postError, formatURI, getDocumentContents, trimPath } from './utils' import { platform } from 'os' import { Graph } from './graph' import { Comment } from './comment' +import { linterLog } from './logging' const reDiag = /^(ERROR|WARNING): ([^?<>*|"]+?):(\d+): (?:'.*?' : )?(.+)\r?/ const reVersion = /#version [\d]{3}/ @@ -25,8 +26,6 @@ const filters = [ export const includeGraph = new Graph() -export const allFiles = new Set() - type IncludeObj = { lineNum: number, lineNumTopLevel: number, @@ -83,41 +82,51 @@ const tokens = new Map([ ]) export function preprocess(lines: string[], docURI: string) { - let hasDirective = true - // wish there was an ignore keyword like Go - if (lines.find((value: string, _, __): boolean => reIncludeExt.test(value)) == undefined) { - hasDirective = false - for (let i = 0; i < lines.length; i++) { - const line = lines[i] - if (reVersion.test(line)) { - lines.splice(i + 1, 0, include) - break - } - if (i === lines.length - 1) { - lines.splice(0, 0, include) - break - } - } - } + const hasDirective = includeDirective(lines, docURI) - const allIncludes: IncludeObj[] = [] + const allIncludes = new Set() const diagnostics = new Map() processIncludes(lines, [docURI], allIncludes, diagnostics, hasDirective) - allIncludes.forEach(inc => allFiles.add(inc.path)) - - const includeMap = new Map(allIncludes.map(obj => [obj.path, obj]) as [string, IncludeObj][]) + const includeMap = new Map(Array.from(allIncludes).map(obj => [obj.path, obj]) as [string, IncludeObj][]) lint(docURI, lines, includeMap, diagnostics) } +function includeDirective(lines: string[], docURI: string): boolean { + if (lines.findIndex(x => reIncludeExt.test(x)) > -1) { + linterLog.info(() => 'include directive found') + return true + } + + let hasDirective = true + linterLog.info(() => 'include directive not found') + hasDirective = false + for (let i = 0; i < lines.length; i++) { + const line = lines[i] + if (reVersion.test(line)) { + linterLog.info(() => 'found version on line ' + (i + 1)) + lines.splice(i + 1, 0, include) + break + } + + if (i === lines.length - 1) { + linterLog.warn(() => `no version found for ${docURI}. inserting at top`) + lines.splice(0, 0, include) + break + } + } + return hasDirective +} + const buildIncludeGraph = (inc: IncludeObj) => includeGraph.setParent(inc.path, inc.parent, inc.lineNum) -function processIncludes(lines: string[], incStack: string[], allIncludes: IncludeObj[], diagnostics: Map, hasDirective: boolean) { +function processIncludes(lines: string[], incStack: string[], allIncludes: Set, diagnostics: Map, hasDirective: boolean) { const includes = getIncludes(incStack[0], lines) - allIncludes.push(...includes) + includes.forEach(i => allIncludes.add(i)) if (includes.length > 0) { + linterLog.info(() => `${trimPath(incStack[0])} has ${includes.length} include(s). [${includes.map(i => '\n\t\t' + trimPath(i.path))}\n\t]`) includes.reverse().forEach(inc => { buildIncludeGraph(inc) mergeInclude(inc, lines, incStack, diagnostics, hasDirective) @@ -144,12 +153,17 @@ export function getIncludes(uri: string, lines: string[]) { function processLine(includes: IncludeObj[], line: string, lines: string[], i: number, linesInfo: LinesProcessingInfo): IncludeObj[] { const updated = Comment.update(line, linesInfo.comment) linesInfo.comment = updated[0] + + if (updated[1] !== line) linterLog.debug(() => `change:\n\t'${line}'\n\t'${updated[1]}'`) + line = updated[1] lines[i] = line linesInfo.count[linesInfo.count.length - 1]++ linesInfo.total++ + if (linesInfo.comment) return includes + if (line.startsWith('#line')) { const inc = line.slice(line.indexOf('"') + 1, line.lastIndexOf('"')) @@ -178,11 +192,15 @@ function processLine(includes: IncludeObj[], line: string, lines: string[], i: n } function ifInvalidFile(inc: IncludeObj, lines: string[], incStack: string[], diagnostics: Map) { + const msg = `${trimPath(inc.path)} is missing or an invalid file.` + + linterLog.error(msg, null) + const file = incStack[incStack.length - 1] const diag: Diagnostic = { severity: DiagnosticSeverity.Error, - range: calcRange(inc.lineNum - (win ? 1 : 0), file), - message: `${inc.path.replace(conf.shaderpacksPath, '')} is missing or an invalid file.`, + range: calcRange(inc.lineNum, file), + message: msg, source: 'mc-glsl' } @@ -192,8 +210,7 @@ function ifInvalidFile(inc: IncludeObj, lines: string[], incStack: string[], dia const error: ErrorMatch = { type: DiagnosticSeverity.Error, line: inc.lineNum, - msg: `${inc.path.replace(conf.shaderpacksPath, '')} is missing or an invalid file.`, - file, + msg, file, } propogateDiagnostic(error, diagnostics) } @@ -232,7 +249,6 @@ function mergeInclude(inc: IncludeObj, lines: string[], incStack: string[], diag } function lint(docURI: string, lines: string[], includes: Map, diagnostics: Map) { - console.log(lines.join('\n')) let out: string = '' try { execSync(`${conf.glslangPath} --stdin -S ${ext.get(path.extname(docURI))}`, {input: lines.join('\n')}) @@ -241,8 +257,8 @@ function lint(docURI: string, lines: string[], includes: Map } if (!diagnostics.has(docURI)) diagnostics.set(docURI, []) - includes.forEach(obj => { - if (!diagnostics.has(obj.path)) diagnostics.set(obj.path, []) + includeGraph.nodes.forEach((node, key) => { + if (!diagnostics.has(key)) diagnostics.set(key, []) }) processErrors(out, docURI, diagnostics) @@ -280,11 +296,10 @@ function processErrors(out: string, docURI: string, diagnostics: Map, parentURI?: string) { includeGraph.get(parentURI || error.file).parents.forEach((pair, parURI) => { - console.log('parent', parURI, 'child', error.file) const diag: Diagnostic = { severity: error.type, - range: calcRange(pair.first, parURI), - message: `Line ${error.line} ${error.file.replace(conf.shaderpacksPath, '')} ${replaceWords(error.msg)}`, + range: calcRange(pair.first - 1, parURI), + message: `Line ${error.line} ${trimPath(error.file)} ${replaceWords(error.msg)}`, source: 'mc-glsl' } @@ -326,7 +341,7 @@ export function absPath(currFile: string, includeFile: string): string { // TODO add explanation comment if (includeFile.charAt(0) === '/') { - const shaderPath = currFile.replace(conf.shaderpacksPath, '').split('/').slice(0, 3).join('/') + const shaderPath = trimPath(currFile).split('/').slice(0, 3).join('/') return path.join(conf.shaderpacksPath, shaderPath, includeFile) } return path.join(path.dirname(currFile), includeFile) diff --git a/server/src/logging.ts b/server/src/logging.ts new file mode 100644 index 0000000..93971a8 --- /dev/null +++ b/server/src/logging.ts @@ -0,0 +1,6 @@ +import { CategoryServiceFactory, CategoryConfiguration, LogLevel, Category } from 'typescript-logging' + +CategoryServiceFactory.setDefaultConfiguration(new CategoryConfiguration(LogLevel.Debug)) + +export const linterLog = new Category('linter') +export const completionLog = new Category('completion') \ No newline at end of file diff --git a/server/src/server.ts b/server/src/server.ts index 2defb1e..d159511 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -32,6 +32,7 @@ documents.onDidOpen((event) => onEvent(event.document)) documents.onDidSave((event) => onEvent(event.document)) +// what am i saying here // dont do this for include files, for non-include files, clear diags for all its includes. Cache this maybe documents.onDidClose((event) => connection.sendDiagnostics({uri: event.document.uri, diagnostics: []})) @@ -44,6 +45,7 @@ export function onEvent(document: vsclangproto.TextDocument) { return } + // i think we still need to keep this in case we havent found the root of this files include tree if (!ext.has(extname(document.uri))) return try { @@ -59,8 +61,13 @@ function lintBubbleDown(uri: string, document: vsclangproto.TextDocument) { lintBubbleDown(parentURI, document) } else { const lines = getDocumentContents(parentURI).split('\n') + // feel like we could perhaps do better? Hope no one puts #version at the top of their includes.. if (lines.filter(l => reVersion.test(l)).length > 0) { - preprocess(lines, parentURI) + try { + preprocess(lines, parentURI) + } catch (e) { + postError(e) + } } } }) diff --git a/server/src/utils.ts b/server/src/utils.ts index 020770f..0180f05 100644 --- a/server/src/utils.ts +++ b/server/src/utils.ts @@ -1,5 +1,6 @@ import { connection, documents } from './server' import { readFileSync } from 'fs' +import { conf } from './config' export function postError(e: Error) { connection.window.showErrorMessage(e.message) @@ -11,4 +12,8 @@ export const formatURI = (uri: string) => uri.replace(/^file:\/\//, '').replace( export function getDocumentContents(uri: string): string { if (documents.keys().includes('file://' + uri)) return documents.get('file://' + uri).getText() else return readFileSync(uri).toString() +} + +export function trimPath(path: string): string { + return path.replace(conf.shaderpacksPath, '') } \ No newline at end of file diff --git a/tslint.json b/tslint.json index 2bd783f..80410c5 100644 --- a/tslint.json +++ b/tslint.json @@ -1,5 +1,5 @@ { - "defaultSeverity": "error", + "defaultSeverity": "warning", "extends": ["tslint:recommended"], "rules": { "quotemark": [true, "single"],