Skip to content

Commit bcbc975

Browse files
authored
Allow overwriting melee object data (#1157)
1 parent 42cacff commit bcbc975

File tree

5 files changed

+152
-13
lines changed

5 files changed

+152
-13
lines changed

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/intermediateLang/interpreter/CompiletimeNatives.java

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import de.peeeq.wurstscript.intermediatelang.interpreter.NativesProvider;
1010
import de.peeeq.wurstscript.intermediatelang.interpreter.ProgramState;
1111
import net.moonlightflower.wc3libs.bin.ObjMod;
12-
import net.moonlightflower.wc3libs.bin.app.objMod.W3U;
1312
import net.moonlightflower.wc3libs.dataTypes.DataType;
1413
import net.moonlightflower.wc3libs.dataTypes.app.War3Int;
1514
import net.moonlightflower.wc3libs.dataTypes.app.War3Real;
@@ -40,20 +39,28 @@ private ILconstTuple makeKey(String key) {
4039
public ILconstTuple createObjectDefinition(ILconstString fileType, ILconstInt newUnitId, ILconstInt deriveFrom) {
4140
ObjMod<? extends ObjMod.Obj> dataStore = globalState.getDataStore(fileType.getVal());
4241
String objIdString = ObjectHelper.objectIdIntToString(newUnitId.getVal());
42+
boolean isMeleeOverride = newUnitId.getVal() == deriveFrom.getVal();
4343

44-
if (dataStore.getObjs().containsKey(ObjId.valueOf(objIdString))) {
44+
if (!isMeleeOverride && dataStore.getObjs().containsKey(ObjId.valueOf(objIdString))) {
4545
globalState.compilationError("Object definition with id " + objIdString + " already exists.");
4646
}
47-
ObjMod.Obj objDef = newDefFromFiletype(dataStore, deriveFrom.getVal(), newUnitId.getVal());
48-
// mark object with special field
49-
ObjMod.Obj.Mod mod = new ObjMod.Obj.Mod(MetaFieldId.valueOf("wurs"), ObjMod.ValType.INT, War3Int.valueOf(ProgramState.GENERATED_BY_WURST));
50-
objDef.addMod(mod);
47+
ObjMod.Obj objDef = newDefFromFiletype(dataStore, deriveFrom.getVal(), newUnitId.getVal(), isMeleeOverride);
48+
if (!isMeleeOverride) {
49+
// mark object with special field
50+
ObjMod.Obj.Mod mod = new ObjMod.Obj.Mod(MetaFieldId.valueOf("wurs"), ObjMod.ValType.INT, War3Int.valueOf(ProgramState.GENERATED_BY_WURST));
51+
objDef.addMod(mod);
52+
}
5153
String key = globalState.addObjectDefinition(objDef);
5254

5355
return makeKey(key);
5456
}
5557

56-
private W3U.Obj newDefFromFiletype(ObjMod<? extends ObjMod.Obj> dataStore, int base, int newId) {
58+
private ObjMod.Obj newDefFromFiletype(ObjMod<? extends ObjMod.Obj> dataStore, int base, int newId, boolean isMeleeOverride) {
59+
if (isMeleeOverride) {
60+
ObjId id = ObjId.valueOf(ObjectHelper.objectIdIntToString(newId));
61+
// same id => modify melee/original definition table
62+
return dataStore.addObj(id, null);
63+
}
5764
ObjId baseIdS = ObjId.valueOf(ObjectHelper.objectIdIntToString(base));
5865
ObjId newIdS = ObjId.valueOf(ObjectHelper.objectIdIntToString(newId));
5966
return dataStore.addObj(newIdS, baseIdS);

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/intermediateLang/interpreter/ProgramStateIO.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -554,17 +554,19 @@ public void exportToWurst(ObjMod<? extends ObjMod.Obj> dataStore,
554554
out.write("// Modified Table (contains all custom objects)\n\n");
555555
exportToWurst(dataStore.getCustomObjs(), fileType, out);
556556

557-
out.write("// Original Table (contains all modified default objects)\n" +
558-
"// Wurst does not support modifying default objects\n" +
559-
"// but you can copy these functions and replace 'xxxx' with a new, custom id.\n\n");
557+
out.write("// Original Table (contains all modified default/melee objects)\n" +
558+
"// These are emitted when createObjectDefinition uses the same base/new id.\n\n");
560559
exportToWurst(dataStore.getOrigObjs(), fileType, out);
561560
}
562561
}
563562

564563
public void exportToWurst(List<? extends ObjMod.Obj> customObjs, ObjectFileType fileType, Appendable out) throws IOException {
565564
for (ObjMod.Obj obj : customObjs) {
566-
String oldId = obj.getBaseId().getVal();
567-
String newId = (obj.getNewId() != null ? obj.getNewId().getVal() : "xxxx");
565+
// Original-table objects (melee overrides) have no base/new id in wc3libs.
566+
// For Wurst export we represent them as same-id overrides.
567+
String objectId = obj.getId().getVal();
568+
String oldId = (obj.getBaseId() != null ? obj.getBaseId().getVal() : objectId);
569+
String newId = (obj.getNewId() != null ? obj.getNewId().getVal() : objectId);
568570
out.append("@compiletime function create_").append(fileType.getExt()).append("_").append(newId)
569571
.append("()\n");
570572
out.append("\tlet def = createObjectDefinition(\"").append(fileType.getExt()).append("\", '");

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/DocumentSymbolRequest.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,34 @@ private DocumentSymbol makeDocumentSymbol(Element p, SymbolKind kind, String nam
6262
if (p instanceof AstElementWithParameters) {
6363
detail = "(" + HoverInfo.getParameterString((AstElementWithParameters) p) + ")";
6464
}
65-
return new DocumentSymbol(name, kind, Convert.range(p), Convert.errorRange(p), detail, children);
65+
Range fullRange = Convert.range(p);
66+
Range selectionRange = sanitizeSelectionRange(fullRange, Convert.errorRange(p));
67+
return new DocumentSymbol(name, kind, fullRange, selectionRange, detail, children);
68+
}
69+
70+
private Range sanitizeSelectionRange(Range fullRange, Range selectionRange) {
71+
if (fullRange == null) {
72+
return selectionRange;
73+
}
74+
if (selectionRange == null) {
75+
return fullRange;
76+
}
77+
if (isRangeContained(fullRange, selectionRange)) {
78+
return selectionRange;
79+
}
80+
return fullRange;
81+
}
82+
83+
private boolean isRangeContained(Range outer, Range inner) {
84+
return compare(inner.getStart(), outer.getStart()) >= 0
85+
&& compare(inner.getEnd(), outer.getEnd()) <= 0;
86+
}
87+
88+
private int compare(Position a, Position b) {
89+
if (a.getLine() != b.getLine()) {
90+
return Integer.compare(a.getLine(), b.getLine());
91+
}
92+
return Integer.compare(a.getCharacter(), b.getCharacter());
6693
}
6794

6895
private void addSymbolsForEntity(List<DocumentSymbol> result, WEntity e) {

de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/CompiletimeNativesTest.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package tests.wurstscript.tests;
22

33
import de.peeeq.wurstio.intermediateLang.interpreter.CompiletimeNatives;
4+
import de.peeeq.wurstio.objectreader.ObjectHelper;
45
import de.peeeq.wurstscript.intermediatelang.ILconstString;
56
import net.moonlightflower.wc3libs.bin.ObjMod;
67
import net.moonlightflower.wc3libs.bin.app.objMod.W3A;
8+
import net.moonlightflower.wc3libs.bin.app.objMod.W3U;
79
import net.moonlightflower.wc3libs.dataTypes.DataType;
810
import net.moonlightflower.wc3libs.dataTypes.app.War3Real;
911
import net.moonlightflower.wc3libs.misc.MetaFieldId;
@@ -53,4 +55,53 @@ public void modifyObjectKeepsDifferentDataPointers() throws Exception {
5355
assertTrue(dataPointers.contains(0));
5456
assertTrue(dataPointers.contains(1));
5557
}
58+
59+
@Test
60+
public void sameIdObjectDefinitionUsesOriginalTable() throws Exception {
61+
CompiletimeNatives natives = new CompiletimeNatives(null, null, false);
62+
W3U w3u = new W3U();
63+
64+
Method newDefFromFiletype = CompiletimeNatives.class.getDeclaredMethod(
65+
"newDefFromFiletype",
66+
ObjMod.class,
67+
int.class,
68+
int.class,
69+
boolean.class
70+
);
71+
newDefFromFiletype.setAccessible(true);
72+
73+
int hfoo = ObjectHelper.objectIdStringToInt("hfoo");
74+
ObjMod.Obj obj = (ObjMod.Obj) newDefFromFiletype.invoke(natives, w3u, hfoo, hfoo, true);
75+
76+
assertEquals(obj.getId().getVal(), "hfoo");
77+
assertEquals(obj.getBaseId(), null, "Melee overwrite should be written as original-table mod");
78+
assertEquals(obj.getNewId(), null, "Melee overwrite should not create a custom/new id");
79+
assertEquals(w3u.getOrigObjs().size(), 1);
80+
assertEquals(w3u.getCustomObjs().size(), 0);
81+
}
82+
83+
@Test
84+
public void differentIdsObjectDefinitionUsesCustomTable() throws Exception {
85+
CompiletimeNatives natives = new CompiletimeNatives(null, null, false);
86+
W3U w3u = new W3U();
87+
88+
Method newDefFromFiletype = CompiletimeNatives.class.getDeclaredMethod(
89+
"newDefFromFiletype",
90+
ObjMod.class,
91+
int.class,
92+
int.class,
93+
boolean.class
94+
);
95+
newDefFromFiletype.setAccessible(true);
96+
97+
int hfoo = ObjectHelper.objectIdStringToInt("hfoo");
98+
int hf01 = ObjectHelper.objectIdStringToInt("hf01");
99+
ObjMod.Obj obj = (ObjMod.Obj) newDefFromFiletype.invoke(natives, w3u, hfoo, hf01, false);
100+
101+
assertEquals(obj.getId().getVal(), "hf01");
102+
assertEquals(obj.getBaseId().getVal(), "hfoo");
103+
assertEquals(obj.getNewId().getVal(), "hf01");
104+
assertEquals(w3u.getOrigObjs().size(), 0);
105+
assertEquals(w3u.getCustomObjs().size(), 1);
106+
}
56107
}

de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/LspNativeFeaturesTests.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import de.peeeq.wurstio.languageserver.ModelManagerImpl;
66
import de.peeeq.wurstio.languageserver.WFile;
77
import de.peeeq.wurstio.languageserver.requests.CodeActionRequest;
8+
import de.peeeq.wurstio.languageserver.requests.DocumentSymbolRequest;
89
import de.peeeq.wurstio.languageserver.requests.GetDefinition;
910
import de.peeeq.wurstio.languageserver.requests.HoverInfo;
1011
import de.peeeq.wurstio.languageserver.requests.InlayHintsRequest;
@@ -18,6 +19,8 @@
1819
import org.eclipse.lsp4j.CodeActionParams;
1920
import org.eclipse.lsp4j.CompletionList;
2021
import org.eclipse.lsp4j.Diagnostic;
22+
import org.eclipse.lsp4j.DocumentSymbol;
23+
import org.eclipse.lsp4j.DocumentSymbolParams;
2124
import org.eclipse.lsp4j.Hover;
2225
import org.eclipse.lsp4j.InlayHint;
2326
import org.eclipse.lsp4j.InlayHintParams;
@@ -136,6 +139,32 @@ public void codeActionsReturnQuickFixWithWorkspaceEdit() throws IOException {
136139
assertTrue(codeActions.stream().anyMatch(a -> a.getEdit() != null));
137140
}
138141

142+
@Test
143+
public void documentSymbolsHaveSelectionInsideRange() throws IOException {
144+
CompletionTestData data = input(
145+
"package test",
146+
"class C",
147+
" int x = 1",
148+
" function f(int a)",
149+
"init",
150+
" skip",
151+
"endpackage"
152+
);
153+
TestContext ctx = createContext(data, data.buffer);
154+
155+
DocumentSymbolParams params = new DocumentSymbolParams(new TextDocumentIdentifier(ctx.uri));
156+
List<Either<org.eclipse.lsp4j.SymbolInformation, DocumentSymbol>> symbols =
157+
new DocumentSymbolRequest(params).execute(ctx.modelManager);
158+
List<DocumentSymbol> docs = symbols.stream()
159+
.filter(Either::isRight)
160+
.map(Either::getRight)
161+
.collect(Collectors.toList());
162+
assertFalse(docs.isEmpty());
163+
for (DocumentSymbol s : docs) {
164+
assertSelectionContainedRecursive(s);
165+
}
166+
}
167+
139168
@Test
140169
public void codeActionCanReplaceVarWithLetForConstantLocal() throws IOException {
141170
CompletionTestData data = input(
@@ -753,6 +782,29 @@ private List<TextEdit> allTextEdits(WorkspaceEdit edit) {
753782
.collect(Collectors.toList());
754783
}
755784

785+
private void assertSelectionContainedRecursive(DocumentSymbol s) {
786+
assertTrue(rangeContains(s.getRange(), s.getSelectionRange()),
787+
"selectionRange must be contained in range for symbol " + s.getName()
788+
+ " range=" + s.getRange() + " selection=" + s.getSelectionRange());
789+
if (s.getChildren() != null) {
790+
for (DocumentSymbol child : s.getChildren()) {
791+
assertSelectionContainedRecursive(child);
792+
}
793+
}
794+
}
795+
796+
private boolean rangeContains(Range outer, Range inner) {
797+
return compare(inner.getStart(), outer.getStart()) >= 0
798+
&& compare(inner.getEnd(), outer.getEnd()) <= 0;
799+
}
800+
801+
private int compare(Position a, Position b) {
802+
if (a.getLine() != b.getLine()) {
803+
return Integer.compare(a.getLine(), b.getLine());
804+
}
805+
return Integer.compare(a.getCharacter(), b.getCharacter());
806+
}
807+
756808
private TestContext createContext(CompletionTestData data, String diskContent) throws IOException {
757809
File projectFolder = new File("./temp/lspNative/" + System.nanoTime());
758810
File wurstFolder = new File(projectFolder, "wurst");

0 commit comments

Comments
 (0)