Skip to content

Commit

Permalink
Add support for MixinExtras expressions (#2274)
Browse files Browse the repository at this point in the history
* Start on MixinExtras Expression language

* MEExpression color settings page

* MEExpression annotator

* MEExpression brace matcher and quote handler

* Switch LHS of MEExpression assignmentExpression to themselves be certain types of expression

* MEExpression language injection inside @expression

* Fix formatting and licenses

* Add MIXINEXTRAS:EXPRESSION injection point and add @expression annotation on completion

* Fix licenser errors

* Add new ME expression features

* Implement MixinExtras expression collect visitor

* Fix cast expressions

* Simple best-effort source matching for ME expressions

* Fix name expression source matching

* Fix MEName.isWildcard

* Fix MELitExpression source matching

* operationSign - operationTokenType

* Add built-in definitions

* Update MixinExtras

* Start with ME definition references

* Attempt to overhaul ME expression injection

* Some fixes to the new injection + navigation

* MixinExtras: Add handler signature support for expressions. (#2244)

* Partially fix ME definition renaming

* Attempt to get inplace rename refactoring to work (it doesn't)

* MixinExtras: Use expression-suggested parameter names if they're present. (#2257)

* Fix MEExpressionInjector. Rename refactoring works!

* Suppress deprecation warning

* ME expression `@Definition` find usages

* Fix/expressions int like types (#2261)

* Mixin: Combine parameter and return type inspections.

* MixinExtras: Offer a choice between all valid int-like types.

* Mixin: Fix tests for handler signature inspection.

* Add simple keyword completion to ME expressions

* Why didn't my local ktlint tell me about these

* Store whether a declaration is a type in the ME PSI

* Add completions for items that already have a definition

* Extract some ME expression matching into its own class, and cache some more things

* Remove some debug code

* First attempt at bytecode-based completion (it's broken)

* Bytecode-based completion fixes

* Add new definition annotations below existing definition annotations, or at the top

* Fix cursor offset

* Add utilities to textify various ASM nodes

* Add expression variants, to allow MixinExtras to match expressions as if they were other types of expressions

* Merge two expression types into a single MENewExpression, improve ME expression completion

* Add better completion tail types

* Fix completion not working inside of constructors

* Add errors/warnings for unused and unresolved definitions, and highlight primitive types specially

* Split MatchUtil into CompletionUtil

* Fold @At.target for definition completions

* Local variable completions

* Some fixes to local variable completion

* Fix can-be-implicit locals with inaccessible types not showing template

* Show field and method types in completion list

* Add completion tests, and make some fixes to completion

* Fix folding on completion

* Refactor mcdev settings, move shadow setting into a project setting

* Add setting for position of @Definition relative to @expression

* Add folding for @definitions

* Fix array literal input completion

* Fix ktlint

* Relax uniqueness in ME expression completions

* Initial switch from @Definition.at to field and method

* Fix tests

* Add references to @Definition.field and method

* Add folding to @Definition.field and method

* Add comments to MEExpressionCompletionUtil to explain the completion process in more detail

* Fix @Local.type extraction

* Add string literal completion for ME expressions

* Fix @Local.type for source matching too

* Fix parsing of multiple @expression annotations in a single modifier list

* Format MEExpressionLexer.flex

* Better handle parenthesized expressions in Java source matching

* Add method reference expressions

* Fix presentable name of method reference completions

* Override matches(Handle) for method MemberDefinition

* Update MixinExtras and don't assume that instructions are part of the method

* Wrap expanded instructions in a custom type to prevent them accidentally being used when an instruction in the original method is expected or vice versa.

* Fix IdentityHashMap with VirtualInsn keys

* Update MixinExtras

* Fix completion of new expressions with new MixinExtras update

* Update MixinExtras, fixes compound instructions

* New: Add support for string concat expressions in MixinExtras. (#2281)

* Address PR comments

* MixinExtras Expressions: Migrate to Expressions library.

* Fix: Resolve being unable to get the descriptor for some complex types.

* MixinExtras Expressions: Recreate `ClassInfo#getCommonSuperClassOrInterface`.
It's quite scuffed and often returns `Object` even when there is a suitable common interface, but what's important is that this matches the runtime logic.

* MixinExtras: Fix completion confidence.

* Expressions: Autocomplete `method`s and `field`s using flows not instructions.

* Expressions: Autocomplete `method`s for method references.

* Expressions: A class constant is an expression.

* Expressions: Show instantiation desc in autocomplete.

* Expressions: Make completions always unique to stop them being filtered.
We filter duplicates ourselves.

* Expressions: Overhaul array completions.
Make the preview text more representative of the actual completion and support array literals which were previously missing.

* Expressions: Fix super call completions.

* Expressions: Confidently suggest completions after `::`.

* Expressions: Add intention action to define unresolved identifiers.

* Expressions: Fix `@Local`s not properly handling compound insns.
Also adapt to related MixinExtras changes.

* Refactor: Add `project` as parameter to `MEExpressionCompletionUtil.addDefinition`

* Use maven central MixinExtras library

* Remove reference to light service in plugin.xml

* Use ReentrantReadWriteLock.read and write extension functions

---------

Co-authored-by: LlamaLad7 <[email protected]>
  • Loading branch information
Earthcomputer and LlamaLad7 authored Jul 12, 2024
1 parent 85e493a commit 6b0471e
Show file tree
Hide file tree
Showing 128 changed files with 8,901 additions and 317 deletions.
11 changes: 11 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,17 @@ repositories {
}
}
mavenCentral()
maven("https://repo.spongepowered.org/maven/")
}

dependencies {
// Add tools.jar for the JDI API
implementation(files(Jvm.current().toolsJar))

implementation(libs.mixinExtras.expressions)
testLibs(libs.mixinExtras.common)
implementation("org.ow2.asm:asm-util:9.3")

// Kotlin
implementation(kotlin("stdlib-jdk8"))
implementation(kotlin("reflect"))
Expand Down Expand Up @@ -198,6 +203,7 @@ intellij {
"Kotlin",
"org.toml.lang:$pluginTomlVersion",
"ByteCodeViewer",
"org.intellij.intelliLang",
"properties",
// needed dependencies for unit tests
"junit"
Expand Down Expand Up @@ -363,6 +369,9 @@ val generateNbttParser by parser("NbttParser", "com/demonwav/mcdev/nbt/lang/gen"
val generateLangLexer by lexer("LangLexer", "com/demonwav/mcdev/translations/lang/gen")
val generateLangParser by parser("LangParser", "com/demonwav/mcdev/translations/lang/gen")

val generateMEExpressionLexer by lexer("MEExpressionLexer", "com/demonwav/mcdev/platform/mixin/expression/gen")
val generateMEExpressionParser by parser("MEExpressionParser", "com/demonwav/mcdev/platform/mixin/expression/gen")

val generateTranslationTemplateLexer by lexer(
"TranslationTemplateLexer",
"com/demonwav/mcdev/translations/template/gen"
Expand All @@ -381,6 +390,8 @@ val generate by tasks.registering {
generateNbttParser,
generateLangLexer,
generateLangParser,
generateMEExpressionLexer,
generateMEExpressionParser,
generateTranslationTemplateLexer,
)
}
Expand Down
3 changes: 3 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ coroutines-jdk8 = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8", ve
coroutines-swing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "coroutines" }

mappingIo = "net.fabricmc:mapping-io:0.2.1"
mixinExtras-expressions = "io.github.llamalad7:mixinextras-expressions:0.0.1"

# GrammarKit
jflex-lib = "org.jetbrains.idea:jflex:1.7.0-b7f882a"
Expand Down Expand Up @@ -40,6 +41,8 @@ junit-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "jun
junit-entine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit" }
junit-platform-launcher = { module = "org.junit.platform:junit-platform-launcher", version.ref = "junit-platform" }

mixinExtras-common = "io.github.llamalad7:mixinextras-common:0.5.0-beta.1"

[bundles]
coroutines = ["coroutines-core", "coroutines-jdk8", "coroutines-swing"]
asm = ["asm", "asm-tree", "asm-analysis"]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Minecraft Development for IntelliJ
*
* https://mcdev.io/
*
* Copyright (C) 2024 minecraft-dev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, version 3.0 only.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package com.demonwav.mcdev.mixintestdata.meExpression;

import java.util.ArrayList;
import java.util.stream.Stream;

public class MEExpressionTestData {
private static final SynchedData<Integer> STINGER_COUNT = null;
private SynchedDataManager synchedData;

public void complexFunction() {
int one = 1;
String local1 = "Hello";
String local2 = "World";

System.out.println(new StringBuilder(local1).append(", ").append(local2));
System.out.println(one);

new ArrayList<>(10);

InaccessibleType varOfInaccessibleType = new InaccessibleType();
acceptInaccessibleType(varOfInaccessibleType);
noArgMethod();

String[] strings1 = new String[] { local1, local2 };
String[] strings2 = new String[one];

Stream.empty().map(this::nonStaticMapper).map(MEExpressionTestData::staticMapper).map(ConstructedByMethodReference::new);
}

private static void acceptInaccessibleType(InaccessibleType type) {
}

private static void noArgMethod() {
}

public int getStingerCount() {
return (Integer) this.synchedData.get(STINGER_COUNT);
}

private Object nonStaticMapper(Object arg) {
return arg;
}

private static Object staticMapper(Object arg) {
return arg;
}

private static class InaccessibleType {

}

public static class SynchedDataManager {
public <V> V get(SynchedData<V> data) {
return null;
}
}

public static class SynchedData<V> {
}

public static class ConstructedByMethodReference {
public ConstructedByMethodReference(Object bar) {}
}
}
146 changes: 146 additions & 0 deletions src/main/grammars/MEExpressionLexer.flex
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*
* Minecraft Development for IntelliJ
*
* https://mcdev.io/
*
* Copyright (C) 2024 minecraft-dev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, version 3.0 only.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package com.demonwav.mcdev.platform.mixin.expression;

import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MEExpressionTypes;
import com.intellij.lexer.FlexLexer;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.TokenType;

%%

%public
%class MEExpressionLexer
%implements FlexLexer
%function advance
%type IElementType

%state STRING

%unicode

WHITE_SPACE = [\ \n\t\r]
RESERVED = assert|break|case|catch|const|continue|default|else|finally|for|goto|if|switch|synchronized|try|while|yield|_
WILDCARD = "?"
NEW = new
INSTANCEOF = instanceof
BOOL_LIT = true|false
NULL_LIT = null
DO = do
RETURN = return
THROW = throw
THIS = this
SUPER = super
CLASS = class
IDENTIFIER = [A-Za-z_][A-Za-z0-9_]*
INT_LIT = ( [0-9]+ | 0x[0-9a-fA-F]+ )
DEC_LIT = [0-9]*\.[0-9]+
PLUS = "+"
MINUS = -
MULT = "*"
DIV = "/"
MOD = %
BITWISE_NOT = "~"
DOT = "."
COMMA = ,
LEFT_PAREN = "("
RIGHT_PAREN = ")"
LEFT_BRACKET = "["
RIGHT_BRACKET = "]"
LEFT_BRACE = "{"
RIGHT_BRACE = "}"
AT = @
SHL = <<
SHR = >>
USHR = >>>
LT = <
LE = <=
GT = >
GE = >=
EQ = ==
NE = "!="
BITWISE_AND = &
BITWISE_XOR = "^"
BITWISE_OR = "|"
ASSIGN = =
METHOD_REF = ::

STRING_TERMINATOR = '
STRING_ESCAPE = \\'|\\\\

%%

<YYINITIAL> {
{WHITE_SPACE}+ { return TokenType.WHITE_SPACE; }
{RESERVED} { return MEExpressionTypes.TOKEN_RESERVED; }
{WILDCARD} { return MEExpressionTypes.TOKEN_WILDCARD; }
{NEW} { return MEExpressionTypes.TOKEN_NEW; }
{INSTANCEOF} { return MEExpressionTypes.TOKEN_INSTANCEOF; }
{BOOL_LIT} { return MEExpressionTypes.TOKEN_BOOL_LIT; }
{NULL_LIT} { return MEExpressionTypes.TOKEN_NULL_LIT; }
{DO} { return MEExpressionTypes.TOKEN_DO; }
{RETURN} { return MEExpressionTypes.TOKEN_RETURN; }
{THROW} { return MEExpressionTypes.TOKEN_THROW; }
{THIS} { return MEExpressionTypes.TOKEN_THIS; }
{SUPER} { return MEExpressionTypes.TOKEN_SUPER; }
{CLASS} { return MEExpressionTypes.TOKEN_CLASS; }
{IDENTIFIER} { return MEExpressionTypes.TOKEN_IDENTIFIER; }
{INT_LIT} { return MEExpressionTypes.TOKEN_INT_LIT; }
{DEC_LIT} { return MEExpressionTypes.TOKEN_DEC_LIT; }
{PLUS} { return MEExpressionTypes.TOKEN_PLUS; }
{MINUS} { return MEExpressionTypes.TOKEN_MINUS; }
{MULT} { return MEExpressionTypes.TOKEN_MULT; }
{DIV} { return MEExpressionTypes.TOKEN_DIV; }
{MOD} { return MEExpressionTypes.TOKEN_MOD; }
{BITWISE_NOT} { return MEExpressionTypes.TOKEN_BITWISE_NOT; }
{DOT} { return MEExpressionTypes.TOKEN_DOT; }
{COMMA} { return MEExpressionTypes.TOKEN_COMMA; }
{LEFT_PAREN} { return MEExpressionTypes.TOKEN_LEFT_PAREN; }
{RIGHT_PAREN} { return MEExpressionTypes.TOKEN_RIGHT_PAREN; }
{LEFT_BRACKET} { return MEExpressionTypes.TOKEN_LEFT_BRACKET; }
{RIGHT_BRACKET} { return MEExpressionTypes.TOKEN_RIGHT_BRACKET; }
{LEFT_BRACE} { return MEExpressionTypes.TOKEN_LEFT_BRACE; }
{RIGHT_BRACE} { return MEExpressionTypes.TOKEN_RIGHT_BRACE; }
{AT} { return MEExpressionTypes.TOKEN_AT; }
{SHL} { return MEExpressionTypes.TOKEN_SHL; }
{SHR} { return MEExpressionTypes.TOKEN_SHR; }
{USHR} { return MEExpressionTypes.TOKEN_USHR; }
{LT} { return MEExpressionTypes.TOKEN_LT; }
{LE} { return MEExpressionTypes.TOKEN_LE; }
{GT} { return MEExpressionTypes.TOKEN_GT; }
{GE} { return MEExpressionTypes.TOKEN_GE; }
{EQ} { return MEExpressionTypes.TOKEN_EQ; }
{NE} { return MEExpressionTypes.TOKEN_NE; }
{BITWISE_AND} { return MEExpressionTypes.TOKEN_BITWISE_AND; }
{BITWISE_XOR} { return MEExpressionTypes.TOKEN_BITWISE_XOR; }
{BITWISE_OR} { return MEExpressionTypes.TOKEN_BITWISE_OR; }
{ASSIGN} { return MEExpressionTypes.TOKEN_ASSIGN; }
{METHOD_REF} { return MEExpressionTypes.TOKEN_METHOD_REF; }
{STRING_TERMINATOR} { yybegin(STRING); return MEExpressionTypes.TOKEN_STRING_TERMINATOR; }
}

<STRING> {
{STRING_ESCAPE} { return MEExpressionTypes.TOKEN_STRING_ESCAPE; }
{STRING_TERMINATOR} { yybegin(YYINITIAL); return MEExpressionTypes.TOKEN_STRING_TERMINATOR; }
[^'\\]+ { return MEExpressionTypes.TOKEN_STRING; }
}

[^] { return TokenType.BAD_CHARACTER; }
Loading

0 comments on commit 6b0471e

Please sign in to comment.