mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
separate single and multi root injected file view providers
GitOrigin-RevId: 2019303584a968437956c9e153df851606f71313
This commit is contained in:
committed by
intellij-monorepo-bot
parent
4d34b046a6
commit
d21c55c102
+50
-147
@@ -1,19 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.psi.impl.source.tree.injected;
|
||||
|
||||
import com.intellij.injected.editor.DocumentWindow;
|
||||
@@ -21,56 +6,32 @@ import com.intellij.injected.editor.VirtualFileWindow;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.lang.injection.InjectedLanguageManager;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.Segment;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.FreeThreadedFileViewProvider;
|
||||
import com.intellij.psi.impl.PsiManagerEx;
|
||||
import com.intellij.psi.impl.source.PsiFileImpl;
|
||||
import com.intellij.psi.templateLanguages.TemplateLanguageFileViewProvider;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.apache.commons.lang.NotImplementedException;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @deprecated Use methods from {@link InjectedLanguageManager} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public class InjectedFileViewProvider extends MultiplePsiFilesPerDocumentFileViewProvider implements FreeThreadedFileViewProvider {
|
||||
private final Object myLock = new Object();
|
||||
private final DocumentWindowImpl myDocumentWindow;
|
||||
private final Language myLanguage;
|
||||
private static final ThreadLocal<Boolean> disabledTemporarily = ThreadLocal.withInitial(() -> false);
|
||||
private boolean myPatchingLeaves;
|
||||
protected final AbstractFileViewProvider myOriginalProvider;
|
||||
public interface InjectedFileViewProvider extends FileViewProvider, FreeThreadedFileViewProvider {
|
||||
Key<Language> LANGUAGE_FOR_INJECTED_COPY_KEY = Key.create("LANGUAGE_FOR_INJECTED_COPY_KEY");
|
||||
ThreadLocal<Boolean> disabledTemporarily = ThreadLocal.withInitial(() -> false);
|
||||
|
||||
private InjectedFileViewProvider(@NotNull PsiManager psiManager,
|
||||
@NotNull VirtualFileWindow virtualFile,
|
||||
@NotNull DocumentWindowImpl documentWindow,
|
||||
@NotNull Language language, AbstractFileViewProvider original) {
|
||||
super(psiManager, (VirtualFile)virtualFile, true);
|
||||
myDocumentWindow = documentWindow;
|
||||
myLanguage = language;
|
||||
myOriginalProvider = original;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rootChanged(@NotNull PsiFile psiFile) {
|
||||
super.rootChanged(psiFile);
|
||||
default void rootChangedImpl(@NotNull PsiFile psiFile) {
|
||||
if (!isPhysical()) return; // injected PSI change happened inside reparse; ignore
|
||||
if (myPatchingLeaves) return;
|
||||
if (getPatchingLeaves()) return;
|
||||
|
||||
DocumentWindowImpl documentWindow = myDocumentWindow;
|
||||
DocumentWindowImpl documentWindow = getDocument();
|
||||
List<PsiLanguageInjectionHost.Shred> shreds = documentWindow.getShreds();
|
||||
assert documentWindow.getHostRanges().length == shreds.size();
|
||||
String[] changes = documentWindow.calculateMinEditSequence(psiFile.getNode().getText());
|
||||
@@ -88,8 +49,7 @@ public class InjectedFileViewProvider extends MultiplePsiFilesPerDocumentFileVie
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileViewProvider clone() {
|
||||
default FileViewProvider cloneImpl() {
|
||||
final DocumentWindow oldDocumentWindow = ((VirtualFileWindow)getVirtualFile()).getDocumentWindow();
|
||||
Document hostDocument = oldDocumentWindow.getDelegate();
|
||||
final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getManager().getProject());
|
||||
@@ -121,47 +81,20 @@ public class InjectedFileViewProvider extends MultiplePsiFilesPerDocumentFileVie
|
||||
return provider.get();
|
||||
}
|
||||
|
||||
static Key<Language> LANGUAGE_FOR_INJECTED_COPY_KEY = Key.create("LANGUAGE_FOR_INJECTED_COPY_KEY");
|
||||
// returns true if shreds were set, false if old ones were reused
|
||||
boolean setShreds(@NotNull Place newShreds) {
|
||||
synchronized (myLock) {
|
||||
Place oldShreds = myDocumentWindow.getShreds();
|
||||
default boolean setShreds(@NotNull Place newShreds) {
|
||||
synchronized (getLock()) {
|
||||
Place oldShreds = getDocument().getShreds();
|
||||
// try to reuse shreds, otherwise there are too many range markers disposals/re-creations
|
||||
if (same(oldShreds, newShreds)) {
|
||||
return false;
|
||||
}
|
||||
myDocumentWindow.setShreds(newShreds);
|
||||
getDocument().setShreds(newShreds);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Project getProject() {
|
||||
return getManager().getProject();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Language getBaseLanguage() {
|
||||
return myLanguage;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<Language> getLanguages() {
|
||||
FileViewProvider original = myOriginalProvider;
|
||||
Set<Language> languages = original.getLanguages();
|
||||
Language base = original.getBaseLanguage();
|
||||
return ContainerUtil.map2Set(languages, (language) -> language == base ? myLanguage : language);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected MultiplePsiFilesPerDocumentFileViewProvider cloneInner(@NotNull VirtualFile fileCopy) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private static boolean same(Place oldShreds, Place newShreds) {
|
||||
static boolean same(Place oldShreds, Place newShreds) {
|
||||
if (oldShreds == newShreds) return true;
|
||||
if (oldShreds.size() != newShreds.size()) return false;
|
||||
for (int i = 0; i < oldShreds.size(); i++) {
|
||||
@@ -172,36 +105,12 @@ public class InjectedFileViewProvider extends MultiplePsiFilesPerDocumentFileVie
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean isValid() {
|
||||
return getShreds().isValid();
|
||||
}
|
||||
|
||||
boolean isDisposed() {
|
||||
return getProject().isDisposed();
|
||||
}
|
||||
|
||||
Place getShreds() {
|
||||
return myDocumentWindow.getShreds();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public DocumentWindow getDocument() {
|
||||
return myDocumentWindow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEventSystemEnabled() {
|
||||
return !disabledTemporarily.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPhysical() {
|
||||
default boolean isPhysicalImpl() {
|
||||
return isEventSystemEnabled();
|
||||
}
|
||||
|
||||
void performNonPhysically(Runnable runnable) {
|
||||
synchronized (myLock) {
|
||||
default void performNonPhysically(Runnable runnable) {
|
||||
synchronized (getLock()) {
|
||||
disabledTemporarily.set(true);
|
||||
try {
|
||||
runnable.run();
|
||||
@@ -212,56 +121,50 @@ public class InjectedFileViewProvider extends MultiplePsiFilesPerDocumentFileVie
|
||||
}
|
||||
}
|
||||
|
||||
@NonNls
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Injected file '"+getVirtualFile().getName()+"' " + (isValid() ? "" : " invalid") + (isPhysical() ? "" : " nonphysical");
|
||||
}
|
||||
|
||||
void doNotInterruptMeWhileImPatchingLeaves(@NotNull Runnable runnable) {
|
||||
myPatchingLeaves = true;
|
||||
default void doNotInterruptMeWhileImPatchingLeaves(@NotNull Runnable runnable) {
|
||||
setPatchingLeaves(true);
|
||||
try {
|
||||
runnable.run();
|
||||
}
|
||||
finally {
|
||||
myPatchingLeaves = false;
|
||||
setPatchingLeaves(false);
|
||||
}
|
||||
}
|
||||
|
||||
public final void forceCachedPsi(@NotNull PsiFile psiFile) {
|
||||
myRoots.put(psiFile.getLanguage(), (PsiFileImpl)psiFile);
|
||||
getManager().getFileManager().setViewProvider(getVirtualFile(), this);
|
||||
void setPatchingLeaves(boolean value);
|
||||
boolean getPatchingLeaves();
|
||||
void forceCachedPsi(@NotNull PsiFile file);
|
||||
Object getLock();
|
||||
|
||||
default boolean isValid() {
|
||||
return getShreds().isValid();
|
||||
}
|
||||
|
||||
static InjectedFileViewProvider create(PsiManagerEx manager,
|
||||
VirtualFileWindowImpl file,
|
||||
DocumentWindowImpl window,
|
||||
Language language) {
|
||||
default boolean isDisposed() {
|
||||
return getManager().getProject().isDisposed();
|
||||
}
|
||||
|
||||
default Place getShreds() {
|
||||
return getDocument().getShreds();
|
||||
}
|
||||
|
||||
default boolean isEventSystemEnabledImpl() {
|
||||
return !disabledTemporarily.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
DocumentWindowImpl getDocument();
|
||||
|
||||
static InjectedFileViewProvider create(@NotNull PsiManagerEx manager,
|
||||
@NotNull VirtualFileWindowImpl file,
|
||||
@NotNull DocumentWindowImpl window,
|
||||
@NotNull Language language) {
|
||||
AbstractFileViewProvider original = (AbstractFileViewProvider)manager.getFileManager().createFileViewProvider(file, false);
|
||||
return original instanceof TemplateLanguageFileViewProvider ?
|
||||
new Template(manager, file, window, language, original) :
|
||||
new InjectedFileViewProvider(manager, file, window, language, original);
|
||||
}
|
||||
|
||||
private static final class Template extends InjectedFileViewProvider implements TemplateLanguageFileViewProvider {
|
||||
private Template(@NotNull PsiManagerEx psiManager,
|
||||
@NotNull VirtualFileWindow virtualFile,
|
||||
@NotNull DocumentWindowImpl documentWindow,
|
||||
@NotNull Language language,
|
||||
AbstractFileViewProvider original) {
|
||||
super(psiManager, virtualFile, documentWindow, language, original);
|
||||
assert myOriginalProvider instanceof TemplateLanguageFileViewProvider;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Language getTemplateDataLanguage() {
|
||||
return ((TemplateLanguageFileViewProvider)myOriginalProvider).getTemplateDataLanguage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IElementType getContentElementType(Language language, PsiFile file) {
|
||||
return ((TemplateLanguageFileViewProvider)myOriginalProvider).getContentElementType(language, file);
|
||||
}
|
||||
new MultipleRootsInjectedFileViewProvider.Template(manager, file, window, language, original) :
|
||||
original instanceof MultiplePsiFilesPerDocumentFileViewProvider ?
|
||||
new MultipleRootsInjectedFileViewProvider(manager, file, window, language, original) :
|
||||
new SingleRootInjectedFileViewProvider(manager, file, window, language);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -606,7 +606,7 @@ class InjectionRegistrarImpl extends MultiHostRegistrarImpl implements MultiHost
|
||||
ASTNode[] parsedNodes = parseFile(language, language, oldDocumentWindow,
|
||||
hostVirtualFile, hostDocument, hostPsiFile, project, newDocumentText, placeInfos, chars,
|
||||
fileName, documentManager);
|
||||
List<PsiFile> oldFiles = oldInjectedPsiViewProvider.getCachedPsiFiles();
|
||||
List<PsiFile> oldFiles = ((AbstractFileViewProvider)oldInjectedPsiViewProvider).getCachedPsiFiles();
|
||||
synchronized (InjectedLanguageManagerImpl.ourInjectionPsiLock) {
|
||||
DiffLog[] diffLogs = new DiffLog[parsedNodes.length];
|
||||
for (int i = 0; i < parsedNodes.length; i++) {
|
||||
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.intellij.psi.impl.source.tree.injected;
|
||||
|
||||
import com.intellij.injected.editor.VirtualFileWindow;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.FreeThreadedFileViewProvider;
|
||||
import com.intellij.psi.impl.PsiManagerEx;
|
||||
import com.intellij.psi.impl.source.PsiFileImpl;
|
||||
import com.intellij.psi.templateLanguages.TemplateLanguageFileViewProvider;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class MultipleRootsInjectedFileViewProvider extends MultiplePsiFilesPerDocumentFileViewProvider implements FreeThreadedFileViewProvider, InjectedFileViewProvider {
|
||||
private final Object myLock = new Object();
|
||||
private final DocumentWindowImpl myDocumentWindow;
|
||||
private final Language myLanguage;
|
||||
private boolean myPatchingLeaves;
|
||||
protected final AbstractFileViewProvider myOriginalProvider;
|
||||
|
||||
MultipleRootsInjectedFileViewProvider(@NotNull PsiManager psiManager,
|
||||
@NotNull VirtualFileWindow virtualFile,
|
||||
@NotNull DocumentWindowImpl documentWindow,
|
||||
@NotNull Language language,
|
||||
@NotNull AbstractFileViewProvider original) {
|
||||
super(psiManager, (VirtualFile)virtualFile, true);
|
||||
myDocumentWindow = documentWindow;
|
||||
myLanguage = language;
|
||||
myOriginalProvider = original;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getLock() {
|
||||
return myLock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPatchingLeaves(boolean value) {
|
||||
myPatchingLeaves = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getPatchingLeaves() {
|
||||
return myPatchingLeaves;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileViewProvider clone() {
|
||||
return cloneImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rootChanged(@NotNull PsiFile psiFile) {
|
||||
super.rootChanged(psiFile);
|
||||
rootChangedImpl(psiFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEventSystemEnabled() {
|
||||
return isEventSystemEnabledImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPhysical() {
|
||||
return isPhysicalImpl();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Language getBaseLanguage() {
|
||||
return myLanguage;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<Language> getLanguages() {
|
||||
FileViewProvider original = myOriginalProvider;
|
||||
Set<Language> languages = original.getLanguages();
|
||||
Language base = original.getBaseLanguage();
|
||||
return ContainerUtil.map2Set(languages, (language) -> language == base ? myLanguage : language);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected MultiplePsiFilesPerDocumentFileViewProvider cloneInner(@NotNull VirtualFile fileCopy) {
|
||||
return (MultiplePsiFilesPerDocumentFileViewProvider)getManager().getFileManager().createFileViewProvider(fileCopy, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public DocumentWindowImpl getDocument() {
|
||||
return myDocumentWindow;
|
||||
}
|
||||
|
||||
@NonNls
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Multi root injected file '"+getVirtualFile().getName()+"' " + (isValid() ? "" : " invalid") + (isPhysical() ? "" : " nonphysical");
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void forceCachedPsi(@NotNull PsiFile psiFile) {
|
||||
myRoots.put(psiFile.getLanguage(), (PsiFileImpl)psiFile);
|
||||
getManager().getFileManager().setViewProvider(getVirtualFile(), this);
|
||||
}
|
||||
|
||||
static final class Template extends MultipleRootsInjectedFileViewProvider implements TemplateLanguageFileViewProvider {
|
||||
Template(@NotNull PsiManagerEx psiManager,
|
||||
@NotNull VirtualFileWindow virtualFile,
|
||||
@NotNull DocumentWindowImpl documentWindow,
|
||||
@NotNull Language language,
|
||||
AbstractFileViewProvider original) {
|
||||
super(psiManager, virtualFile, documentWindow, language, original);
|
||||
assert myOriginalProvider instanceof TemplateLanguageFileViewProvider;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Language getTemplateDataLanguage() {
|
||||
return ((TemplateLanguageFileViewProvider)myOriginalProvider).getTemplateDataLanguage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IElementType getContentElementType(Language language, PsiFile file) {
|
||||
return ((TemplateLanguageFileViewProvider)myOriginalProvider).getContentElementType(language, file);
|
||||
}
|
||||
}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.intellij.psi.impl.source.tree.injected;
|
||||
|
||||
import com.intellij.injected.editor.VirtualFileWindow;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.FileViewProvider;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.psi.SingleRootFileViewProvider;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class SingleRootInjectedFileViewProvider extends SingleRootFileViewProvider implements InjectedFileViewProvider {
|
||||
private final Object myLock = new Object();
|
||||
private final DocumentWindowImpl myDocumentWindow;
|
||||
private boolean myPatchingLeaves;
|
||||
|
||||
SingleRootInjectedFileViewProvider(@NotNull PsiManager psiManager,
|
||||
@NotNull VirtualFileWindow virtualFile,
|
||||
@NotNull DocumentWindowImpl documentWindow,
|
||||
@NotNull Language language) {
|
||||
super(psiManager, (VirtualFile)virtualFile, true, language);
|
||||
myDocumentWindow = documentWindow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getLock() {
|
||||
return myLock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPatchingLeaves(boolean value) {
|
||||
myPatchingLeaves = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getPatchingLeaves() {
|
||||
return myPatchingLeaves;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileViewProvider clone() {
|
||||
return cloneImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rootChanged(@NotNull PsiFile psiFile) {
|
||||
super.rootChanged(psiFile);
|
||||
rootChangedImpl(psiFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEventSystemEnabled() {
|
||||
return isEventSystemEnabledImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPhysical() {
|
||||
return isPhysicalImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public DocumentWindowImpl getDocument() {
|
||||
return myDocumentWindow;
|
||||
}
|
||||
|
||||
@NonNls
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Single root injected file '"+getVirtualFile().getName()+"' " + (isValid() ? "" : " invalid") + (isPhysical() ? "" : " nonphysical");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user