Skip to content

Commit

Permalink
fixed inline values only showing values for active function (#137)
Browse files Browse the repository at this point in the history
* fixed inline values only showing values for active function
* added filters for sharp conditions, type names and enum fields
* fixed sharp detection for complex sharp conditions
* fixed JS debugger not evaluating variable values
* fixed type parameters showing up as inline values
* updated version number
* changed default of disableInlineValue to true
  • Loading branch information
AlexHaxe authored Feb 3, 2025
1 parent f17c912 commit e1fed62
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 28 deletions.
19 changes: 10 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "@vshaxe/haxe-language-server",
"version": "2.33.0",
"version": "2.34.1",
"devDependencies": {
"lix": "^15.12.0",
"lix": "^15.12.4",
"terser": "^5.15.0"
},
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion src/haxeLanguageServer/Configuration.hx
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ class Configuration {
maxCompletionItems: 1000,
renameSourceFolders: ["src", "source", "Source", "test", "tests"],
disableRefactorCache: false,
disableInlineValue: false,
disableInlineValue: true,
inlayHints: {
variableTypes: false,
parameterNames: false,
Expand Down
184 changes: 168 additions & 16 deletions src/haxeLanguageServer/features/haxe/InlineValueFeature.hx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import jsonrpc.Types.NoData;
import languageServerProtocol.Types.InlineValue;
import languageServerProtocol.protocol.InlineValue;
import refactor.discover.Identifier;
import refactor.discover.IdentifierPos;
import refactor.discover.Type;
import tokentree.utils.TokenTreeCheckUtils;

using tokentree.TokenTreeAccessHelper;

class InlineValueFeature {
final context:Context;
Expand All @@ -26,56 +31,203 @@ class InlineValueFeature {
}

function onInlineValue(params:InlineValueParams, token:CancellationToken, resolve:Array<InlineValue>->Void, reject:ResponseError<NoData>->Void) {
final onResolve = context.startTimer("textDocument/inlineValue");
if (context.config.user.disableRefactorCache || context.config.user.disableInlineValue) {
resolve([]);
onResolve();
return;
}

var file = refactorCache.fileList.getFile(params.textDocument.uri.toFsPath().toString());
if (file == null) {
reject.handler()("file not found");
onResolve();
return;
}

var editDoc = new EditDoc(params.textDocument.uri.toFsPath(), new EditList(), context, converter);

var localScopedNames:Array<String> = [];
var outOfScope:Array<Identifier> = [];
var functionStartLine:Int = params.context.stoppedLocation.start.line;

function matchLocalScoped(identifier:Identifier):Bool {
switch (identifier.name) {
case "this" | "super":
return false;
default:
}
return switch (identifier.type) {
case ScopedLocal(scopeStart, scopeEnd, scopeType):
var pos:IdentifierPos = {
fileName: identifier.pos.fileName,
start: scopeStart,
end: scopeEnd
};
var range = editDoc.posToRange(pos);
if (!range.contains(params.context.stoppedLocation)) {
outOfScope.push(identifier);
return false;
}
localScopedNames.push(identifier.name);
true;
case Access: true;
case Access:
for (scoped in outOfScope) {
switch (scoped.type) {
case ScopedLocal(scopeStart, scopeEnd, scopeType):
if ((scoped.name == identifier.name) || identifier.name.startsWith('${scoped.name}.')) {
if (scopeStart <= identifier.pos.start && scopeEnd >= identifier.pos.end) {
return false;
}
}
default:
}
}
true;
case Method(_):
var functionRange:Range = editDoc.posToRange(identifier.pos);
if (functionRange.start.line <= params.context.stoppedLocation.start.line) {
functionStartLine = functionRange.start.line;
}
false;
default: false;
}
}
final identifiers:Array<Identifier> = file.findAllIdentifiers(matchLocalScoped);
final inlineValueVars:Array<InlineValue> = [];
for (identifier in identifiers) {
var identifierRange = editDoc.posToRange(identifier.pos);
if (identifierRange.start.line < functionStartLine) {
continue;
}
if (!params.range.contains(identifierRange)) {
continue;
}
var needsExpression:Bool = identifier.name.contains(".");
if ((identifier.type == Access) && !localScopedNames.contains(identifier.name)) {
needsExpression = true;
if (params.context.stoppedLocation.end.line < identifierRange.start.line) {
continue;
}

if (needsExpression) {
inlineValueVars.push({
range: identifierRange,
expression: identifier.name
});
} else {
inlineValueVars.push({
range: identifierRange,
variableName: identifier.name,
caseSensitiveLookup: true
});
if (isSharpCondition(params, identifier)) {
continue;
}
if (isTypeParam(params, identifier)) {
continue;
}
if (isLocalFunctionName(params, identifier)) {
continue;
}
var hasDot:Bool = identifier.name.contains(".");
if (!hasDot) {
if (skipIdentifier(identifier)) {
continue;
}
}
inlineValueVars.push({
range: identifierRange,
expression: identifier.name
});
}

resolve(inlineValueVars);
onResolve();
}

function isSharpCondition(params:InlineValueParams, identifier:Identifier):Bool {
final doc:Null<HaxeDocument> = context.documents.getHaxe(params.textDocument.uri);
final token = doc?.tokens?.getTokenAtOffset(identifier.pos.start);

if (token == null) {
return false;
}
var parent = token.parent;
while (parent != null) {
switch (parent.tok) {
case Dot:
case Const(CIdent(_)):
case POpen:
case Unop(OpNot):
case Binop(OpBoolAnd) | Binop(OpBoolOr):
case Sharp("if") | Sharp("elseif"):
return true;
default:
return false;
}
parent = parent.parent;
}

return false;
}

function isLocalFunctionName(params:InlineValueParams, identifier:Identifier):Bool {
final doc:Null<HaxeDocument> = context.documents.getHaxe(params.textDocument.uri);
final token = doc?.tokens?.getTokenAtOffset(identifier.pos.start);

if (token == null) {
return false;
}
switch (token.parent?.tok) {
case Kwd(KwdFunction):
return true;
default:
return false;
}
}

function isTypeParam(params:InlineValueParams, identifier:Identifier):Bool {
final doc:Null<HaxeDocument> = context.documents.getHaxe(params.textDocument.uri);
final token = doc?.tokens?.getTokenAtOffset(identifier.pos.end);

if (token == null) {
return false;
}
var parent = token.parent;
while (parent != null) {
switch (parent.tok) {
case Dot:
case DblDot:
case BrOpen:
case Const(CIdent(_)):
case Binop(OpAssign):
case Sharp(_):
return true;
case Binop(OpLt):
return parent.access().firstOf(Binop(OpGt)).exists();
default:
return false;
}
parent = parent.parent;
}

return false;
}

function skipIdentifier(identifier:Identifier):Bool {
if (isTypeUsed(identifier.defineType, identifier.name)) {
return true;
}
var allUses:Array<Identifier> = refactorCache.nameMap.getIdentifiers(identifier.name);
for (use in allUses) {
switch (use.type) {
case EnumField(_):
return true;
default:
}
}
return false;
}

function isTypeUsed(containerType:Null<Type>, name:String):Bool {
if (containerType == null) {
return false;
}
final types:Array<Type> = refactorCache.typeList.findTypeName(name);
for (type in types) {
switch (containerType.file.importsModule(type.file.getPackage(), type.file.getMainModulName(), name)) {
case None:
case ImportedWithAlias(_):
case Global | ParentPackage | SamePackage | Imported | StarImported:
return true;
}
}
return false;
}
}

0 comments on commit e1fed62

Please sign in to comment.