/// <summary> /// Returns the position and the kind of the closest specialization preceding the given position, /// and the name of the callable it belongs to as well as its position as Nullable. /// Returns null if the given file or position is null, or if no preceding callable can be found (e.g. because the callable name is invalid). /// If a callable name but no specializations (preceding or otherwise) within that callable can be found, /// assumes that the correct specialization is an auto-inserted default body, /// and returns QsBody as well as the start position of the callable declaration along with the callable name and its position. /// If a callable name as well as existing specializations can be found, but no specialization precedes the given position, /// returns null for the specialization kind as well as for its position. /// </summary> public static ((string, Position), (QsSpecializationKind?, Position?))? TryGetClosestSpecialization( this FileContentManager file, Position pos) { QsSpecializationKind?GetSpecializationKind(CodeFragment fragment) { var specDecl = fragment.Kind.DeclaredSpecialization(); if (specDecl.IsNull) { return(null); } var((kind, gen), typeArgs) = specDecl.Item; // note: if we want to support type specializations we need to compute the signature of the spec to find the right one return(kind); } if (file == null || pos == null || !file.ContainsPosition(pos)) { return(null); } file.SyncRoot.EnterReadLock(); try { var declarations = file.CallableDeclarationTokens(); var precedingDecl = declarations.TakeWhile(tIndex => tIndex.GetFragment().Range.Start < pos); if (!precedingDecl.Any()) { return(null); } var closestCallable = precedingDecl.Last(); var callablePosition = closestCallable.GetFragment().Range.Start; var callableName = closestCallable.GetFragment().Kind.DeclaredCallableName(null); if (callableName == null) { return(null); } var specializations = FileHeader.FilterCallableSpecializations(closestCallable.GetChildren(deep: false).Select(tIndex => tIndex.GetFragment())); var precedingSpec = specializations.TakeWhile(fragment => fragment.Range.Start < pos); var lastPreceding = precedingSpec.Any() ? precedingSpec.Last() : null; if (specializations.Any() && lastPreceding == null) { // the given position is within a callable declaration return((callableName, callablePosition), (null, null)); } return(lastPreceding == null ? ((callableName, callablePosition), (QsSpecializationKind.QsBody, callablePosition)) : ((callableName, callablePosition), (GetSpecializationKind(lastPreceding), lastPreceding.Range.Start))); } finally { file.SyncRoot.ExitReadLock(); } }