diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 1b5c34b..5e7f981 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -46,8 +46,8 @@ "script": "watch:client", "isBackground": true, "presentation": { - "echo": true, - "reveal": "always", + "echo": false, + "reveal": "never", "focus": false, "panel": "shared" }, @@ -60,8 +60,8 @@ "script": "watch:server", "isBackground": true, "presentation": { - "echo": true, - "reveal": "always", + "echo": false, + "reveal": "never", "focus": false, "panel": "shared" }, @@ -73,8 +73,8 @@ "label": "watch", "isBackground": true, "presentation": { - "echo": true, - "reveal": "always", + "echo": false, + "reveal": "never", "focus": false, "panel": "shared" }, diff --git a/CHANGELOG.md b/CHANGELOG.md index 2074a5c..aabf1ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) ### Added -- Deletion of temp files when VSCode closed. +- Incomplete completion items +- Support for #include directives - Basic linting with highlighting. - Support for .fsh, .vsh, .glsl and .gsh files. \ No newline at end of file diff --git a/README.md b/README.md index ce16861..c6ab860 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ VSCode-MC-Shader is a [Visual Studio Code](https://code.visualstudio.com/) exten - Linting (unpolished) - Syntax highlighting (by extension dependency) -- Support for `#includes` (WIP) +- Support for `#includes` directives - Auto-complete prompts (incomplete) ## Planned diff --git a/client/src/config.ts b/client/src/config.ts deleted file mode 100644 index 7e28667..0000000 --- a/client/src/config.ts +++ /dev/null @@ -1,39 +0,0 @@ -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 59cccc1..0b4313a 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -1,7 +1,6 @@ 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')) @@ -20,17 +19,12 @@ 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 langServer = new vscodeLang.LanguageClient('vscode-mc-shader', serverOpts, clientOpts) - configChangeHandler(langServer) - vscode.workspace.onDidChangeConfiguration((e) => { - configChangeHandler(langServer, e) - }) - context.subscriptions.push(langServer.start()) } \ No newline at end of file diff --git a/package.json b/package.json index 46a8a7a..915860c 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "mcglsl.lintOnType": { "type": "boolean", "default": false, - "description": "Whether or not to lint while typing. Can decrease performance." + "description": "Whether or not to lint while typing. May decrease performance." }, "mcglsl.shaderpacksPath": { "type": "string", diff --git a/server/package.json b/server/package.json index fe060b7..7bb32b0 100644 --- a/server/package.json +++ b/server/package.json @@ -1,7 +1,7 @@ { "name": "vscode-mc-shader-server", - "description": "A Visual Studio Code extension for linting/etc Minecraft GLSL Shaders", - "version": "0.0.1", + "description": "A Language Server for linting/etc Minecraft GLSL Shaders", + "version": "0.8.0", "author": "Noah Santschi-Cooney (Strum355)", "license": "MIT", "repository": { diff --git a/server/src/linter.ts b/server/src/linter.ts index d902348..a7dea21 100644 --- a/server/src/linter.ts +++ b/server/src/linter.ts @@ -1,5 +1,5 @@ -import { conf, connection, documents } from './server' import { TextDocument, Diagnostic, DiagnosticSeverity, Range } from 'vscode-languageserver' +import { conf, connection, documents } from './server' import { execSync } from 'child_process' import * as path from 'path' import { readFileSync } from 'fs' @@ -7,6 +7,7 @@ import { readFileSync } from 'fs' const reDiag = /^(ERROR|WARNING): ([^?<>:*|"]+?):(\d+): (?:'.*?' : )?(.+)$/ const reVersion = /#version [\d]{3}/ const reInclude = /^(?:\s)*?(?:#include) "((?:\/?[^?<>:*|"]+?)+?\.(?:[a-zA-Z]+?))"$/ +const reIncludeExt = /#extension GL_GOOGLE_include_directive ?: ?require/ const include = '#extension GL_GOOGLE_include_directive : require' const filters = [ @@ -64,20 +65,17 @@ enum Comment { } 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 + if (lines.find((value: string, i: number, obj: string[]): boolean => reIncludeExt.test(value)) == undefined) { + 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 + } } } @@ -104,6 +102,7 @@ function processIncludes(lines: string[], incStack: string[]) { function mergeInclude(inc: IncludeObj, lines: string[], incStack: string[]) { const incPath = absPath(inc.parent, inc.match[1]) + if (!incPath) return false const dataLines = readFileSync(incPath).toString().split('\n') incStack.push(incPath) @@ -121,39 +120,40 @@ export const formatURI = (uri: string) => uri.replace(/^file:\/\//, '') export function getIncludes(uri: string, lines: string[]) { const out: IncludeObj[] = [] - const count = [0] // for each file we need to track the line number + const count = [-1] // for each file we need to track the line number const parStack = [uri] // for each include we need to track its parent - let total = 0 // current line number overall + let total = -1 // 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() - } else { - count.push(0) - parStack.push(inc) - } - } else if (match) { - out.push({ - lineNum: count[count.length - 1], - lineNumParent: total, - parent: parStack[parStack.length - 1], - match - }) - } - } - count[count.length - 1]++ total++ + if (comment) return + if (line.startsWith('#line')) { + const inc = line.slice(line.indexOf('"') + 1, line.lastIndexOf('"')) + + if (inc === parStack[parStack.length - 2]) { + count.pop() + parStack.pop() + } else { + count.push(-1) + parStack.push(inc) + } + return + } + + const match = line.match(reInclude) + + if (match) { + out.push({ + lineNum: count[count.length - 1], + lineNumParent: total, + parent: parStack[parStack.length - 1], + match + }) + } }) return out } @@ -167,8 +167,8 @@ export function isInComment(line: string, state: Comment): Comment { } export function absPath(currFile: string, includeFile: string): string { - if (!currFile.startsWith(conf.shaderpacksPath)) { - connection.window.showErrorMessage(`Shaderpacks path may not be correct. Current file is in ${currFile} but the path is set to ${conf.shaderpacksPath}`) + if (!currFile.startsWith(conf.shaderpacksPath) || conf.shaderpacksPath === '') { + connection.window.showErrorMessage(`Shaderpacks path may not be correct. Current file is in '${currFile}' but the path is set to '${conf.shaderpacksPath}'`) return } @@ -181,7 +181,7 @@ export function absPath(currFile: string, includeFile: string): string { } function lint(uri: string, lines: string[], includes: string[]) { - //console.log(lines.join('\n')) + console.log(lines.join('\n')) //return let out: string = '' try { @@ -211,7 +211,7 @@ function lint(uri: string, lines: string[], includes: string[]) { }) } -export const replaceWord = (msg: string) => Object.entries(tokens).reduce((acc, [key, value]) => acc.replace(key, value), msg) +export const replaceWord = (msg: string) => Array.from(tokens.entries()).reduce((acc, [key, value]) => acc.replace(key, value), msg) const daigsArray = (diags: Map) => Array.from(diags).map(kv => ({uri: 'file://' + kv[0], diag: kv[1]})) diff --git a/server/src/server.ts b/server/src/server.ts index 10ddd2f..1b70a79 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -1,5 +1,5 @@ import * as vsclang from 'vscode-languageserver' -import { TextDocumentChangeEvent, TextDocument } from 'vscode-languageserver-protocol' +import * as vsclangproto from 'vscode-languageserver-protocol' import { Config } from './config' import { completions } from './completionProvider' import { preprocess, ext, formatURI } from './linter' @@ -32,12 +32,16 @@ documents.onDidSave((event) => onEvent(event.document)) //documents.onDidChangeContent(onEvent) -function onEvent(document: TextDocument) { +function onEvent(document: vsclangproto.TextDocument) { if (!ext.has(extname(document.uri))) return - preprocess(document.getText().split('\n'), formatURI(document.uri)) + try { + preprocess(document.getText().split('\n'), formatURI(document.uri)) + } catch (e) { + connection.window.showErrorMessage(`[mc-glsl] ${e.message}`) + } } -connection.onDidChangeConfiguration((change) => { +connection.onDidChangeConfiguration(async (change) => { const temp = change.settings.mcglsl as Config conf = new Config(temp['shaderpacksPath'], temp['glslangValidatorPath']) try { @@ -45,7 +49,12 @@ connection.onDidChangeConfiguration((change) => { documents.all().forEach(document => onEvent) } catch (e) { if (e.status !== 1) { - connection.window.showErrorMessage(`[mc-glsl] glslangValidator not found at: '${conf.glslangPath}' or returned non-0 code`) + const chosen = await connection.window.showErrorMessage( + `[mc-glsl] glslangValidator not found at: '${conf.glslangPath}' or returned non-0 code`, + {title: 'Download'}, + {title: 'Cancel'} + ) + console.log(chosen.title) } } })