@Nonnull
		protected List<AssemblerCompletion> complete(@Nonnull Workspace workspace, @Nonnull InheritanceGraph inheritanceGraph,
		                                             @Nonnull Function<ClassInfo, Stream<? extends ClassMember>> classMemberLookup) {
			// Skip if no owner type specified.
			if (owner == null) return Collections.emptyList();

			// Complete class names if the '.' separator is not seen, otherwise suggest any member in the class.
			if (name == null && desc == null) {
				// TODO: startsWith is nice, but mirroring IntelliJ would be nicer
				//  - Should be able to complete "new Strin" to "new java/lang/String"
				//    - But out completion system just appends text so we'd need to do some more work there.
				//  - Also like IntelliJ we should prioritize common completions over niche ones
				//    - Also not something that happens right here
				if (partialInput.endsWith(".")) {
					ClassPathNode ownerPath = workspace.findClass(owner);
					if (ownerPath != null) {
						List<ClassMember> items = new ArrayList<>();
						InheritanceVertex vertex = inheritanceGraph.getVertex(owner);
						if (vertex != null)
							for (InheritanceVertex parent : vertex.getAllParents())
								classMemberLookup.apply(parent.getValue()).forEach(items::add);
						classMemberLookup.apply(ownerPath.getValue()).forEach(items::add);
						return items.stream()
								.map(ownerPath::child)
								.map(AssemblerCompletion.Member::new)
								.map(AssemblerCompletion.class::cast)
								.toList();
					}
				} else {
					// No separator, user is still typing out a class name.
					return workspace.findClasses(c -> c.getName().startsWith(owner)).stream()
							.map(AssemblerCompletion.Type::new)
							.map(AssemblerCompletion.class::cast)
							.toList();
				}
			}

			// Complete member name/descriptors if we have "owner.x"
			if (name != null && desc == null) {
				ClassPathNode ownerPath = workspace.findClass(owner);
				if (ownerPath != null) {
					List<ClassMember> items = new ArrayList<>();
					InheritanceVertex vertex = inheritanceGraph.getVertex(owner);
					Predicate<ClassMember> filter = member -> member.getName().startsWith(name);
					if (vertex != null)
						for (InheritanceVertex parent : vertex.getAllParents())
							classMemberLookup.apply(parent.getValue())
									.filter(filter)
									.forEach(items::add);
					classMemberLookup.apply(ownerPath.getValue())
							.filter(filter)
							.forEach(items::add);
					return items.stream()
							.map(ownerPath::child)
							.map(AssemblerCompletion.Member::new)
							.map(AssemblerCompletion.class::cast)
							.toList();
				}
			}

			// Complete member descriptors if we have "owner.name"
			if (desc != null) {
				ClassPathNode ownerPath = workspace.findClass(owner);
				if (ownerPath != null) {
					List<ClassMember> items = new ArrayList<>();
					InheritanceVertex vertex = inheritanceGraph.getVertex(owner);
					Predicate<ClassMember> filter = member -> member.getName().equals(name) && member.getDescriptor().startsWith(desc);
					if (vertex != null)
						for (InheritanceVertex parent : vertex.getAllParents())
							classMemberLookup.apply(parent.getValue())
									.filter(filter)
									.forEach(items::add);
					classMemberLookup.apply(ownerPath.getValue())
							.filter(filter)
							.forEach(items::add);
					return items.stream()
							.map(ownerPath::child)
							.map(AssemblerCompletion.Member::new)
							.map(AssemblerCompletion.class::cast)
							.toList();
				}
			}

			return Collections.emptyList();
		}
@param partialInput
		Partial input text.
@param owner
		Reference owner text. May be partial if the type/name are {@code null}.
@param name
		Reference name text. May be partial if the type is {@code null}.
@param desc
		Reference type text. May be partial.
recaf-ui/src/main/java/software/coley/recaf/ui/control/richtext/suggest/AssemblerTabCompleter.java