mirror of
https://github.com/godotengine/godot-vscode-plugin.git
synced 2025-12-31 13:48:24 +03:00
1. Add enum and match keywords highlight support
2. Shorthand if else support fix #17 3. Fix compains with used variables in array and dictionary fix #18 4. Fix error syntax check with keywords in strings fix #12 5. Add syntax check for end of expression
This commit is contained in:
@@ -20,7 +20,7 @@
|
||||
"name": "comment.line.number-sign.gdscript"
|
||||
},
|
||||
{
|
||||
"match": "\\b(?i:elif|else|for|if|while|break|continue|pass|and|in|is|not|or|return|onready|setget|breakpoint)\\b",
|
||||
"match": "\\b(?i:elif|else|for|if|while|break|continue|pass|and|in|is|not|or|return|onready|setget|enum|match|breakpoint)\\b",
|
||||
"name": "keyword.control.gdscript"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"name": "comment.line.number-sign.gdscript"
|
||||
},
|
||||
{
|
||||
"match": "\\b(?i:elif|else|for|if|while|break|continue|pass|and|in|is|not|or|return|onready|setget|breakpoint)\\b",
|
||||
"match": "\\b(?i:elif|else|for|if|while|break|continue|pass|and|in|is|not|or|return|onready|setget|enum|match|breakpoint)\\b",
|
||||
"name": "keyword.control.gdscript"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -4,50 +4,45 @@ import {DiagnosticCollection, DiagnosticSeverity} from 'vscode';
|
||||
import config from '../config';
|
||||
|
||||
interface GDParseError {
|
||||
message: string,
|
||||
column: number,
|
||||
row: number
|
||||
message : string,
|
||||
column : number,
|
||||
row : number
|
||||
}
|
||||
|
||||
interface GDScript {
|
||||
members: {
|
||||
members : {
|
||||
constants: {},
|
||||
functions: {},
|
||||
variables: {},
|
||||
signals: {}
|
||||
},
|
||||
base: string,
|
||||
errors: GDParseError[],
|
||||
valid: boolean,
|
||||
is_tool: boolean,
|
||||
native: string
|
||||
base : string,
|
||||
errors : GDParseError[],
|
||||
valid : boolean,
|
||||
is_tool : boolean,
|
||||
native : string
|
||||
}
|
||||
|
||||
interface ParseRequest {
|
||||
text: string,
|
||||
path: string
|
||||
text : string,
|
||||
path : string
|
||||
}
|
||||
|
||||
|
||||
|
||||
class GDScriptDiagnosticSeverity {
|
||||
private _subscription: DiagnosticCollection;
|
||||
|
||||
private _subscription : DiagnosticCollection;
|
||||
|
||||
constructor() {
|
||||
this._subscription = vscode.languages.createDiagnosticCollection("gdscript")
|
||||
}
|
||||
|
||||
dispose() {
|
||||
dispose() {
|
||||
this._subscription.dispose()
|
||||
}
|
||||
|
||||
async validateScript(doc: vscode.TextDocument, script: any) {
|
||||
if(doc.languageId == 'gdscript') {
|
||||
if(script) {
|
||||
let diagnostics = [
|
||||
...(this.validateExpression(doc)),
|
||||
...(this.validateUnusedSymbols(doc, script)),
|
||||
];
|
||||
async validateScript(doc : vscode.TextDocument, script : any) {
|
||||
if (doc.languageId == 'gdscript') {
|
||||
if (script) {
|
||||
let diagnostics = [ ...(this.validateExpression(doc)), ...(this.validateUnusedSymbols(doc, script)) ];
|
||||
this._subscription.set(doc.uri, diagnostics);
|
||||
return true;
|
||||
}
|
||||
@@ -55,91 +50,126 @@ class GDScriptDiagnosticSeverity {
|
||||
return false;
|
||||
}
|
||||
|
||||
private validateUnusedSymbols(doc: vscode.TextDocument,script) {
|
||||
private validateUnusedSymbols(doc : vscode.TextDocument, script) {
|
||||
let diagnostics = [];
|
||||
const text = doc.getText();
|
||||
|
||||
const check = (name:string, range: vscode.Range) => {
|
||||
var matchs = text.match(new RegExp(`[^\\w]\\s*${name}[^\\w]\\s*`, 'g'));
|
||||
let count = matchs?matchs.length:0;
|
||||
var incomment = text.match(new RegExp(`#.*?[^\\w]*${name}[^\\w]`, 'g'));
|
||||
count -= incomment?incomment.length:0;
|
||||
if(count <= 1)
|
||||
|
||||
const check = (name : string, range : vscode.Range) => {
|
||||
var matchs = text.match(new RegExp(`([^\\w]|\\[|\\{)\\s*${name}\\s*([^\\w]|\\[|\\{)`, 'g'));
|
||||
let count = matchs ? matchs.length : 0;
|
||||
var incomment = text.match(new RegExp(`#.*?([^\\w]|\\[|\\{)\\s*${name}\\s*([^\\w]|\\[|\\{)`, 'g'));
|
||||
count -= incomment ? incomment.length : 0;
|
||||
if (count <= 1)
|
||||
diagnostics.push(new vscode.Diagnostic(range, `${name} is never used.`, DiagnosticSeverity.Warning));
|
||||
};
|
||||
// Unused variables
|
||||
for (let key of Object.keys(script.variables))
|
||||
for (let key of Object.keys(script.variables))
|
||||
check(key, script.variables[key]);
|
||||
for (let key of Object.keys(script.constants))
|
||||
for (let key of Object.keys(script.constants))
|
||||
check(key, script.constants[key]);
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
private validateExpression(doc: vscode.TextDocument) {
|
||||
private validateExpression(doc : vscode.TextDocument) {
|
||||
let diagnostics = [];
|
||||
let expectEndOfLine = false;
|
||||
const text = doc.getText();
|
||||
const lines = text.split(/\r?\n/);
|
||||
lines.map((line:string, i: number) =>{
|
||||
lines.map((line : string, i : number) => {
|
||||
let matchstart = /[^\s]+.*/.exec(line);
|
||||
let curLineStartAt = 0;
|
||||
if(matchstart)
|
||||
if (matchstart)
|
||||
curLineStartAt = matchstart.index;
|
||||
|
||||
// ignore comments
|
||||
if(line.match(/^\s*#.*/) || line.match(/^#.*/)) return
|
||||
if (line.match(/^\s*#.*/) || line.match(/^#.*/))
|
||||
return
|
||||
// normalize line content
|
||||
line = "\t" + line + "\t";
|
||||
var range = new vscode.Range(i, curLineStartAt, i, line.length);
|
||||
|
||||
if(line.match(/[^#].*?\;/) && !line.match(/[#].*?\;/)) {
|
||||
if (line.match(/[^#].*?\;/) && !line.match(/[#].*?\;/)) {
|
||||
const semicolonIndex = line.indexOf(';');
|
||||
diagnostics.push(new vscode.Diagnostic(new vscode.Range(i, semicolonIndex, i, semicolonIndex+1), "Statement contains a semicolon.", DiagnosticSeverity.Warning));
|
||||
} if (line.match(/[^#].*?/) && expectEndOfLine){
|
||||
if(!line.match(/.*?(\\|\:)/)){
|
||||
diagnostics.push(new vscode.Diagnostic(new vscode.Range(i, semicolonIndex, i, semicolonIndex + 1), "Statement contains a semicolon.", DiagnosticSeverity.Warning));
|
||||
}
|
||||
if (line.match(/[^#].*?/) && expectEndOfLine) {
|
||||
if (!line.match(/.*?(\\|\:)/)) {
|
||||
diagnostics.push(new vscode.Diagnostic(range, "': or \\' expected at end of the line.", DiagnosticSeverity.Error));
|
||||
expectEndOfLine = false;
|
||||
}
|
||||
if(line.match(/.*?\:/))
|
||||
if (line.match(/.*?\:/))
|
||||
expectEndOfLine = false;
|
||||
}
|
||||
if(line.match(/[^\w](if|elif|else|for|while|func|class)[^\w].*?/) && !line.match(/#.*?[^\w](if|elif|else|for|while|func|class)[^\w].*?/)) {
|
||||
if(line.match(/(if|elif|else|for|while|func|class).*?\\/))
|
||||
expectEndOfLine = true;
|
||||
if(line.match(/(if|elif|else|for|while|func|class).*?/) && !line.match(/.*?(\\|\:)/))
|
||||
diagnostics.push(new vscode.Diagnostic(range, "': or \\' expected at end of the line.", DiagnosticSeverity.Error));
|
||||
else if(line.match(/(if|elif|while|func|class)\s*\:/))
|
||||
diagnostics.push(new vscode.Diagnostic(range, "Indentifier expected before ':'", DiagnosticSeverity.Error));
|
||||
else if(line.match(/[^\w]for[^\w]/) && !line.match(/\s+for\s\w+\s+in\s+[\w+]|\{.*?\}|\[.*?\]/))
|
||||
diagnostics.push(new vscode.Diagnostic(range, "Invalid for expression", DiagnosticSeverity.Error));
|
||||
else if(line.match(/(if|elif|while)\s*\(.*\)/))
|
||||
diagnostics.push(new vscode.Diagnostic(range, "Extra brackets in condition expression.", DiagnosticSeverity.Warning));
|
||||
if(line.match(/([^\w]if|elif|else|for|while|func|class[^\w]).*\:[ \t]+[^#\s]+/))
|
||||
const colonKeywords = /\b(if|elif|else|for|while|func|class|match)\b/;
|
||||
let keywords = line.match(colonKeywords)
|
||||
if (keywords) {
|
||||
if(line.match(new RegExp(`".*?\\s${keywords[1]}\\s.*?"`)) || line.match(new RegExp(`'.*?\\s${keywords[1]}\\s.*?\'`)))
|
||||
return
|
||||
else if( i < lines.length-1) {
|
||||
let next = i+1;
|
||||
let nextline = lines[next];
|
||||
// changes nextline until finds a line containg text or comes to the last line
|
||||
while ((!nextline || nextline.match(/\s+$/)) && next < lines.length-1) {
|
||||
++next;
|
||||
nextline = lines[next];
|
||||
}
|
||||
let nextLineStartAt = -1;
|
||||
let match = /[^\s]+.*/.exec(nextline);
|
||||
if(match)
|
||||
nextLineStartAt = match.index;
|
||||
|
||||
if(nextLineStartAt <= curLineStartAt)
|
||||
diagnostics.push(new vscode.Diagnostic(range, "Expected indented block after expression", DiagnosticSeverity.Error));
|
||||
if(line.match(/.*?\sif\s+\w.*?\s+else\s+\w.*/))
|
||||
return
|
||||
if (line.match(/.*?\\/))
|
||||
expectEndOfLine = true;
|
||||
else if (line.match(/.*?\:[\s+]+[^#\s]+/))
|
||||
return
|
||||
else if (!line.match(/.*?(\\|\:)/))
|
||||
diagnostics.push(new vscode.Diagnostic(range, "': or \\' expected at end of the line.", DiagnosticSeverity.Error));
|
||||
else if (line.match(/(if|elif|while|func|class|match)\s*\:/))
|
||||
diagnostics.push(new vscode.Diagnostic(range, "Indentifier expected before ':'", DiagnosticSeverity.Error));
|
||||
else if (line.match(/[^\w]for[^\w]/) && !line.match(/\s+for\s\w+\s+in\s+[\w+]|\{.*?\}|\[.*?\]/)){
|
||||
if(!(line.match(/".*?for.*?"/) || line.match(/'.*?for.*?'/)))
|
||||
diagnostics.push(new vscode.Diagnostic(range, "Invalid for expression", DiagnosticSeverity.Error));
|
||||
}
|
||||
else
|
||||
diagnostics.push(new vscode.Diagnostic(range, "Expected indented block after expression", DiagnosticSeverity.Error));
|
||||
else if (line.match(/(if|elif|while|match)\s*\(.*\)/))
|
||||
diagnostics.push(new vscode.Diagnostic(range, "Extra brackets in condition expression.", DiagnosticSeverity.Warning));
|
||||
const blockIndetCheck = function() {
|
||||
const err = new vscode.Diagnostic(range, "Expected indented block after expression", DiagnosticSeverity.Error);
|
||||
if (i < lines.length - 1) {
|
||||
let next = i + 1;
|
||||
let nextline = lines[next];
|
||||
// changes nextline until finds a line containg text or comes to the last line
|
||||
while (((!nextline || nextline.match(/\s+$/)) || nextline.match(/\s*#/)) && next < lines.length - 1) {
|
||||
++next;
|
||||
nextline = lines[next];
|
||||
}
|
||||
let nextLineStartAt = -1;
|
||||
let match = /[^\s]+.*/.exec(nextline);
|
||||
if (match)
|
||||
nextLineStartAt = match.index;
|
||||
|
||||
if (nextLineStartAt <= curLineStartAt)
|
||||
diagnostics.push(err);
|
||||
}
|
||||
else if(line.match(/\:\s*$/))
|
||||
diagnostics.push(err);
|
||||
};
|
||||
if(!expectEndOfLine)
|
||||
blockIndetCheck();
|
||||
}
|
||||
if(line.match(/(if|elif|while|return)\s+\w+\s*=\s*\w+/))
|
||||
diagnostics.push(new vscode.Diagnostic(range, "Assignment in condition or return expressions", DiagnosticSeverity.Warning));
|
||||
else if (line.indexOf("==") > 0 ) {
|
||||
if(!line.match(colonKeywords) && line.match(/\:\s*$/)) {
|
||||
let showErr = true;
|
||||
if( i >= 1 ) {
|
||||
let previous = i - 1;
|
||||
let previousline = lines[previous];
|
||||
while(previousline.match(/\\\s*$/) && previous>=1) {
|
||||
--previous;
|
||||
const ppreviousline = lines[previous];
|
||||
if(ppreviousline.match(/\\\s*$/))
|
||||
previousline = ppreviousline;
|
||||
}
|
||||
const keywords = previousline.match(colonKeywords);
|
||||
if(keywords && !(previousline.match(new RegExp(`".*?\\s${keywords[1]}\\s.*?"`)) || previousline.match(new RegExp(`'.*?\\s${keywords[1]}\\s.*?'`)) ))
|
||||
showErr = false
|
||||
|
||||
}
|
||||
if(showErr)
|
||||
diagnostics.push(new vscode.Diagnostic(range, "Expected end of statement after expression", DiagnosticSeverity.Error));
|
||||
}
|
||||
if (line.match(/(if|elif|while|return)\s+\w+\s*=\s*\w+/))
|
||||
diagnostics.push(new vscode.Diagnostic(range, "Assignment in condition or return expressions", DiagnosticSeverity.Warning));
|
||||
else if (line.indexOf("==") > 0 && !line.match(/\:\s*/)) {
|
||||
const endAt = line.indexOf("==");
|
||||
const precontent = line.substring(0, endAt);
|
||||
if(!precontent.match(/\s(if|elif|while|return)\s/) && !precontent.match(/=[^=]/)) {
|
||||
if (!precontent.match(/\s(if|elif|while|return)\s/) && !precontent.match(/=[^=]/) && !expectEndOfLine) {
|
||||
diagnostics.push(new vscode.Diagnostic(range, "Unhandled comparation expression contains", DiagnosticSeverity.Warning));
|
||||
}
|
||||
}
|
||||
@@ -150,7 +180,7 @@ class GDScriptDiagnosticSeverity {
|
||||
});
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export default GDScriptDiagnosticSeverity;
|
||||
|
||||
Reference in New Issue
Block a user