mxd 3 жил өмнө
parent
commit
ab978e977b

+ 80 - 84
magic-editor/src/console/src/scripts/editor/completion.js

@@ -93,6 +93,67 @@ const completionFunction = (suggestions, input) => {
     }
 };
 
+const completionMethod = async (className, suggestions) => {
+    let clazz = await JavaClass.loadClass(className)
+    let index = className.lastIndexOf('.')
+    let simpleName = index > 0 ? className.substring(index  + 1) : className
+    let enums = JavaClass.findEnums(clazz);
+    if (enums) {
+        for (let j = 0; j < enums.length; j++) {
+            let value = enums[j];
+            suggestions.push({
+                label: value,
+                kind: monaco.languages.CompletionItemKind.Enum,
+                detail: value + ":" + value,
+                insertText: value,
+                sortText: ' ~~~' + value
+            })
+        }
+    }
+    let attributes = JavaClass.findAttributes(clazz);
+    if (attributes) {
+        for (let j = 0; j < attributes.length; j++) {
+            let attribute = attributes[j];
+            suggestions.push({
+                label: attribute.name,
+                kind: monaco.languages.CompletionItemKind.Field,
+                detail: attribute.comment || (attribute.type + ":" + attribute.name),
+                insertText: attribute.name,
+                sortText: ' ~~' + attribute.name
+            })
+        }
+    }
+    let methods = JavaClass.findMethods(clazz);
+    if (methods) {
+        let mmap = {};
+        for (let j = 0; j < methods.length; j++) {
+            let method = methods[j];
+            if (mmap[method.signature]) {
+                continue;
+            }
+            mmap[method.signature] = true;
+            let document = [];
+            for (let j = (method.extension ? 1 : 0); j < method.parameters.length; j++) {
+                let param = method.parameters[j];
+                document.push('`' + param.name + '` ' + (param.comment || param.type));
+                document.push('\r\n')
+            }
+            method.comment && document.push('\r\n') && document.push(method.comment)
+            suggestions.push({
+                sortText: method.sortText || method.fullName,
+                label: method.fullName,
+                kind: monaco.languages.CompletionItemKind.Method,
+                detail: `${simpleName}.${method.fullName}: ${method.returnType}`,
+                documentation: {
+                    value: document.join('\r\n')
+                },
+                insertText: method.insertText,
+                insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet
+            })
+        }
+    }
+}
+
 async function completionScript(suggestions, input) {
     try {
         let tokens = tokenizer(input);
@@ -100,82 +161,19 @@ async function completionScript(suggestions, input) {
         if (tokenLen === 0) {
             return;
         }
-        let tokenType = tokens[tokenLen - 1].getTokenType();
-        if (tokenType === TokenType.Identifier) {
-            if (tokenLen === 1) {
-                completionFunction(suggestions, input);
-                return;
-            }
-            tokenType = tokens[tokenLen - 2].getTokenType();
-            tokens.pop();
-        }
-        if (tokenType === TokenType.Period) {
-            tokens.pop();
-        } else {
-            completionFunction(suggestions, input);
-            return;
-        }
         let parser = new Parser(new TokenStream(tokens));
-        let clazz = await parser.completion(RequestParameter.environmentFunction());
-        if (clazz) {
-            let enums = JavaClass.findEnums(clazz);
-            if (enums) {
-                for (let j = 0; j < enums.length; j++) {
-                    let value = enums[j];
-                    suggestions.push({
-                        label: value,
-                        kind: monaco.languages.CompletionItemKind.Enum,
-                        detail: value + ":" + value,
-                        insertText: value,
-                        sortText: ' ~~~' + value
-                    })
-                }
-            }
-            let attributes = JavaClass.findAttributes(clazz);
-            if (attributes) {
-                for (let j = 0; j < attributes.length; j++) {
-                    let attribute = attributes[j];
-                    suggestions.push({
-                        label: attribute.name,
-                        kind: monaco.languages.CompletionItemKind.Field,
-                        detail: attribute.comment || (attribute.type + ":" + attribute.name),
-                        insertText: attribute.name,
-                        sortText: ' ~~' + attribute.name
-                    })
-                }
-            }
-            let methods = JavaClass.findMethods(clazz);
-            if (methods) {
-                let mmap = {};
-                for (let j = 0; j < methods.length; j++) {
-                    let method = methods[j];
-                    if (mmap[method.signature]) {
-                        continue;
-                    }
-                    mmap[method.signature] = true;
-                    let document = [];
-                    for (let j = (method.extension ? 1 : 0); j < method.parameters.length; j++) {
-                        let param = method.parameters[j];
-                        document.push('- ' + param.name + ':' + (param.comment || param.type));
-                        document.push('---')
-                    }
-                    document.push(`- 返回值:\`${method.returnType}\``)
-                    suggestions.push({
-                        sortText: method.sortText || method.fullName,
-                        label: method.fullName,
-                        kind: monaco.languages.CompletionItemKind.Method,
-                        detail: method.comment,
-                        documentation: {
-                            value: document.join('\r\n')
-                        },
-                        insertText: method.insertText,
-                        insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet
-                    })
-                }
+        const { best, env } = await parser.parseBest(input.length - 1, env);
+        if(input.endsWith(".")){
+            await completionMethod(await best.getJavaType(env), suggestions)
+        } else {
+            let astName = best.constructor.name;
+            if (astName === 'MemberAccess' || astName === 'MethodCall') {
+                await completionMethod(await best.target.getJavaType(env), suggestions)
             }
         }
+        return suggestions;
     } catch (e) {
-        // console.log(e);
+          console.error(e);
     }
 }
 
@@ -197,18 +195,16 @@ const CompletionItemProvider = {
         let importIndex;
         if (line.length > 1 && (importIndex = line.trim().indexOf('import')) === 0) {
             completionImport(suggestions, position, line, importIndex)
-        } else if (line.endsWith(":")) {
-            if (line.endsWith("::")) {
-                suggestions = ['int', 'long', 'date', 'string', 'short', 'byte', 'float', 'double', 'json','stringify', 'sql'].map(it => {
-                    return {
-                        label: it,
-                        detail: `转换为${it === 'stringify' ? 'json字符串': it === 'sql' ? 'sql参数类型': it}`,
-                        insertText: it,
-                        kind: monaco.languages.CompletionItemKind.TypeParameter,
-                        insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet
-                    }
-                })
-            }
+        } else if (line.endsWith("::")) {
+            suggestions = ['int', 'long', 'date', 'string', 'short', 'byte', 'float', 'double', 'json','stringify', 'sql'].map(it => {
+                return {
+                    label: it,
+                    detail: `转换为${it === 'stringify' ? 'json字符串': it === 'sql' ? 'sql参数类型': it}`,
+                    insertText: it,
+                    kind: monaco.languages.CompletionItemKind.TypeParameter,
+                    insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet
+                }
+            })
         } else if (value.length > 1) {
             await completionScript(suggestions, value)
         }

+ 44 - 38
magic-editor/src/console/src/scripts/editor/hover.js

@@ -1,6 +1,7 @@
 import tokenizer from '@/scripts/parsing/tokenizer.js'
-import {TokenStream, TokenType} from '../parsing/index.js'
+import { TokenStream } from '../parsing/index.js'
 import {
+    ClassConverter,
     FunctionCall,
     LinqSelect,
     MapOrArrayAccess,
@@ -9,10 +10,9 @@ import {
     VarDefine,
     VariableAccess
 } from '../parsing/ast.js'
-import {keywords, Parser} from '@/scripts/parsing/parser.js'
-import {Range} from 'monaco-editor'
+import { Parser } from '@/scripts/parsing/parser.js'
+import { Range } from 'monaco-editor'
 import JavaClass from "./java-class"
-import RequestParameter from './request-parameter.js';
 
 const findBestMatch = (node, row, col) => {
     let expressions = node.expressions().filter(it => it);
@@ -31,50 +31,51 @@ const findBestMatch = (node, row, col) => {
     }
     return null;
 }
-const generateMethodDocument = (method, contents) => {
-    contents.push({value: `${method.fullName}`})
+const generateMethodDocument = (prefix,method, contents) => {
+    contents.push({value: `${prefix}${method.fullName}`})
     contents.push({value: `${method.comment}`})
     method.parameters.forEach((param, pIndex) => {
         if (pIndex > 0 || !method.extension) {
             contents.push({value: `${param.name}:${(param.comment || param.type)}`})
         }
     })
-    contents.push({value: `返回类型:${method.returnType}`})
+    contents.push({value: `返回类型:\`${method.returnType}\``})
 }
 const HoverProvider = {
     provideHover: async (model, position) => {
         let value = model.getValue()
         let tokens = tokenizer(value);
-        let row = position.lineNumber;
-        let col = position.column;
-        for (let index in tokens) {
-            let token = tokens[index];
-            if (token.getTokenType() === TokenType.Identifier && token.getSpan().inPosition(row, col) && keywords.indexOf(token.getText()) > -1) {
-                let line = token.getSpan().getLine();
-                return {
-                    range: new Range(line.lineNumber, line.startCol, line.endLineNumber, line.endCol + 1),
-                    contents: [{
-                        value: `关键字 **${token.getText()}**`
-                    }]
-                };
-            }
-        }
         let tokenStream = new TokenStream(tokens);
         let parser = new Parser(tokenStream)
         let nodes = parser.parse(true);
-        tokenStream.resetIndex(0)
-        parser.linqLevel = 0;
-        for (let index in nodes) {
-            let node = nodes[index];
-            if (node.getSpan().inPosition(row, col)) {
-                let best = findBestMatch(node, row, col);
-                let env = await parser.preprocessComplection(false,RequestParameter.environmentFunction() || {});
+        let input = model.getValueInRange({
+            startLineNumber: 1,
+            startColumn: 1,
+            endLineNumber: position.lineNumber,
+            endColumn: position.column
+        });
+        let index = input.length;
+        for (let i = 0, len = nodes.length; i < len; i++) {
+            let best = parser.findBestMatch(nodes[i], index)
+            if(best){
+                let env = await parser.processEnv(nodes)
                 let contents = [];
                 let line = best.getSpan().getLine();
                 if (best instanceof VarDefine) {
                     let value = env[best.getVarName()];
                     contents.push({value: `变量:${best.getVarName()}`})
                     contents.push({value: `类型:${value}`})
+                } else if (best instanceof ClassConverter) {
+                    if(best.convert === 'json'){
+                        contents.push({value: '强制转换为`JSON`类型'})
+                    }else if(best.convert === 'stringify'){
+                        contents.push({value: '转换为`JSON`字符串'})
+                    }else if(best.convert === 'sql'){
+                        let args = best.args || []
+                        contents.push({value: `等同于\`new SqlParameterValue(java.sql.Types.${args[0]?.span?.getText()?.toUpperCase()},${best.target.getSpan().getText()})\``})
+                    }else{
+                        contents.push({value: `转换为\`${best.convert}\``})
+                    }
                 } else if (best instanceof VariableAccess) {
                     let value = env[best.getVariable()];
                     contents.push({value: `访问变量:${best.getVariable()}`})
@@ -82,27 +83,32 @@ const HoverProvider = {
                 } else if (best instanceof MemberAccess) {
                     let javaType = await best.getTarget().getJavaType(env);
                     let clazz = await JavaClass.loadClass(javaType);
-                    let methods = JavaClass.findMethods(clazz);
-                    for (let m in methods) {
-                        let method = methods[m];
-                        if (method.name === best.member.getText()) {
-                            generateMethodDocument(method, contents);
-                        }
-                    }
+                    let memberName = best.member.getText()
+                    JavaClass.findMethods(clazz).filter(method => method.name === memberName)
+                        .forEach(method => generateMethodDocument(`${JavaClass.getSimpleClass(javaType)}.`,method, contents))
+                    JavaClass.findEnums(clazz).filter(it => it === memberName).forEach(it => {
+                        contents.push({value: `访问枚举:\`${javaType}.${memberName}\``})
+                    })
+                    JavaClass.findAttributes(clazz).filter(attr => attr.name === memberName).forEach(it => {
+                        contents.push({value: `访问属性:\`${javaType}.${memberName}\``})
+                        it.comment && contents.push({value: `${it.comment}`})
+                        contents.push({value: `属性类型:` + `\`${it.type}\``})
+                    })
+                    line = best.member.getLine();
                 } else if (best instanceof FunctionCall) {
                     let target = best.target;
                     let functions = JavaClass.findFunction().filter(method => method.name === target.variable);
                     if (functions.length > 0) {
-                        generateMethodDocument(functions[0], contents);
+                        generateMethodDocument('', functions[0], contents);
                     } else {
                         let value = env[target.variable];
                         if (value && value.indexOf('@') === 0) {
-                            var functionName = value.substring(1);
+                            let functionName = value.substring(1);
                             let func = JavaClass.getOnlineFunction(functionName);
                             if (func) {
                                 let parameters = Array.isArray(func.parameter) ? func.parameter : JSON.parse(func.parameter || '[]');
                                 parameters.forEach(it => it.comment = it.description);
-                                generateMethodDocument({
+                                generateMethodDocument('', {
                                     fullName: target.variable + " " + func.name,
                                     comment: func.description || '',
                                     parameters,

+ 1 - 0
magic-editor/src/console/src/scripts/editor/java-class.js

@@ -271,5 +271,6 @@ const exportValue = {
     getOnlineFunction,
     setupOnlineFunction,
     setExtensionAttribute,
+    getSimpleClass
 }
 export default exportValue;

+ 27 - 40
magic-editor/src/console/src/scripts/editor/signature.js

@@ -2,7 +2,6 @@ import JavaClass from './java-class.js'
 import tokenizer from '@/scripts/parsing/tokenizer.js'
 import {TokenStream, TokenType} from '../parsing/index.js'
 import {Parser} from '@/scripts/parsing/parser.js'
-import RequestParameter from './request-parameter.js'
 
 const SignatureHelpProvider = {
     signatureHelpRetriggerCharacters: ['(', ','],
@@ -26,47 +25,35 @@ const SignatureHelpProvider = {
             endLineNumber: position.lineNumber,
             endColumn: position.column
         });
-        let char = value.charAt(value.length - 1);
-        if (char !== '(') {
-            return;
-        }
-        let input = value.substring(0, value.lastIndexOf('('));
         try {
-            let tokens = tokenizer(input);
-            let tokenLen = tokens.length;
-            if (tokenLen === 0 || tokens[tokenLen - 1].getTokenType() !== TokenType.Identifier) {
-                return;
-            }
-            let token = tokens.pop();
-            tokens.pop();
+            let tokens = tokenizer(value);
             let parser = new Parser(new TokenStream(tokens));
-            var clazz = await parser.completion(RequestParameter.environmentFunction());
-            let methods = JavaClass.findMethods(clazz);
-            if (methods) {
-                var name = token.getText();
-                var signatures = [];
-                for (var i = 0, len = methods.length; i < len; i++) {
-                    var method = methods[i];
-                    if (method.name === name) {
-                        var document = [];
-                        for (var j = (method.extension ? 1 : 0); j < method.parameters.length; j++) {
-                            var param = method.parameters[j];
-                            document.push('- ' + param.name + ':' + (param.comment || param.type));
-                        }
-                        signatures.push({
-                            label: method.fullName,
-                            documentation: {
-                                value: method.comment
-                            },
-                            parameters: [{
-                                label: 'param1',
-                                documentation: {
-                                    value: document.join('\r\n')
-                                }
-                            }]
-                        });
+            const { best, env} = await parser.parseBest(value.length - 1);
+            if(best && best.constructor.name === 'MethodCall'){
+                let target = best.target
+                let className = await target.getTarget().getJavaType(env);
+                let methodName = target.member.getText()
+                let methods = JavaClass.findMethods(await JavaClass.loadClass(className));
+                let signatures = []
+                methods.filter(it => it.name === methodName).forEach(method => {
+                    let document = [];
+                    for (let j = (method.extension ? 1 : 0); j < method.parameters.length; j++) {
+                        let param = method.parameters[j];
+                        document.push('- ' + param.name + ':' + (param.comment || param.type));
                     }
-                }
+                    signatures.push({
+                        label: method.fullName,
+                        documentation: {
+                            value: method.comment
+                        },
+                        parameters: [{
+                            label: 'param1',
+                            documentation: {
+                                value: document.join('\r\n')
+                            }
+                        }]
+                    });
+                })
                 if (signatures.length > 0) {
                     return {
                         dispose: function () {
@@ -80,7 +67,7 @@ const SignatureHelpProvider = {
                 }
             }
         } catch (e) {
-            //console.log(e);
+            // console.log(e);
         }
     }
 }

+ 18 - 2
magic-editor/src/console/src/scripts/parsing/ast.js

@@ -323,7 +323,7 @@ class UnaryOperation extends Node {
     }
 
     async getJavaType() {
-        return this.operand.getJavaType();
+        return await this.operand.getJavaType();
     }
 
 }
@@ -378,6 +378,20 @@ class Import extends Node {
         this.module = module;
     }
 
+    async getJavaType(env){
+        if(this.packageName.endsWith('.*')) {
+            env['@import'].push(this.packageName.substring(0, this.packageName.length - 1))
+        }else if(this.module){
+            env[this.packageName] = this.packageName
+        }else if(this.varName){
+            env[this.varName.getText()] = this.packageName
+        }else {
+            let index = this.packageName.lastIndexOf('.');
+            if (index > -1) {
+                env[this.packageName.substring(index + 1)] = this.packageName
+            }
+        }
+    }
 
 }
 
@@ -397,7 +411,9 @@ class VarDefine extends Node {
     }
 
     async getJavaType(env) {
-        return this.expression.getJavaType(env);
+        let type = await this.expression.getJavaType(env);
+        env[this.varName] = type
+        return type
     }
 }
 

+ 14 - 11
magic-editor/src/console/src/scripts/parsing/index.js

@@ -53,9 +53,8 @@ class Span {
         return "Span [text=" + this.getText() + ", start=" + this.start + ", end=" + this.end + "]";
     }
 
-    inPosition(row, col) {
-        let line = this.getLine();
-        return line.endLineNumber >= row && line.lineNumber <= row && line.endCol >= col && line.startCol <= col;
+    inPosition(position) {
+        return this.start <= position && this.end >= position;
     }
 
     getLine() {
@@ -191,7 +190,8 @@ const TokenType = {
     StringLiteral: {error: '一个 字符串'},
     NullLiteral: {error: 'null'},
     Language: {error: 'language'},
-    Identifier: {error: '标识符'}
+    Identifier: {error: '标识符'},
+    Unknown: {error: 'unknown'}
 };
 let tokenTypeValues = Object.getOwnPropertyNames(TokenType).map(e => TokenType[e]);
 TokenType.getSortedValues = function () {
@@ -394,8 +394,11 @@ class CharacterStream {
         this.spanStart = this.index;
     }
 
-    endSpan(offset) {
-        return new Span(this.source, this.spanStart, this.index + (offset || 0));
+    endSpan(offsetOrStart, end) {
+        if(end !== undefined) {
+            return new Span(this.source, offsetOrStart, end)
+        }
+        return new Span(this.source, this.spanStart, this.index + (offsetOrStart || 0));
     }
 
     getPosition() {
@@ -514,15 +517,15 @@ class TokenStream {
         if (this.match(text, true, ignoreCase)) {
             return this.tokens[this.index - 1];
         } else {
-            let token = this.index < this.tokens.length ? this.tokens[this.index] : null;
-            let span = token != null ? token.getSpan() : null;
-            if (span == null) {
-                throw new ParseException("Expected '" + this.textToString(text) + "', but reached the end of the source.", this.hasMore() ? this.consume().getSpan() : this.getPrev().getSpan());
+            if (!this.hasMore()) {
+                let span = this.tokens[this.index - 1].getSpan();
+                return new Token(TokenType.Unknown, span);
             } else {
+                let token = this.tokens[this.index];
                 if (text instanceof Token) {
                     text = text.type.error;
                 }
-                throw new ParseException("Expected '" + this.textToString(text) + "', but got '" + token.getText() + "'", span);
+                throw new ParseException("Expected '" + this.textToString(text) + "', but got '" + token.getText() + "'", token.getSpan());
             }
         }
     }

+ 39 - 80
magic-editor/src/console/src/scripts/parsing/parser.js

@@ -37,6 +37,7 @@ import {
     WholeLiteral,
     Throw
 } from './ast.js'
+import RequestParameter from "@/scripts/editor/request-parameter";
 
 export const keywords = ["import", "as", "var", "let", "const", "return", "break", "continue", "if", "for", "in", "new", "true", "false", "null", "else", "try", "catch", "finally", "async", "while", "exit", "and", "or", "throw"/*"assert"*/];
 export const linqKeywords = ["from", "join", "left", "group", "by", "as", "having", "and", "or", "in", "where", "on", "limit", "offset"];
@@ -92,6 +93,32 @@ export class Parser {
         return nodes;
     }
 
+    async parseBest(position){
+        let nodes = this.parse()
+        let env = await this.processEnv(nodes)
+        return {
+            best: this.findBestMatch(nodes[nodes.length - 1], position),
+            env
+        }
+    }
+
+    async processEnv(nodes){
+        let nodeLen = nodes.length
+        let env = {
+            ...RequestParameter.environmentFunction(),
+            ...JavaClass.getAutoImportClass(),
+            ...JavaClass.getAutoImportModule(),
+            '@import': []
+        }
+        // todo 赋值、async、import 未处理
+        if(nodeLen > 1){
+            for (let i = 0; i < nodeLen - 1; i++) {
+                await nodes[i].getJavaType(env)
+            }
+        }
+        return env
+    }
+
     validateNode(node) {
         if (node instanceof Literal || node instanceof VariableAccess || node instanceof MapOrArrayAccess) {
             throw new ParseException('literal cannot be used alone', node.getSpan());
@@ -186,6 +213,7 @@ export class Parser {
         if (this.stream.hasMore()) {
             let expected = this.stream.consume();
             let varName;
+            let packageName;
             let module = expected.getTokenType() === TokenType.Identifier
             if (expected.getTokenType() === TokenType.StringLiteral || module) {
                 if (expected.getTokenType() === TokenType.StringLiteral) {
@@ -194,7 +222,7 @@ export class Parser {
                         this.checkKeyword(varName.getSpan());
                     }
                 }
-                return new Import(new Span(opening, expected.getSpan()), expected.getSpan(), module);
+                return new Import(new Span(opening, expected.getSpan()), expected.getText(), varName, module);
             } else {
                 throw new ParseException("Expected identifier or string, but got stream is " + expected.getTokenType().error, this.stream.getPrev().getSpan());
             }
@@ -335,7 +363,7 @@ export class Parser {
 
     expectCloseing() {
         if (!this.stream.hasMore()) {
-            throw new ParseException("Did not find closing }.", this.stream.prev().getSpan());
+            // throw new ParseException("Did not find closing }.", this.stream.prev().getSpan());
         }
         return this.stream.expect("}").getSpan();
     }
@@ -741,90 +769,21 @@ export class Parser {
         return this.parseAccessOrCall(expression);
     }
 
-    async preprocessComplection(returnJavaType, defineEnvironment) {
-        if(returnJavaType && this.stream.getEnd() instanceof LiteralToken){
-            return this.stream.getEnd().getJavaType();
-        }
-        let env = {
-            ...defineEnvironment,
-            ...JavaClass.getAutoImportClass(),
-            ...JavaClass.getAutoImportModule(),
-            '@import': []
-        }
-        let expression;
-        while (this.stream.hasMore()) {
-            let token = this.stream.consume();
-            let index = this.stream.makeIndex();
-            try {
-                if (token.type === TokenType.Identifier && token.getText() === 'import') {
-                    let varName;
-                    let value;
-                    if (this.stream.match(TokenType.Identifier, false)) {
-                        varName = this.stream.consume().getText();
-                        value = varName;
-                    } else if (this.stream.match(TokenType.StringLiteral, false)) {
-                        value = this.stream.consume().getText();
-                    }
-                    let index = -1;
-                    if (this.stream.match('as', true)) {
-                        varName = this.stream.consume().getText();
-                    } else {
-                        index = value.lastIndexOf('.');
-                        if (index > -1) {
-                            varName = value.substring(index + 1)
-                        }
-                    }
-                    if (value.endsWith(".*")) {
-                        env['@import'].push(value.substring(0, value.length - 1))
-                    } else if (varName) {
-                        env[varName] = value;
-                    }
-                } else if (token.getTokenType() === TokenType.Assignment) {
-                    let varName = this.stream.getPrev().getText()
-                    let value = this.parseStatement();
-                    env[varName] = await value.getJavaType(env);
-                    if (!this.stream.hasMore()) {
-                        expression = value;
-                    }
-                } else if (token.getTokenType() === TokenType.Identifier) {
-                    let defineName = token.getText()
-                    let define = ['var','let','const'].indexOf(token.getText()) > -1;
-                    if (define || (this.stream.hasMore() && this.stream.getToken().type === TokenType.Identifier && keywords.indexOf(defineName) === -1)) {
-                        let varName = this.stream.consume().getText();
-                        if (this.stream.match(TokenType.Assignment, true)) {
-                            let isAsync = this.stream.match("async", true);
-                            let value = this.parseExpression();
-                            if(!define){
-                                env[varName] = env[defineName]
-                            }else{
-                                env[varName] = isAsync ? "java.util.concurrent.Future" : await value.getJavaType(env);
-                            }
-                            if (!this.stream.hasMore()) {
-                                expression = value;
-                            }
-                        }
-                    } else {
-                        this.stream.prev();
-                        expression = this.parseAccessOrCall(token.getTokenType());
-                    }
-                }
-            } catch (e) {
-                this.stream.resetIndex(index);
-                expression = null;
+    findBestMatch(node, position){
+        let expressions = node.expressions().filter(it => it);
+        for (let index in expressions) {
+            let best = this.findBestMatch(expressions[index], position)
+            if (best) {
+                return best;
             }
-
         }
-        if (returnJavaType) {
-            return expression && await expression.getJavaType(env);
+        if (node.getSpan().inPosition(position)) {
+            return node;
         }
-        return env;
+        return null;
     }
 
-    async completion(env) {
-        let type = await this.preprocessComplection(true, env || {});
-        return await JavaClass.loadClass(type);
 
-    }
 }
 
 function processBody(body, level) {