Doing more on the extension side and splitting up the lang server linting more

This commit is contained in:
Noah Santschi-Cooney 2018-07-13 18:16:50 +01:00
parent d7d41985ef
commit cacfbe7a4e
6 changed files with 120 additions and 40 deletions

17
.vscode/tasks.json vendored
View file

@ -3,6 +3,23 @@
// for the documentation about the tasks.json format
"version": "2.0.0",
"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",

39
client/src/config.ts Normal file
View file

@ -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?
}
}
}
}

View file

@ -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())
}

View file

@ -56,9 +56,7 @@ enum Comment {
Multi
}
// TODO exclude exts not in ext
export function preprocess(lines: string[], docURI: string, topLevel: boolean, incStack: string[], num: number) {
if (topLevel) {
export function preprocess(lines: string[], docURI: string) {
let inComment = false
for (let i = 0; i < lines.length; i++) {
const line = lines[i]
@ -75,23 +73,9 @@ export function preprocess(lines: string[], docURI: string, topLevel: boolean, i
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

View file

@ -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) => {