2 * Copyright 2000-2016 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.psi.search;
18 import com.intellij.openapi.fileTypes.FileType;
19 import com.intellij.openapi.module.Module;
20 import com.intellij.openapi.module.UnloadedModuleDescription;
21 import com.intellij.openapi.project.Project;
22 import com.intellij.openapi.roots.FileIndexFacade;
23 import com.intellij.openapi.util.Comparing;
24 import com.intellij.openapi.util.text.StringUtil;
25 import com.intellij.openapi.vfs.VirtualFile;
26 import com.intellij.psi.PsiBundle;
27 import com.intellij.psi.PsiElement;
28 import com.intellij.psi.PsiFile;
29 import com.intellij.util.ArrayUtil;
30 import com.intellij.util.Processor;
31 import com.intellij.util.containers.ContainerUtil;
32 import org.jetbrains.annotations.Contract;
33 import org.jetbrains.annotations.NonNls;
34 import org.jetbrains.annotations.NotNull;
35 import org.jetbrains.annotations.Nullable;
39 public abstract class GlobalSearchScope extends SearchScope implements ProjectAwareFileFilter {
40 @Nullable private final Project myProject;
42 protected GlobalSearchScope(@Nullable Project project) {
46 protected GlobalSearchScope() {
52 public Project getProject() {
57 * @return a positive integer (+1), if file1 is located in the classpath before file2,
58 * a negative integer (-1), if file1 is located in the classpath after file2
59 * zero - otherwise or when the files are not comparable.
61 public abstract int compare(@NotNull VirtualFile file1, @NotNull VirtualFile file2);
63 // optimization methods:
65 public abstract boolean isSearchInModuleContent(@NotNull Module aModule);
67 public boolean isSearchInModuleContent(@NotNull Module aModule, boolean testSources) {
68 return isSearchInModuleContent(aModule);
72 public final boolean accept(VirtualFile file) {
73 return contains(file);
76 public abstract boolean isSearchInLibraries();
78 public boolean isForceSearchingInLibrarySources() {
82 public boolean isSearchOutsideRootModel() {
87 * Returns descriptions of unloaded modules content of whose might be included into this scope if they had been loaded. Actually search in
88 * unloaded modules isn't performed, so this method is used to determine whether a warning about possible missing results should be shown.
90 public Collection<UnloadedModuleDescription> getUnloadedModulesBelongingToScope() {
91 return Collections.emptySet();
95 public GlobalSearchScope intersectWith(@NotNull GlobalSearchScope scope) {
96 if (scope == this) return this;
97 if (scope instanceof IntersectionScope && ((IntersectionScope)scope).containsScope(this)) {
100 return new IntersectionScope(this, scope, null);
105 public SearchScope intersectWith(@NotNull SearchScope scope2) {
106 if (scope2 instanceof LocalSearchScope) {
107 LocalSearchScope localScope2 = (LocalSearchScope)scope2;
108 return intersectWith(localScope2);
110 return intersectWith((GlobalSearchScope)scope2);
114 public SearchScope intersectWith(@NotNull LocalSearchScope localScope2) {
115 PsiElement[] elements2 = localScope2.getScope();
116 List<PsiElement> result = new ArrayList<>(elements2.length);
117 for (final PsiElement element2 : elements2) {
118 if (PsiSearchScopeUtil.isInScope(this, element2)) {
119 result.add(element2);
122 return result.isEmpty() ? EMPTY_SCOPE : new LocalSearchScope(result.toArray(new PsiElement[result.size()]), null, localScope2.isIgnoreInjectedPsi());
127 public GlobalSearchScope union(@NotNull SearchScope scope) {
128 if (scope instanceof GlobalSearchScope) return uniteWith((GlobalSearchScope)scope);
129 return union((LocalSearchScope)scope);
133 public GlobalSearchScope union(@NotNull final LocalSearchScope scope) {
134 PsiElement[] localScopeElements = scope.getScope();
135 if (localScopeElements.length == 0) {
138 return new GlobalSearchScope(localScopeElements[0].getProject()) {
140 public boolean contains(@NotNull VirtualFile file) {
141 return GlobalSearchScope.this.contains(file) || scope.isInScope(file);
145 public int compare(@NotNull VirtualFile file1, @NotNull VirtualFile file2) {
146 return GlobalSearchScope.this.contains(file1) && GlobalSearchScope.this.contains(file2) ? GlobalSearchScope.this.compare(file1, file2) : 0;
150 public boolean isSearchInModuleContent(@NotNull Module aModule) {
151 return GlobalSearchScope.this.isSearchInModuleContent(aModule);
155 public boolean isSearchOutsideRootModel() {
156 return GlobalSearchScope.this.isSearchOutsideRootModel();
160 public boolean isSearchInLibraries() {
161 return GlobalSearchScope.this.isSearchInLibraries();
165 public Collection<UnloadedModuleDescription> getUnloadedModulesBelongingToScope() {
166 return GlobalSearchScope.this.getUnloadedModulesBelongingToScope();
171 public String toString() {
172 return "UnionToLocal: (" + GlobalSearchScope.this + ", " + scope + ")";
178 public GlobalSearchScope uniteWith(@NotNull GlobalSearchScope scope) {
179 if (scope == this) return scope;
181 return new UnionScope(this, scope);
185 @Contract(pure = true)
186 public static GlobalSearchScope union(@NotNull GlobalSearchScope[] scopes) {
187 if (scopes.length == 0) {
188 throw new IllegalArgumentException("Empty scope array");
190 if (scopes.length == 1) {
193 return new UnionScope(scopes);
197 public static GlobalSearchScope allScope(@NotNull Project project) {
198 return ProjectScope.getAllScope(project);
202 public static GlobalSearchScope projectScope(@NotNull Project project) {
203 return ProjectScope.getProjectScope(project);
207 public static GlobalSearchScope everythingScope(@NotNull Project project) {
208 return ProjectScope.getEverythingScope(project);
212 public static GlobalSearchScope notScope(@NotNull final GlobalSearchScope scope) {
213 return new NotScope(scope);
216 private static class NotScope extends DelegatingGlobalSearchScope {
217 private NotScope(@NotNull GlobalSearchScope scope) {
222 public boolean contains(@NotNull VirtualFile file) {
223 return !myBaseScope.contains(file);
227 public boolean isSearchInLibraries() {
228 return true; // not (in library A) is perfectly fine to find classes in another library B.
232 public boolean isSearchInModuleContent(@NotNull Module aModule, boolean testSources) {
233 return true; // not (some files in module A) is perfectly fine to find classes in another part of module A.
237 public boolean isSearchInModuleContent(@NotNull Module aModule) {
238 return true; // not (some files in module A) is perfectly fine to find classes in another part of module A.
242 public boolean isSearchOutsideRootModel() {
247 public String toString() {
248 return "NOT: "+myBaseScope;
253 * Returns module scope including sources and tests, excluding libraries and dependencies.
255 * @param module the module to get the scope.
256 * @return scope including sources and tests, excluding libraries and dependencies.
259 public static GlobalSearchScope moduleScope(@NotNull Module module) {
260 return module.getModuleScope();
264 * Returns module scope including sources, tests, and libraries, excluding dependencies.
266 * @param module the module to get the scope.
267 * @return scope including sources, tests, and libraries, excluding dependencies.
270 public static GlobalSearchScope moduleWithLibrariesScope(@NotNull Module module) {
271 return module.getModuleWithLibrariesScope();
275 * Returns module scope including sources, tests, and dependencies, excluding libraries.
277 * @param module the module to get the scope.
278 * @return scope including sources, tests, and dependencies, excluding libraries.
281 public static GlobalSearchScope moduleWithDependenciesScope(@NotNull Module module) {
282 return module.getModuleWithDependenciesScope();
286 public static GlobalSearchScope moduleRuntimeScope(@NotNull Module module, final boolean includeTests) {
287 return module.getModuleRuntimeScope(includeTests);
291 public static GlobalSearchScope moduleWithDependenciesAndLibrariesScope(@NotNull Module module) {
292 return moduleWithDependenciesAndLibrariesScope(module, true);
296 public static GlobalSearchScope moduleWithDependenciesAndLibrariesScope(@NotNull Module module, boolean includeTests) {
297 return module.getModuleWithDependenciesAndLibrariesScope(includeTests);
301 public static GlobalSearchScope moduleWithDependentsScope(@NotNull Module module) {
302 return module.getModuleWithDependentsScope();
306 public static GlobalSearchScope moduleTestsWithDependentsScope(@NotNull Module module) {
307 return module.getModuleTestsWithDependentsScope();
311 public static GlobalSearchScope fileScope(@NotNull PsiFile psiFile) {
312 return new FileScope(psiFile.getProject(), psiFile.getVirtualFile());
316 public static GlobalSearchScope fileScope(@NotNull Project project, final VirtualFile virtualFile) {
317 return fileScope(project, virtualFile, null);
321 public static GlobalSearchScope fileScope(@NotNull Project project, @Nullable VirtualFile virtualFile, @Nullable final String displayName) {
322 return new FileScope(project, virtualFile) {
325 public String getDisplayName() {
326 return displayName == null ? super.getDisplayName() : displayName;
332 * Please consider using {@link this#filesWithLibrariesScope} or {@link this#filesWithoutLibrariesScope} for optimization
335 public static GlobalSearchScope filesScope(@NotNull Project project, @NotNull Collection<VirtualFile> files) {
336 return filesScope(project, files, null);
340 * Optimization. By default FilesScope makes a decision about searching in libraries by checking that
341 * at least one file is placed out of module roots. So if you're sure about files placement you can explicitly say FilesScope whether
342 * it should include libraries or not in order to avoid checking each file.
343 * Also, if you have a lot of files it might be faster to always search in libraries.
346 public static GlobalSearchScope filesWithoutLibrariesScope(@NotNull Project project, @NotNull Collection<VirtualFile> files) {
347 if (files.isEmpty()) return EMPTY_SCOPE;
348 return new FilesScope(project, files, false, false);
352 public static GlobalSearchScope filesWithLibrariesScope(@NotNull Project project, @NotNull Collection<VirtualFile> files) {
353 return filesWithLibrariesScope(project, files, false);
360 public static GlobalSearchScope filesWithLibrariesScope(@NotNull Project project, @NotNull Collection<VirtualFile> files,
361 boolean searchOutsideRootModel) {
362 if (files.isEmpty()) return EMPTY_SCOPE;
363 return new FilesScope(project, files, true, searchOutsideRootModel);
367 * Please consider using {@link this#filesWithLibrariesScope} or {@link this#filesWithoutLibrariesScope} for optimization
370 public static GlobalSearchScope filesScope(@NotNull Project project, @NotNull Collection<VirtualFile> files, @Nullable final String displayName) {
371 if (files.isEmpty()) return EMPTY_SCOPE;
372 return files.size() == 1? fileScope(project, files.iterator().next(), displayName) : new FilesScope(project, files) {
375 public String getDisplayName() {
376 return displayName == null ? super.getDisplayName() : displayName;
381 private static class IntersectionScope extends GlobalSearchScope {
382 private final GlobalSearchScope myScope1;
383 private final GlobalSearchScope myScope2;
384 private final String myDisplayName;
386 private IntersectionScope(@NotNull GlobalSearchScope scope1, @NotNull GlobalSearchScope scope2, String displayName) {
387 super(scope1.getProject() == null ? scope2.getProject() : scope1.getProject());
390 myDisplayName = displayName;
395 public GlobalSearchScope intersectWith(@NotNull GlobalSearchScope scope) {
396 return containsScope(scope) ? this : new IntersectionScope(this, scope, null);
399 private boolean containsScope(@NotNull GlobalSearchScope scope) {
400 if (myScope1.equals(scope) || myScope2.equals(scope) || equals(scope)) return true;
401 if (myScope1 instanceof IntersectionScope && ((IntersectionScope)myScope1).containsScope(scope)) return true;
402 if (myScope2 instanceof IntersectionScope && ((IntersectionScope)myScope2).containsScope(scope)) return true;
408 public String getDisplayName() {
409 if (myDisplayName == null) {
410 return PsiBundle.message("psi.search.scope.intersection", myScope1.getDisplayName(), myScope2.getDisplayName());
412 return myDisplayName;
416 public boolean contains(@NotNull VirtualFile file) {
417 return myScope1.contains(file) && myScope2.contains(file);
421 public int compare(@NotNull VirtualFile file1, @NotNull VirtualFile file2) {
422 int res1 = myScope1.compare(file1, file2);
423 int res2 = myScope2.compare(file1, file2);
425 if (res1 == 0) return res2;
426 if (res2 == 0) return res1;
428 res1 /= Math.abs(res1);
429 res2 /= Math.abs(res2);
430 if (res1 == res2) return res1;
436 public boolean isSearchInModuleContent(@NotNull Module aModule) {
437 return myScope1.isSearchInModuleContent(aModule) && myScope2.isSearchInModuleContent(aModule);
441 public boolean isSearchInModuleContent(@NotNull final Module aModule, final boolean testSources) {
442 return myScope1.isSearchInModuleContent(aModule, testSources) && myScope2.isSearchInModuleContent(aModule, testSources);
446 public boolean isSearchInLibraries() {
447 return myScope1.isSearchInLibraries() && myScope2.isSearchInLibraries();
451 public boolean isSearchOutsideRootModel() {
452 return myScope1.isSearchOutsideRootModel() && myScope2.isSearchOutsideRootModel();
456 public Collection<UnloadedModuleDescription> getUnloadedModulesBelongingToScope() {
457 return ContainerUtil.intersection(myScope1.getUnloadedModulesBelongingToScope(), myScope2.getUnloadedModulesBelongingToScope());
461 public boolean equals(Object o) {
462 if (this == o) return true;
463 if (!(o instanceof IntersectionScope)) return false;
465 IntersectionScope that = (IntersectionScope)o;
467 return myScope1.equals(that.myScope1) && myScope2.equals(that.myScope2);
471 public int hashCode() {
472 return 31 * myScope1.hashCode() + myScope2.hashCode();
477 public String toString() {
478 return "Intersection: (" + myScope1 + ", " + myScope2 + ")";
482 private static class UnionScope extends GlobalSearchScope {
483 private final GlobalSearchScope[] myScopes;
484 private final int myNestingLevel;
486 private UnionScope(@NotNull GlobalSearchScope scope1, @NotNull GlobalSearchScope scope2) {
487 this(new GlobalSearchScope[]{scope1, scope2});
490 private UnionScope(@NotNull GlobalSearchScope[] scopes) {
491 super(ContainerUtil.getFirstItem(ContainerUtil.mapNotNull(scopes, scope -> scope.getProject()), null));
492 if (scopes.length <= 1) throw new IllegalArgumentException("Too few scopes: "+ Arrays.asList(scopes));
494 final int[] nested = {0};
495 ContainerUtil.process(scopes, new Processor<GlobalSearchScope>() {
497 public boolean process(GlobalSearchScope scope) {
498 nested[0] = Math.max(nested[0], scope instanceof UnionScope ? ((UnionScope)scope).myNestingLevel : 0);
502 myNestingLevel = 1 + nested[0];
503 if (myNestingLevel > 1000) {
504 throw new IllegalStateException("Too many scopes combined: " + myNestingLevel + StringUtil.last(toString(), 500, true));
510 public String getDisplayName() {
511 return PsiBundle.message("psi.search.scope.union", myScopes[0].getDisplayName(), myScopes[1].getDisplayName());
515 public boolean contains(@NotNull final VirtualFile file) {
516 return ContainerUtil.find(myScopes, scope -> scope.contains(file)) != null;
520 public boolean isSearchOutsideRootModel() {
521 return ContainerUtil.find(myScopes, scope -> scope.isSearchOutsideRootModel()) != null;
525 public Collection<UnloadedModuleDescription> getUnloadedModulesBelongingToScope() {
526 Set<UnloadedModuleDescription> result = new LinkedHashSet<>();
527 for (GlobalSearchScope scope : myScopes) {
528 result.addAll(scope.getUnloadedModulesBelongingToScope());
534 public int compare(@NotNull final VirtualFile file1, @NotNull final VirtualFile file2) {
535 final int[] result = {0};
536 ContainerUtil.process(myScopes, scope -> {
537 int res1 = scope.contains(file1) && scope.contains(file2) ? scope.compare(file1, file2) : 0;
538 if (result[0] == 0) {
542 if (result[0] > 0 != res1 > 0) {
552 public boolean isSearchInModuleContent(@NotNull final Module module) {
553 return ContainerUtil.find(myScopes, scope -> scope.isSearchInModuleContent(module)) != null;
557 public boolean isSearchInModuleContent(@NotNull final Module module, final boolean testSources) {
558 return ContainerUtil.find(myScopes, scope -> scope.isSearchInModuleContent(module, testSources)) != null;
562 public boolean isSearchInLibraries() {
563 return ContainerUtil.find(myScopes, scope -> scope.isSearchInLibraries()) != null;
567 public boolean equals(Object o) {
568 if (this == o) return true;
569 if (!(o instanceof UnionScope)) return false;
571 UnionScope that = (UnionScope)o;
573 return new HashSet<>(Arrays.asList(myScopes)).equals(new HashSet<>(Arrays.asList(that.myScopes)));
577 public int hashCode() {
578 return Arrays.hashCode(myScopes);
583 public String toString() {
584 return "Union: (" + StringUtil.join(Arrays.asList(myScopes), ",") + ")";
589 public GlobalSearchScope uniteWith(@NotNull GlobalSearchScope scope) {
590 if (scope instanceof UnionScope) {
591 GlobalSearchScope[] newScopes = ArrayUtil.mergeArrays(myScopes, ((UnionScope)scope).myScopes);
592 return new UnionScope(newScopes);
594 return super.uniteWith(scope);
600 public static GlobalSearchScope getScopeRestrictedByFileTypes(@NotNull GlobalSearchScope scope, @NotNull FileType... fileTypes) {
601 if (scope == EMPTY_SCOPE) {
604 if (fileTypes.length == 0) throw new IllegalArgumentException("empty fileTypes");
605 return new FileTypeRestrictionScope(scope, fileTypes);
608 private static class FileTypeRestrictionScope extends DelegatingGlobalSearchScope {
609 private final FileType[] myFileTypes;
611 private FileTypeRestrictionScope(@NotNull GlobalSearchScope scope, @NotNull FileType[] fileTypes) {
613 myFileTypes = fileTypes;
617 public boolean contains(@NotNull VirtualFile file) {
618 if (!super.contains(file)) return false;
620 final FileType fileType = file.getFileType();
621 for (FileType otherFileType : myFileTypes) {
622 if (fileType.equals(otherFileType)) return true;
630 public GlobalSearchScope intersectWith(@NotNull GlobalSearchScope scope) {
631 if (scope instanceof FileTypeRestrictionScope) {
632 FileTypeRestrictionScope restrict = (FileTypeRestrictionScope)scope;
633 if (restrict.myBaseScope == myBaseScope) {
634 List<FileType> intersection = new ArrayList<>(Arrays.asList(restrict.myFileTypes));
635 intersection.retainAll(Arrays.asList(myFileTypes));
636 return new FileTypeRestrictionScope(myBaseScope, intersection.toArray(new FileType[intersection.size()]));
639 return super.intersectWith(scope);
644 public GlobalSearchScope uniteWith(@NotNull GlobalSearchScope scope) {
645 if (scope instanceof FileTypeRestrictionScope) {
646 FileTypeRestrictionScope restrict = (FileTypeRestrictionScope)scope;
647 if (restrict.myBaseScope == myBaseScope) {
648 return new FileTypeRestrictionScope(myBaseScope, ArrayUtil.mergeArrays(myFileTypes, restrict.myFileTypes));
651 return super.uniteWith(scope);
655 public boolean equals(Object o) {
656 if (this == o) return true;
657 if (!(o instanceof FileTypeRestrictionScope)) return false;
658 if (!super.equals(o)) return false;
660 FileTypeRestrictionScope that = (FileTypeRestrictionScope)o;
662 return Arrays.equals(myFileTypes, that.myFileTypes);
666 public int hashCode() {
667 int result = super.hashCode();
668 result = 31 * result + Arrays.hashCode(myFileTypes);
673 public String toString() {
674 return "(restricted by file types: "+Arrays.asList(myFileTypes)+" in "+ myBaseScope + ")";
678 private static class EmptyScope extends GlobalSearchScope {
680 public boolean contains(@NotNull VirtualFile file) {
685 public int compare(@NotNull VirtualFile file1, @NotNull VirtualFile file2) {
690 public boolean isSearchInModuleContent(@NotNull Module aModule) {
695 public boolean isSearchInLibraries() {
701 public GlobalSearchScope intersectWith(@NotNull final GlobalSearchScope scope) {
707 public GlobalSearchScope uniteWith(@NotNull final GlobalSearchScope scope) {
712 public String toString() {
717 public static final GlobalSearchScope EMPTY_SCOPE = new EmptyScope();
719 private static class FileScope extends GlobalSearchScope implements Iterable<VirtualFile> {
720 private final VirtualFile myVirtualFile; // files can be out of project roots
721 private final Module myModule;
722 private final boolean mySearchOutsideContent;
724 private FileScope(@NotNull Project project, @Nullable VirtualFile virtualFile) {
726 myVirtualFile = virtualFile;
727 final FileIndexFacade facade = FileIndexFacade.getInstance(project);
728 myModule = virtualFile == null || project.isDefault() ? null : facade.getModuleForFile(virtualFile);
729 mySearchOutsideContent = virtualFile != null && myModule == null && !facade.isInLibraryClasses(virtualFile) && !facade.isInLibrarySource(virtualFile);
733 public boolean contains(@NotNull VirtualFile file) {
734 return Comparing.equal(myVirtualFile, file);
738 public int compare(@NotNull VirtualFile file1, @NotNull VirtualFile file2) {
743 public boolean isSearchInModuleContent(@NotNull Module aModule) {
744 return aModule == myModule;
748 public boolean isSearchInLibraries() {
749 return myModule == null;
753 public String toString() {
754 return "File :"+myVirtualFile;
759 public Iterator<VirtualFile> iterator() {
760 return Collections.singletonList(myVirtualFile).iterator();
764 public boolean isSearchOutsideRootModel() {
765 return mySearchOutsideContent;
769 public static class FilesScope extends GlobalSearchScope implements Iterable<VirtualFile> {
770 private final Collection<VirtualFile> myFiles;
771 private final boolean mySearchOutsideRootModel;
772 private volatile Boolean myHasFilesOutOfProjectRoots;
775 * @deprecated use {@link GlobalSearchScope#filesScope(Project, Collection)}
777 public FilesScope(@Nullable Project project, @NotNull Collection<VirtualFile> files) {
778 this(project, files, null, false);
782 private FilesScope(@Nullable Project project, @NotNull Collection<VirtualFile> files, @Nullable Boolean hasFilesOutOfProjectRoots,
783 boolean searchOutsideRootModel) {
786 mySearchOutsideRootModel = searchOutsideRootModel;
787 myHasFilesOutOfProjectRoots = hasFilesOutOfProjectRoots;
791 public boolean contains(@NotNull final VirtualFile file) {
792 return myFiles.contains(file);
796 public int compare(@NotNull final VirtualFile file1, @NotNull final VirtualFile file2) {
801 public boolean isSearchInModuleContent(@NotNull Module aModule) {
806 public boolean isSearchInLibraries() {
807 return hasFilesOutOfProjectRoots();
811 public boolean equals(Object o) {
812 return this == o || o instanceof FilesScope && myFiles.equals(((FilesScope)o).myFiles);
816 public int hashCode() {
817 return myFiles.hashCode();
820 private boolean hasFilesOutOfProjectRoots() {
821 Boolean result = myHasFilesOutOfProjectRoots;
822 if (result == null) {
823 Project project = getProject();
824 myHasFilesOutOfProjectRoots = result =
825 project != null && !project.isDefault() &&
826 myFiles.stream().anyMatch(file -> FileIndexFacade.getInstance(project).getModuleForFile(file) == null);
832 public String toString() {
833 List<VirtualFile> files = myFiles.size() <= 20 ? new ArrayList<>(myFiles) : new ArrayList<>(myFiles).subList(0, 20);
834 return "Files: ("+ files +"); search in libraries: " + (myHasFilesOutOfProjectRoots != null ? myHasFilesOutOfProjectRoots : "unknown");
839 public Iterator<VirtualFile> iterator() {
840 return myFiles.iterator();
844 public boolean isSearchOutsideRootModel() {
845 return mySearchOutsideRootModel;