Skip to content

Commit

Permalink
Fixes #336
Browse files Browse the repository at this point in the history
  • Loading branch information
isc-bsaviano committed Jul 29, 2024
1 parent 414f6db commit d333ce6
Show file tree
Hide file tree
Showing 5 changed files with 442 additions and 304 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## [2.5.2] - 2024-XX-XX
- Fix issue [#336](https://github.com/intersystems/language-server/issues/336): Add intellisense for variables set to the returned value of a method, or a property, with a declared type

## [2.5.1] - 2024-07-09
- Fix issue [#328](https://github.com/intersystems/language-server/issues/328): Fix namespace detection for Diagnostic computation
- Fix issue [#331](https://github.com/intersystems/language-server/issues/331): Fix display of method arguments with a colon in the default value
Expand Down
21 changes: 13 additions & 8 deletions server/src/providers/completion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1087,7 +1087,9 @@ export async function onCompletion(params: CompletionParams): Promise<Completion

// Query the server to get the names and descriptions of all parameters
const data: QueryData = {
query: "SELECT Name, Description, Origin, Type, Deprecated FROM %Dictionary.CompiledParameter WHERE parent->ID = ?",
query: `SELECT Name, Description, Origin, Type, Deprecated FROM %Dictionary.CompiledParameter WHERE parent->ID = ?${
membercontext.context == "instance" ? " AND (parent->ClassType IS NULL OR parent->ClassType != 'datatype')" : ""
}`,
parameters: [membercontext.baseclass]
}
const respdata = await makeRESTRequest("POST",1,"/action/query",server,data);
Expand Down Expand Up @@ -1156,17 +1158,20 @@ export async function onCompletion(params: CompletionParams): Promise<Completion
}
else if (membercontext.context == "instance") {
data.query = "SELECT Name, Description, Origin, FormalSpec, ReturnType AS Type, 'method' AS MemberType, Deprecated, NULL AS Aliases " +
"FROM %Dictionary.CompiledMethod WHERE parent->ID = ? AND Stub IS NULL AND ((Origin = parent->ID) OR (Origin != parent->ID AND NotInheritable = 0)) UNION ALL %PARALLEL " +
"FROM %Dictionary.CompiledMethod WHERE parent->ID = ? AND Stub IS NULL AND ((Origin = parent->ID) OR (Origin != parent->ID AND NotInheritable = 0)) " +
"AND (parent->ClassType IS NULL OR parent->ClassType != 'datatype') UNION ALL %PARALLEL " +
"SELECT parent->name||Name AS Name, Description, parent->Origin AS Origin, FormalSpec, ReturnType AS Type, 'method' AS MemberType, Deprecated, NULL AS Aliases " +
"FROM %Dictionary.CompiledIndexMethod WHERE parent->parent->ID = ? UNION ALL %PARALLEL " +
"FROM %Dictionary.CompiledIndexMethod WHERE parent->parent->ID = ? AND (parent->parent->ClassType IS NULL OR parent->parent->ClassType != 'datatype') UNION ALL %PARALLEL " +
"SELECT parent->name||Name AS Name, Description, parent->Origin AS Origin, FormalSpec, ReturnType AS Type, 'method' AS MemberType, Deprecated, NULL AS Aliases " +
"FROM %Dictionary.CompiledQueryMethod WHERE parent->parent->ID = ? UNION ALL %PARALLEL " +
"FROM %Dictionary.CompiledQueryMethod WHERE parent->parent->ID = ? AND (parent->parent->ClassType IS NULL OR parent->parent->ClassType != 'datatype') UNION ALL %PARALLEL " +
"SELECT parent->name||Name AS Name, Description, parent->Origin AS Origin, FormalSpec, ReturnType AS Type, 'method' AS MemberType, Deprecated, NULL AS Aliases " +
"FROM %Dictionary.CompiledPropertyMethod WHERE parent->parent->ID = ? UNION ALL %PARALLEL " +
"FROM %Dictionary.CompiledPropertyMethod WHERE parent->parent->ID = ? AND (parent->parent->ClassType IS NULL OR parent->parent->ClassType != 'datatype') UNION ALL %PARALLEL " +
"SELECT parent->name||Name AS Name, Description, parent->Origin AS Origin, FormalSpec, ReturnType AS Type, 'method' AS MemberType, Deprecated, NULL AS Aliases " +
"FROM %Dictionary.CompiledConstraintMethod WHERE parent->parent->ID = ? UNION ALL %PARALLEL " +
"SELECT Name, Description, Origin, NULL AS FormalSpec, RuntimeType AS Type, 'property' AS MemberType, Deprecated, Aliases FROM %Dictionary.CompiledProperty WHERE parent->ID = ? UNION ALL %PARALLEL " +
"SELECT Name, Description, Origin, NULL AS FormalSpec, Type, 'parameter' AS MemberType, Deprecated, NULL AS Aliases FROM %Dictionary.CompiledParameter WHERE parent->ID = ?";
"FROM %Dictionary.CompiledConstraintMethod WHERE parent->parent->ID = ? AND (parent->parent->ClassType IS NULL OR parent->parent->ClassType != 'datatype') UNION ALL %PARALLEL " +
"SELECT Name, Description, Origin, NULL AS FormalSpec, RuntimeType AS Type, 'property' AS MemberType, Deprecated, Aliases " +
"FROM %Dictionary.CompiledProperty WHERE parent->ID = ? AND (parent->ClassType IS NULL OR parent->ClassType != 'datatype') UNION ALL %PARALLEL " +
"SELECT Name, Description, Origin, NULL AS FormalSpec, Type, 'parameter' AS MemberType, Deprecated, NULL AS Aliases " +
"FROM %Dictionary.CompiledParameter WHERE parent->ID = ? AND (parent->ClassType IS NULL OR parent->ClassType != 'datatype')";
data.parameters = new Array(7).fill(membercontext.baseclass);
}
else {
Expand Down
2 changes: 1 addition & 1 deletion server/src/providers/diagnostic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -775,7 +775,7 @@ export async function onDiagnostics(params: DocumentDiagnosticParams): Promise<D
else if (parsed[i][j].s == ld.cos_mem_attrindex) {
// This is a generic member

if (membercontext.baseclass.substr(0,7) === "%SYSTEM") {
if (membercontext.baseclass.startsWith("%SYSTEM.")) {
// This is always a method
addRangeToMapVal(methods,memberstr,memberrange);
}
Expand Down
107 changes: 25 additions & 82 deletions server/src/providers/typeDefinition.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { TextDocumentPositionParams, Position, Range } from 'vscode-languageserver';
import { getServerSpec, findFullRange, quoteUDLIdentifier, makeRESTRequest, createDefinitionUri, determineVariableClass, getClassMemberContext, getParsedDocument, getTextForUri } from '../utils/functions';
import { getServerSpec, findFullRange, quoteUDLIdentifier, makeRESTRequest, createDefinitionUri, determineVariableClass, getClassMemberContext, getParsedDocument, getTextForUri, currentClass, getMemberType } from '../utils/functions';
import { ServerSpec, QueryData } from '../utils/types';
import { documents } from '../utils/variables';
import * as ld from '../utils/languageDefinitions';
Expand Down Expand Up @@ -27,101 +27,44 @@ export async function onTypeDefinition(params: TextDocumentPositionParams) {
parsed[params.position.line][i].l == ld.cos_langindex && (
parsed[params.position.line][i].s == ld.cos_method_attrindex ||
parsed[params.position.line][i].s == ld.cos_attr_attrindex ||
parsed[params.position.line][i].s == ld.cos_mem_attrindex
parsed[params.position.line][i].s == ld.cos_mem_attrindex ||
parsed[params.position.line][i].s == ld.cos_instvar_attrindex
)
) {
// This token is a method or property

// Get the full text of the member
originrange = findFullRange(params.position.line,parsed,i,symbolstart,symbolend);
let member = quoteUDLIdentifier(doc.getText(originrange),0);
const member = quoteUDLIdentifier(doc.getText(originrange).slice(parsed[params.position.line][i].s == ld.cos_instvar_attrindex ? 2 : 0),0);

// Find the dot token
let dottkn = 0;
for (let tkn = 0; tkn < parsed[params.position.line].length; tkn ++) {
if (parsed[params.position.line][tkn].p >= originrange.start.character) {
break;
let membercontext = {
baseclass: "",
context: ""
};
if (parsed[params.position.line][i].s != ld.cos_instvar_attrindex) {
// Find the dot token
var dottkn = 0;
for (let tkn = 0; tkn < parsed[params.position.line].length; tkn ++) {
if (parsed[params.position.line][tkn].p >= originrange.start.character) {
break;
}
dottkn = tkn;
}
dottkn = tkn;
}

// Get the base class that this member is in
const membercontext = await getClassMemberContext(doc,parsed,dottkn,params.position.line,server);
// Get the base class that this member is in
membercontext = await getClassMemberContext(doc,parsed,dottkn,params.position.line,server);
} else {
membercontext = {
baseclass: currentClass(doc,parsed),
context: ""
};
}
if (membercontext.baseclass === "") {
// If we couldn't determine the class, don't return anything
return null;
}

let data: QueryData = {
query: "",
parameters: []
};
if (parsed[params.position.line][i].s == ld.cos_method_attrindex) {
// This is a method
data.query = "SELECT ReturnType AS Type, Stub FROM %Dictionary.CompiledMethod WHERE parent->ID = ? AND name = ?";
data.parameters = [membercontext.baseclass,member];
}
else if (parsed[params.position.line][i].s == ld.cos_attr_attrindex) {
// This is a property
data.query = "SELECT RuntimeType AS Type, NULL AS Stub FROM %Dictionary.CompiledProperty WHERE parent->ID = ? AND name = ?";
data.parameters = [membercontext.baseclass,member];
}
else {
// This is a generic member
if (membercontext.baseclass.substr(0,7) === "%SYSTEM") {
// This is always a method
data.query = "SELECT ReturnType AS Type, Stub FROM %Dictionary.CompiledMethod WHERE parent->ID = ? AND name = ?";
data.parameters = [membercontext.baseclass,member];
}
else {
// This can be a method or property
data.query = "SELECT ReturnType AS Type, Stub FROM %Dictionary.CompiledMethod WHERE parent->ID = ? AND name = ? UNION ALL ";
data.query = data.query.concat("SELECT RuntimeType AS Type, NULL AS Stub FROM %Dictionary.CompiledProperty WHERE parent->ID = ? AND name = ?");
data.parameters = [membercontext.baseclass,member,membercontext.baseclass,member];
}
}
const respdata = await makeRESTRequest("POST",1,"/action/query",server,data);
if (Array.isArray(respdata?.data?.result?.content) && respdata.data.result.content.length > 0) {
// We got data back

let memobj = respdata.data.result.content[0];
if (respdata.data.result.content[0].Stub !== "") {
// This is a method generated by member inheritance, so we need to get its type from the proper subtable

const stubarr = respdata.data.result.content[0].Stub.split(".");
let stubquery = "";
if (stubarr[2] === "i") {
// This is a method generated from an index
stubquery = "SELECT ReturnType AS Type FROM %Dictionary.CompiledIndexMethod WHERE Name = ? AND parent->parent->ID = ? AND parent->Name = ?";
}
if (stubarr[2] === "q") {
// This is a method generated from a query
stubquery = "SELECT ReturnType AS Type FROM %Dictionary.CompiledQueryMethod WHERE Name = ? AND parent->parent->ID = ? AND parent->Name = ?";
}
if (stubarr[2] === "a") {
// This is a method generated from a property
stubquery = "SELECT ReturnType AS Type FROM %Dictionary.CompiledPropertyMethod WHERE Name = ? AND parent->parent->ID = ? AND parent->Name = ?";
}
if (stubarr[2] === "n") {
// This is a method generated from a constraint
stubquery = "SELECT ReturnType AS Type FROM %Dictionary.CompiledConstraintMethod WHERE Name = ? AND parent->parent->ID = ? AND parent->Name = ?";
}
if (stubquery !== "") {
const stubrespdata = await makeRESTRequest("POST",1,"/action/query",server,{
query: stubquery,
parameters: [stubarr[1],membercontext.baseclass,stubarr[0]]
});
if (Array.isArray(stubrespdata?.data?.result?.content) && stubrespdata.data.result.content.length > 0) {
// We got data back
memobj = stubrespdata.data.result.content[0];
}
}
}

if (memobj.Type !== "") {
targetcls = memobj.Type;
}
}
targetcls = await getMemberType(parsed,params.position.line,i,membercontext.baseclass,member,server);
}
else {
// This token is an ObjectScript variable
Expand Down
Loading

0 comments on commit d333ce6

Please sign in to comment.