From cacfbe7a4e1783ab36787903ec94a3a011963530 Mon Sep 17 00:00:00 2001 From: Noah Santschi-Cooney Date: Fri, 13 Jul 2018 18:16:50 +0100 Subject: [PATCH] Doing more on the extension side and splitting up the lang server linting more --- .vscode/tasks.json | 19 ++++++++- client/src/config.ts | 39 +++++++++++++++++++ client/src/extension.ts | 12 ++++-- server/package.json | 2 +- server/src/linter.ts | 86 +++++++++++++++++++++++++---------------- server/src/server.ts | 2 +- 6 files changed, 120 insertions(+), 40 deletions(-) create mode 100644 client/src/config.ts diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 9c9d2fa..1b5c34b 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -2,7 +2,24 @@ // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "2.0.0", - "tasks": [ + "tasks": [ + { + "type": "npm", + "script": "fix", + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "echo": false, + "reveal": "silent", + "focus": false, + "panel": "shared" + }, + "problemMatcher": [ + "$tslint5" + ] + }, { "type": "npm", "script": "compile", diff --git a/client/src/config.ts b/client/src/config.ts new file mode 100644 index 0000000..7e28667 --- /dev/null +++ b/client/src/config.ts @@ -0,0 +1,39 @@ +import * as vscode from 'vscode' +import { execSync } from 'child_process' +import * as vscodeLang from 'vscode-languageclient' + +export class Config { + public readonly shaderpacksPath: string + public readonly glslangPath: string + + constructor(shaderpacksPath: string, glslangPath: string) { + this.shaderpacksPath = shaderpacksPath + this.glslangPath = glslangPath || 'glslangValidator' + } +} + +let conf = new Config('', '') + +export async function configChangeHandler(langServer: vscodeLang.LanguageClient, event?: vscode.ConfigurationChangeEvent) { + if (event && !event.affectsConfiguration('mcglsl')) return + + const temp = vscode.workspace.getConfiguration('mcglsl') + conf = new Config(temp.get('shaderpacksPath'), temp.get('glslangValidatorPath')) + + try { + execSync(conf.glslangPath) + langServer.sendNotification(vscodeLang.DidChangeConfigurationNotification.type) + } catch (e) { + if (e.status !== 1) { + const selected = await vscode.window.showErrorMessage( + `[mc-glsl] glslangValidator not found at: '${conf.glslangPath}' or returned non-0 code`, + 'Download', + 'Cancel' + ) + + if (selected === 'Download') { + //TODO can i use the python script? + } + } + } +} \ No newline at end of file diff --git a/client/src/extension.ts b/client/src/extension.ts index 8996c20..59cccc1 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -1,6 +1,7 @@ import * as vscode from 'vscode' import * as vscodeLang from 'vscode-languageclient' import * as path from 'path' +import { Config, configChangeHandler } from './config' export function activate(context: vscode.ExtensionContext) { const serverModule = context.asAbsolutePath(path.join('server', 'out', 'server.js')) @@ -19,12 +20,17 @@ export function activate(context: vscode.ExtensionContext) { const clientOpts: vscodeLang.LanguageClientOptions = { documentSelector: [{scheme: 'file', language: 'glsl'}], synchronize: { - configurationSection: 'mcglsl', + //configurationSection: 'mcglsl', fileEvents: vscode.workspace.createFileSystemWatcher('**/*.{fsh,gsh,vsh,glsl}') } } - const disposable = new vscodeLang.LanguageClient('vscode-mc-shader', serverOpts, clientOpts) + const langServer = new vscodeLang.LanguageClient('vscode-mc-shader', serverOpts, clientOpts) - context.subscriptions.push(disposable.start()) + configChangeHandler(langServer) + vscode.workspace.onDidChangeConfiguration((e) => { + configChangeHandler(langServer, e) + }) + + context.subscriptions.push(langServer.start()) } \ No newline at end of file diff --git a/server/package.json b/server/package.json index 54072bc..fe060b7 100644 --- a/server/package.json +++ b/server/package.json @@ -19,5 +19,5 @@ "compile": "installServerIntoExtension ../client ./package.json ./tsconfig.json && tsc -p .", "watch": "installServerIntoExtension ../client ./package.json ./tsconfig.json && tsc -w -p .", "lint": "tslint -c ../tslint.json 'src/**/*.ts'" - } + } } diff --git a/server/src/linter.ts b/server/src/linter.ts index a2cdf07..19f98f0 100644 --- a/server/src/linter.ts +++ b/server/src/linter.ts @@ -56,42 +56,26 @@ enum Comment { Multi } -// TODO exclude exts not in ext -export function preprocess(lines: string[], docURI: string, topLevel: boolean, incStack: string[], num: number) { - if (topLevel) { - let inComment = false - for (let i = 0; i < lines.length; i++) { - const line = lines[i] - //TODO something better than this - if (line.includes('/*')) inComment = true - if (line.includes('*/')) inComment = false - if (line.trim().startsWith('//')) continue - if (!inComment && reVersion.test(line)) { - lines.splice(i + 1, 0, include) - break - } - if (i === lines.length - 1) { - lines.splice(0, 0, include) - break - } +export function preprocess(lines: string[], docURI: string) { + let inComment = false + for (let i = 0; i < lines.length; i++) { + const line = lines[i] + //TODO something better than this + if (line.includes('/*')) inComment = true + if (line.includes('*/')) inComment = false + if (line.trim().startsWith('//')) continue + if (!inComment && reVersion.test(line)) { + lines.splice(i + 1, 0, include) + break + } + if (i === lines.length - 1) { + lines.splice(0, 0, include) + break } } - const includes = getIncludes(incStack[0], lines) - if (includes.length > 0) { - includes.reverse().forEach(inc => { - const incPath = absPath(inc.parent, inc.match[1]) - incStack.push(incPath) - const dataLines = readFileSync(incPath).toString().split('\n') - lines[inc.lineNumParent] = `#line 0 "${incPath}"` - lines.splice(inc.lineNumParent + 1, 0, ...dataLines) - lines.splice(inc.lineNumParent + 1 + dataLines.length, 0, `#line ${inc.lineNum} "${inc.parent}"`) - - }) - preprocess(lines, docURI, false, incStack, num) - } - - if (!topLevel) return + const incStack = [docURI] + processIncludes(lines, incStack) try { lint(docURI, lines, incStack) @@ -100,21 +84,50 @@ export function preprocess(lines: string[], docURI: string, topLevel: boolean, i } } +function processIncludes(lines: string[], incStack: string[]) { + const includes = getIncludes(incStack[0], lines) + if (includes.length > 0) { + includes.reverse().forEach(inc => { + mergeInclude(inc, lines, incStack) + }) + // recursively check for more includes to be merged + processIncludes(lines, incStack) + } +} + +function mergeInclude(inc: {lineNum: number, lineNumParent: number, parent: string, match: RegExpMatchArray}, lines: string[], incStack: string[]) { + const incPath = absPath(inc.parent, inc.match[1]) + const dataLines = readFileSync(incPath).toString().split('\n') + incStack.push(incPath) + + // add #line indicating we are entering a new include block + lines[inc.lineNumParent] = `#line 0 "${incPath}"` + // merge the lines of the file into the current document + lines.splice(inc.lineNumParent + 1, 0, ...dataLines) + // add the closing #line indicating we're re-entering a block a level up + lines.splice(inc.lineNumParent + 1 + dataLines.length, 0, `#line ${inc.lineNum} "${inc.parent}"`) +} + export const formatURI = (uri: string) => uri.replace(/^file:\/\//, '') // TODO no export function getIncludes(uri: string, lines: string[]) { const out: {lineNum: number, lineNumParent: number, parent: string, match: RegExpMatchArray}[] = [] + const count = [0] // for each file we need to track the line number - let total = 0 const parStack = [uri] // for each include we need to track its parent + + let total = 0 // current line number overall let comment = Comment.No + lines.forEach(line => { comment = isInComment(line, comment) if (!comment) { const match = line.match(reInclude) + if (line.startsWith('#line')) { const inc = line.slice(line.indexOf('"') + 1, line.lastIndexOf('"')) + if (inc === parStack[parStack.length - 2]) { count.pop() parStack.pop() @@ -131,6 +144,7 @@ export function getIncludes(uri: string, lines: string[]) { }) } } + count[count.length - 1]++ total++ }) @@ -175,12 +189,14 @@ function lint(uri: string, lines: string[], includes: string[]) { const matches = filterMatches(out) matches.forEach((match) => { const [whole, type, file, line, msg] = match + const diag: Diagnostic = { severity: type === 'ERROR' ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning, range: calcRange(parseInt(line) - 1, file.length - 1 ? file : uri), message: replaceWord(msg), source: 'mc-glsl' } + diagnostics.get(file.length - 1 ? file : uri).push(diag) }) @@ -201,12 +217,14 @@ const filterMatches = (output: string) => output 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 line = lines[lineNum] const startOfLine = line.length - line.trimLeft().length const endOfLine = line.slice(0, line.indexOf('//')).trimRight().length + 1 diff --git a/server/src/server.ts b/server/src/server.ts index 5fd22f5..10ddd2f 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -34,7 +34,7 @@ documents.onDidSave((event) => onEvent(event.document)) function onEvent(document: TextDocument) { if (!ext.has(extname(document.uri))) return - preprocess(document.getText().split('\n'), formatURI(document.uri), true, [document.uri.replace(/^file:\/\//, '')], 0) + preprocess(document.getText().split('\n'), formatURI(document.uri)) } connection.onDidChangeConfiguration((change) => {