Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import de.peeeq.wurstscript.intermediatelang.interpreter.NativesProvider;
import de.peeeq.wurstscript.intermediatelang.interpreter.ProgramState;
import net.moonlightflower.wc3libs.bin.ObjMod;
import net.moonlightflower.wc3libs.bin.app.objMod.W3U;
import net.moonlightflower.wc3libs.dataTypes.DataType;
import net.moonlightflower.wc3libs.dataTypes.app.War3Int;
import net.moonlightflower.wc3libs.dataTypes.app.War3Real;
Expand Down Expand Up @@ -40,20 +39,28 @@ private ILconstTuple makeKey(String key) {
public ILconstTuple createObjectDefinition(ILconstString fileType, ILconstInt newUnitId, ILconstInt deriveFrom) {
ObjMod<? extends ObjMod.Obj> dataStore = globalState.getDataStore(fileType.getVal());
String objIdString = ObjectHelper.objectIdIntToString(newUnitId.getVal());
boolean isMeleeOverride = newUnitId.getVal() == deriveFrom.getVal();

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

return makeKey(key);
}

private W3U.Obj newDefFromFiletype(ObjMod<? extends ObjMod.Obj> dataStore, int base, int newId) {
private ObjMod.Obj newDefFromFiletype(ObjMod<? extends ObjMod.Obj> dataStore, int base, int newId, boolean isMeleeOverride) {
if (isMeleeOverride) {
ObjId id = ObjId.valueOf(ObjectHelper.objectIdIntToString(newId));
// same id => modify melee/original definition table
return dataStore.addObj(id, null);
}
ObjId baseIdS = ObjId.valueOf(ObjectHelper.objectIdIntToString(base));
ObjId newIdS = ObjId.valueOf(ObjectHelper.objectIdIntToString(newId));
return dataStore.addObj(newIdS, baseIdS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -554,17 +554,19 @@ public void exportToWurst(ObjMod<? extends ObjMod.Obj> dataStore,
out.write("// Modified Table (contains all custom objects)\n\n");
exportToWurst(dataStore.getCustomObjs(), fileType, out);

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

public void exportToWurst(List<? extends ObjMod.Obj> customObjs, ObjectFileType fileType, Appendable out) throws IOException {
for (ObjMod.Obj obj : customObjs) {
String oldId = obj.getBaseId().getVal();
String newId = (obj.getNewId() != null ? obj.getNewId().getVal() : "xxxx");
// Original-table objects (melee overrides) have no base/new id in wc3libs.
// For Wurst export we represent them as same-id overrides.
String objectId = obj.getId().getVal();
String oldId = (obj.getBaseId() != null ? obj.getBaseId().getVal() : objectId);
String newId = (obj.getNewId() != null ? obj.getNewId().getVal() : objectId);
out.append("@compiletime function create_").append(fileType.getExt()).append("_").append(newId)
.append("()\n");
out.append("\tlet def = createObjectDefinition(\"").append(fileType.getExt()).append("\", '");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,34 @@ private DocumentSymbol makeDocumentSymbol(Element p, SymbolKind kind, String nam
if (p instanceof AstElementWithParameters) {
detail = "(" + HoverInfo.getParameterString((AstElementWithParameters) p) + ")";
}
return new DocumentSymbol(name, kind, Convert.range(p), Convert.errorRange(p), detail, children);
Range fullRange = Convert.range(p);
Range selectionRange = sanitizeSelectionRange(fullRange, Convert.errorRange(p));
return new DocumentSymbol(name, kind, fullRange, selectionRange, detail, children);
}

private Range sanitizeSelectionRange(Range fullRange, Range selectionRange) {
if (fullRange == null) {
return selectionRange;
}
if (selectionRange == null) {
return fullRange;
}
if (isRangeContained(fullRange, selectionRange)) {
return selectionRange;
}
return fullRange;
}

private boolean isRangeContained(Range outer, Range inner) {
return compare(inner.getStart(), outer.getStart()) >= 0
&& compare(inner.getEnd(), outer.getEnd()) <= 0;
}

private int compare(Position a, Position b) {
if (a.getLine() != b.getLine()) {
return Integer.compare(a.getLine(), b.getLine());
}
return Integer.compare(a.getCharacter(), b.getCharacter());
}

private void addSymbolsForEntity(List<DocumentSymbol> result, WEntity e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package tests.wurstscript.tests;

import de.peeeq.wurstio.intermediateLang.interpreter.CompiletimeNatives;
import de.peeeq.wurstio.objectreader.ObjectHelper;
import de.peeeq.wurstscript.intermediatelang.ILconstString;
import net.moonlightflower.wc3libs.bin.ObjMod;
import net.moonlightflower.wc3libs.bin.app.objMod.W3A;
import net.moonlightflower.wc3libs.bin.app.objMod.W3U;
import net.moonlightflower.wc3libs.dataTypes.DataType;
import net.moonlightflower.wc3libs.dataTypes.app.War3Real;
import net.moonlightflower.wc3libs.misc.MetaFieldId;
Expand Down Expand Up @@ -53,4 +55,53 @@ public void modifyObjectKeepsDifferentDataPointers() throws Exception {
assertTrue(dataPointers.contains(0));
assertTrue(dataPointers.contains(1));
}

@Test
public void sameIdObjectDefinitionUsesOriginalTable() throws Exception {
CompiletimeNatives natives = new CompiletimeNatives(null, null, false);
W3U w3u = new W3U();

Method newDefFromFiletype = CompiletimeNatives.class.getDeclaredMethod(
"newDefFromFiletype",
ObjMod.class,
int.class,
int.class,
boolean.class
);
newDefFromFiletype.setAccessible(true);

int hfoo = ObjectHelper.objectIdStringToInt("hfoo");
ObjMod.Obj obj = (ObjMod.Obj) newDefFromFiletype.invoke(natives, w3u, hfoo, hfoo, true);

assertEquals(obj.getId().getVal(), "hfoo");
assertEquals(obj.getBaseId(), null, "Melee overwrite should be written as original-table mod");
assertEquals(obj.getNewId(), null, "Melee overwrite should not create a custom/new id");
assertEquals(w3u.getOrigObjs().size(), 1);
assertEquals(w3u.getCustomObjs().size(), 0);
}

@Test
public void differentIdsObjectDefinitionUsesCustomTable() throws Exception {
CompiletimeNatives natives = new CompiletimeNatives(null, null, false);
W3U w3u = new W3U();

Method newDefFromFiletype = CompiletimeNatives.class.getDeclaredMethod(
"newDefFromFiletype",
ObjMod.class,
int.class,
int.class,
boolean.class
);
newDefFromFiletype.setAccessible(true);

int hfoo = ObjectHelper.objectIdStringToInt("hfoo");
int hf01 = ObjectHelper.objectIdStringToInt("hf01");
ObjMod.Obj obj = (ObjMod.Obj) newDefFromFiletype.invoke(natives, w3u, hfoo, hf01, false);

assertEquals(obj.getId().getVal(), "hf01");
assertEquals(obj.getBaseId().getVal(), "hfoo");
assertEquals(obj.getNewId().getVal(), "hf01");
assertEquals(w3u.getOrigObjs().size(), 0);
assertEquals(w3u.getCustomObjs().size(), 1);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import de.peeeq.wurstio.languageserver.ModelManagerImpl;
import de.peeeq.wurstio.languageserver.WFile;
import de.peeeq.wurstio.languageserver.requests.CodeActionRequest;
import de.peeeq.wurstio.languageserver.requests.DocumentSymbolRequest;
import de.peeeq.wurstio.languageserver.requests.GetDefinition;
import de.peeeq.wurstio.languageserver.requests.HoverInfo;
import de.peeeq.wurstio.languageserver.requests.InlayHintsRequest;
Expand All @@ -18,6 +19,8 @@
import org.eclipse.lsp4j.CodeActionParams;
import org.eclipse.lsp4j.CompletionList;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.DocumentSymbol;
import org.eclipse.lsp4j.DocumentSymbolParams;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.InlayHint;
import org.eclipse.lsp4j.InlayHintParams;
Expand Down Expand Up @@ -136,6 +139,32 @@ public void codeActionsReturnQuickFixWithWorkspaceEdit() throws IOException {
assertTrue(codeActions.stream().anyMatch(a -> a.getEdit() != null));
}

@Test
public void documentSymbolsHaveSelectionInsideRange() throws IOException {
CompletionTestData data = input(
"package test",
"class C",
" int x = 1",
" function f(int a)",
"init",
" skip",
"endpackage"
);
TestContext ctx = createContext(data, data.buffer);

DocumentSymbolParams params = new DocumentSymbolParams(new TextDocumentIdentifier(ctx.uri));
List<Either<org.eclipse.lsp4j.SymbolInformation, DocumentSymbol>> symbols =
new DocumentSymbolRequest(params).execute(ctx.modelManager);
List<DocumentSymbol> docs = symbols.stream()
.filter(Either::isRight)
.map(Either::getRight)
.collect(Collectors.toList());
assertFalse(docs.isEmpty());
for (DocumentSymbol s : docs) {
assertSelectionContainedRecursive(s);
}
}

@Test
public void codeActionCanReplaceVarWithLetForConstantLocal() throws IOException {
CompletionTestData data = input(
Expand Down Expand Up @@ -753,6 +782,29 @@ private List<TextEdit> allTextEdits(WorkspaceEdit edit) {
.collect(Collectors.toList());
}

private void assertSelectionContainedRecursive(DocumentSymbol s) {
assertTrue(rangeContains(s.getRange(), s.getSelectionRange()),
"selectionRange must be contained in range for symbol " + s.getName()
+ " range=" + s.getRange() + " selection=" + s.getSelectionRange());
if (s.getChildren() != null) {
for (DocumentSymbol child : s.getChildren()) {
assertSelectionContainedRecursive(child);
}
}
}

private boolean rangeContains(Range outer, Range inner) {
return compare(inner.getStart(), outer.getStart()) >= 0
&& compare(inner.getEnd(), outer.getEnd()) <= 0;
}

private int compare(Position a, Position b) {
if (a.getLine() != b.getLine()) {
return Integer.compare(a.getLine(), b.getLine());
}
return Integer.compare(a.getCharacter(), b.getCharacter());
}

private TestContext createContext(CompletionTestData data, String diskContent) throws IOException {
File projectFolder = new File("./temp/lspNative/" + System.nanoTime());
File wurstFolder = new File(projectFolder, "wurst");
Expand Down
Loading