Skip to content

Enhance Completion feature in VSCE #1289

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 15, 2024
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
5 changes: 5 additions & 0 deletions jac/jaclang/compiler/symtable.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ def sym_dotted_name(self) -> str:
out.reverse()
return ".".join(out)

@property
def fetch_sym_tab(self) -> Optional[SymbolTable]:
"""Get symbol table."""
return self.parent_tab.find_scope(self.sym_name)

def add_defn(self, node: ast.NameAtom) -> None:
"""Add defn."""
self.defn.append(node)
Expand Down
68 changes: 63 additions & 5 deletions jac/jaclang/langserve/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
get_location_range,
get_symbols_for_outline,
parse_symbol_path,
resolve_completion_symbol_table,
)
from jaclang.vendor.pygls import uris
from jaclang.vendor.pygls.server import LanguageServer
Expand Down Expand Up @@ -188,14 +187,73 @@ def get_completion(
if not node_selected
else node_selected.sym_tab
)
current_tab = self.modules[file_path].ir._sym_tab
current_symbol_table = mod_tab

if completion_trigger == ".":
if current_symbol_path:
completion_items = resolve_completion_symbol_table(
mod_tab, current_symbol_path, current_tab
)
temp_tab = mod_tab
for symbol in current_symbol_path:
if symbol == "self":
is_ability_def = (
temp_tab.owner
if isinstance(temp_tab.owner, ast.AbilityDef)
else temp_tab.owner.find_parent_of_type(ast.AbilityDef)
)
if not is_ability_def:
archi_owner = mod_tab.owner.find_parent_of_type(
ast.Architype
)
temp_tab = (
archi_owner._sym_tab
if archi_owner and archi_owner._sym_tab
else mod_tab
)
continue
else:
archi_owner = (
(
is_ability_def.decl_link.find_parent_of_type(
ast.Architype
)
)
if is_ability_def.decl_link
else None
)
temp_tab = (
archi_owner.sym_tab
if archi_owner and archi_owner.sym_tab
else temp_tab
)
continue
symb = temp_tab.lookup(symbol)
if symb:
fetc_tab = symb.fetch_sym_tab
if fetc_tab:
temp_tab = fetc_tab
else:
temp_tab = (
symb.defn[0].type_sym_tab
if symb.defn[0].type_sym_tab
else temp_tab
)
else:
break
completion_items = collect_all_symbols_in_scope(temp_tab, up_tree=False)
if (
isinstance(temp_tab.owner, ast.Architype)
and temp_tab.owner.base_classes
):
base = []
for base_name in temp_tab.owner.base_classes.items:
if isinstance(base_name, ast.Name) and base_name.sym:
base.append(base_name.sym)
for base_class_symbol in base:
if base_class_symbol.fetch_sym_tab:
completion_items += collect_all_symbols_in_scope(
base_class_symbol.fetch_sym_tab,
up_tree=False,
)

else:
completion_items = []
else:
Expand Down
113 changes: 0 additions & 113 deletions jac/jaclang/langserve/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,119 +332,6 @@ def parse_symbol_path(text: str, dot_position: int) -> list[str]:
return all_words


def resolve_symbol_path(sym_name: str, node_tab: SymbolTable) -> str:
"""Resolve symbol path."""
visited = set()
current_tab: Optional[SymbolTable] = node_tab

while current_tab is not None and current_tab not in visited:
visited.add(current_tab)
for name, symbol in current_tab.tab.items():
if name not in dir(builtins) and name == sym_name:
path = symbol.defn[0]._sym_type
if symbol.sym_type == SymbolType.ENUM_ARCH:
if isinstance(current_tab.owner, ast.Module):
return current_tab.owner.name + "." + sym_name
elif isinstance(current_tab.owner, ast.AstSymbolNode):
return current_tab.owner.name_spec._sym_type + "." + sym_name
return path
current_tab = current_tab.parent if current_tab.parent != current_tab else None
return ""


def find_symbol_table(path: str, current_tab: Optional[SymbolTable]) -> SymbolTable:
"""Find symbol table."""
path = path.lstrip(".")
current_table = current_tab
if current_table:
for segment in path.split("."):
current_table = next(
(
child_table
for child_table in current_table.kid
if child_table.name == segment
),
current_table,
)
if current_table:
return current_table
raise ValueError(f"Symbol table not found for path {path}")


def resolve_completion_symbol_table(
mod_tab: SymbolTable,
current_symbol_path: list[str],
current_tab: Optional[SymbolTable],
) -> list[lspt.CompletionItem]:
"""Resolve symbol table for completion items."""
current_symbol_table = mod_tab
for obj in current_symbol_path:
if obj == "self":
try:
try:
is_abilitydef = (
mod_tab.owner
if isinstance(mod_tab.owner, ast.AbilityDef)
else mod_tab.owner.parent_of_type(ast.AbilityDef)
)
archi_owner = (
(is_abilitydef.decl_link.parent_of_type(ast.Architype))
if is_abilitydef.decl_link
else None
)
current_symbol_table = (
archi_owner._sym_tab
if archi_owner and archi_owner._sym_tab
else mod_tab
)
continue

except ValueError:
pass
archi_owner = mod_tab.owner.parent_of_type(ast.Architype)
current_symbol_table = (
archi_owner._sym_tab
if archi_owner and archi_owner._sym_tab
else mod_tab
)
except ValueError:
pass
else:
path: str = resolve_symbol_path(obj, current_symbol_table)
if path:
current_symbol_table = find_symbol_table(path, current_tab)
else:
if (
isinstance(current_symbol_table.owner, ast.Architype)
and current_symbol_table.owner.base_classes
):
for base_name in current_symbol_table.owner.base_classes.items:
if isinstance(base_name, ast.Name) and base_name.sym:
path = base_name.sym.sym_dotted_name + "." + obj
current_symbol_table = find_symbol_table(path, current_tab)
if (
isinstance(current_symbol_table.owner, ast.Architype)
and current_symbol_table.owner.base_classes
):
base = []
for base_name in current_symbol_table.owner.base_classes.items:
if isinstance(base_name, ast.Name) and base_name.sym:
base.append(base_name.sym.sym_dotted_name)
for base_ in base:
completion_items = collect_all_symbols_in_scope(
find_symbol_table(base_, current_tab),
up_tree=False,
)
else:
completion_items = []
if isinstance(current_symbol_table.owner, (ast.Ability, ast.AbilityDef)):
return completion_items
completion_items.extend(
collect_all_symbols_in_scope(current_symbol_table, up_tree=False)
)
return completion_items


def get_token_start(
token_index: int | None, sem_tokens: list[int]
) -> tuple[int, int, int]:
Expand Down
Loading