Package com.intellij.lang

Source Code of com.intellij.lang.Language

/*
* Copyright 2000-2007 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.lang;

import com.intellij.formatting.CustomFormattingModelBuilder;
import com.intellij.formatting.FormattingModelBuilder;
import com.intellij.ide.structureView.StructureViewBuilder;
import com.intellij.lang.annotation.Annotator;
import com.intellij.lang.annotation.ExternalAnnotator;
import com.intellij.lang.documentation.DocumentationProvider;
import com.intellij.lang.findUsages.EmptyFindUsagesProvider;
import com.intellij.lang.findUsages.FindUsagesProvider;
import com.intellij.lang.folding.FoldingBuilder;
import com.intellij.lang.parameterInfo.ParameterInfoHandler;
import com.intellij.lang.refactoring.DefaultRefactoringSupportProvider;
import com.intellij.lang.refactoring.JavaNamesValidator;
import com.intellij.lang.refactoring.NamesValidator;
import com.intellij.lang.refactoring.RefactoringSupportProvider;
import com.intellij.lang.surroundWith.SurroundDescriptor;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.ExtensionPoint;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.extensions.PluginDescriptor;
import com.intellij.openapi.extensions.SmartExtensionPoint;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.PlainSyntaxHighlighter;
import com.intellij.openapi.fileTypes.SyntaxHighlighter;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.NotNullLazyValue;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.FileViewProvider;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.tree.TokenSet;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.*;

/**
* The base class for all programming language support implementations. Specific language implementations should inherit from this class
* and its register instance wrapped with {@link com.intellij.openapi.fileTypes.LanguageFileType} instance through
* <code>FileTypeManager.getInstance().registerFileType</code>
* There should be exactly one instance of each Language. It is usually created when creating {@link com.intellij.openapi.fileTypes.LanguageFileType} and can be retrieved later
* with {@link #findInstance(Class)}.
* For the list of standard languages, see {@link com.intellij.lang.StdLanguages}.
*/
public abstract class Language {
  private static final Logger LOG = Logger.getInstance("#com.intellij.lang.Language");

  private static final SurroundDescriptor[] EMPTY_SURROUND_DESCRIPTORS_ARRAY = new SurroundDescriptor[0];
  private static final Map<Class<? extends Language>, Language> ourRegisteredLanguages = new HashMap<Class<? extends Language>, Language>();
  private final String myID;
  private final String[] myMimeTypes;
  public static final Language ANY = new Language("", "") { };
  private static final EmptyFindUsagesProvider EMPTY_FIND_USAGES_PROVIDER = new EmptyFindUsagesProvider();

  private final NotNullLazyValue<SmartExtensionPoint<AnnotatorEP, Annotator>> myAnnotatorsSmartEP = new NotNullLazyValue<SmartExtensionPoint<AnnotatorEP, Annotator>>() {
    @NotNull
    protected SmartExtensionPoint<AnnotatorEP, Annotator> compute() {
      return new SmartExtensionPoint<AnnotatorEP, Annotator>(new THashSet<Annotator>()) {
        @NotNull
        protected ExtensionPoint<AnnotatorEP> getExtensionPoint() {
          return Extensions.getRootArea().getExtensionPoint(AnnotatorEP.EP_NAME);
        }

        protected Annotator getExtension(@NotNull final AnnotatorEP ep) {
          if (ep.language.equals(getID())) {
            try {
              Annotator epAnnotator = ep.getAnnotator();
              if (epAnnotator == null) {
                PluginDescriptor descriptor = ep.getPluginDescriptor();
                epAnnotator = (Annotator)Class.forName(ep.annotatorClass, true,
                                                       descriptor == null ? getClass().getClassLoader() : descriptor.getPluginClassLoader()).newInstance();
                ep.setAnnotator(epAnnotator);
              }
              return epAnnotator;
            }
            catch(Exception e) {
              LOG.error(e);
            }
          }
          return null;
        }
      };
    }
  };

  private Set<ExternalAnnotator> myInjectedExternalAnnotators;
  private Annotator myLastAnnotator;
  private ExternalAnnotator myLastExternalAnnotator;
  private List<ExternalAnnotator> myCachedExternalAnnotators;
  private final List<CustomFormattingModelBuilder> myCustomFormatters = new ArrayList<CustomFormattingModelBuilder>();
  private DocumentationProvider myDocumentationProvider;

  private FileType myFileType;

  protected Language(@NonNls String id) {
    this(id, "");
  }

  protected Language(@NonNls final String ID, @NonNls final String... mimeTypes) {
    myID = ID;
    myMimeTypes = mimeTypes;
    Class<? extends Language> langClass = getClass();

    if (ourRegisteredLanguages.containsKey(langClass)) {
      LOG.error("Language '" + langClass.getName() + "' is already registered");
      return;
    }
    ourRegisteredLanguages.put(langClass, this);
  }

  /**
   * @return collection of all languages registered so far.
   */
  public static Collection<Language> getRegisteredLanguages() {
    return Collections.unmodifiableCollection(ourRegisteredLanguages.values());
  }

  /**
   * @param klass <code>java.lang.Class</code> of the particular language. Serves key purpose.
   * @return instance of the <code>klass</code> language registered if any.
   */
  public static <T extends Language> T findInstance(Class<T> klass) {
    //noinspection unchecked
    return (T)ourRegisteredLanguages.get(klass);
  }

  /**
   * Override this method to provide syntax highlighting (coloring) capabilities for your language implementation.
   * By syntax highlighting we mean highlighting of keywords, comments, braces etc. where lexing the file content is enough
   * to identify proper highlighting attributes.
   * <p/>
   * Default implementation doesn't highlight anything.
   *
   * @param project might be necessary to gather various project settings from.
   * @param virtualFile might be necessary to collect file specific settings
   * @return <code>SyntaxHighlighter</code> interface implementation for this particular language.
   */
  @NotNull
  public SyntaxHighlighter getSyntaxHighlighter(Project project, final VirtualFile virtualFile) {
    return new PlainSyntaxHighlighter();
  }

  /**
   * Returns a final incarnation of the formatting facilities with language's default formatting probably overriden by
   * ones injected with {@link #registerCustomFormattingModelBuilder(com.intellij.formatting.CustomFormattingModelBuilder)}.
   *
   * @param context to ask {@link com.intellij.formatting.CustomFormattingModelBuilder} if they're willing to take part in actuall formatting
   * @return a <code>FormattingModelBuilder</code> this <code>context</code> shall be formatted with.
   */
  @Nullable
  public final FormattingModelBuilder getEffectiveFormattingModelBuilder(PsiElement context) {
    for (CustomFormattingModelBuilder builder : myCustomFormatters) {
      if (builder.isEngagedToFormat(context)) return builder;
    }

    return getFormattingModelBuilder();
  }

  /**
   * Inject a custom (context specific) formatting.
   * @param builder a context sensitive formatting model builder to override a language's default.
   */
  public final void registerCustomFormattingModelBuilder(@NotNull CustomFormattingModelBuilder builder) {
    myCustomFormatters.add(builder);
  }

  /**
   * Unregister previously injected context sensitive formatting.
   * @param builder a @{CustomFormattingModelBuilder} to exclude from formatting voting races.
   */
  public final void unregisterCustomFormattingModelBuilder(@NotNull CustomFormattingModelBuilder builder) {
    myCustomFormatters.remove(builder);
  }

  /**
   * Override this method to provide code formatter (aka pretty print, aka code beauitifier) for your language implementation.
   * Language's default implementation of the FormatterModelBuilder can be overriden for certain contexts by external injections
   * with {@link #registerCustomFormattingModelBuilder(com.intellij.formatting.CustomFormattingModelBuilder)}.
   * Note that formatter implementation is necessary to make smart enter and smart end functions to work properly.
   *
   * @return <code>FormattingModelBuilder</code> interface implementation for this particular language or <code>null</code>
   *         if no formatting capabilities provided.
   */
  @Nullable
  public FormattingModelBuilder getFormattingModelBuilder() {
    return null;
  }

  /**
   * Override this method to provide parser implementation.
   * Parsed tree (AST) and program structure interface (PSI) based on AST is necessary for most of IDEA smart functions like
   * in-editor error highlighting, advanced syntax highlighting, error-checking, intention actions, inspections, folding,
   * finding usages, refactoring, file structure view etc.
   *
   * @return <code>ParserDefinition</code> interface implementation for this particular language or <code>null</code>
   *         if no parsing capabilities provided.
   */
  @Nullable
  public ParserDefinition getParserDefinition() {
    return null;
  }

  /**
   * Override this method to provide code folding capabilities when editing files of this language.
   * Please note {@link #getParserDefinition()} should return parser implementation for folding building to work properly.
   *
   * @return <code>FoldingBuilder</code> interface implementation for this particular language or <code>null</code>
   *         if no folding capabilities provided.
   */
  @Nullable
  public FoldingBuilder getFoldingBuilder() {
    return null;
  }

  /**
   * Override this method to provide paired brace matching and highlighting ability for editors of the language.
   * For this functionality to work properly own {@link SyntaxHighlighter} implementation is necessary.
   *
   * @return <code>PairedBraceMatcher</code> interface implementation for this particular language or <code>null</code>
   *         if no brace matching capabilities provided.
   */
  @Nullable
  public PairedBraceMatcher getPairedBraceMatcher() {
    return null;
  }

  /**
   * Override this method to provide comment-by-block and/or comment-by-line actions implementations for your language.
   * For this functionality to work properly {@link ParserDefinition} implementation is necessary.
   *
   * @return <code>Commenter</code> interface implementation for this particular language or <code>null</code>
   *         if no auto-commenting capabilities provided.
   */
  @Nullable
  public Commenter getCommenter() {
    return null;
  }

  /**
   * Word completion feature related method. It supposed to return token types of the places where word completion should be enabled.
   * Default implementation delegates to parser definition and returns comment tokens so words completion is enabled in comments
   * if parser definition is implemented.
   *
   * @return set of token types where word completion should be enabled.
   */
  @NotNull
  public TokenSet getReadableTextContainerElements() {
    final ParserDefinition parserDefinition = getParserDefinition();
    if (parserDefinition != null) return parserDefinition.getCommentTokens();
    return TokenSet.EMPTY;
  }

  /**
   * Override this method to provide on-the-fly error highlighting with quickfixes as well as parse tree based syntax annotations
   * like highlighting instance variables in java.
   * For this functionality to work properly {@link ParserDefinition} implementation is necessary.
   * Note that syntax errors flagged by parser in ParserDefinition are highlighted automatically.
   * Annotator is run against changed parts of the parse tree incrementally.
   *
   * @return <code>Annotator</code> interface implementation for this particular language or <code>null</code>
   *         if no error and syntax highlighting capabilities provided.
   */
  @Nullable
  public Annotator getAnnotator() {
    return null;
  }

  /**
   * Registers an annotator to provide additional error highlighting for files in the language.
   * Can be used, for example, to provide additional highlighting in Java files.
   *
   * @param annotator the annotator to inject.
   */
  public final synchronized void injectAnnotator(@NotNull Annotator annotator) {
    myAnnotatorsSmartEP.getValue().addExplicitExtension(annotator);
  }

  public final synchronized void injectAnnotator(@NotNull final Annotator annotator, Disposable parentDisposable) {
    injectAnnotator(annotator);
    Disposer.register(parentDisposable, new Disposable() {
      public void dispose() {
        removeAnnotator(annotator);
      }
    });
  }

  /**
   * Unregisters an injected annotator.
   *
   * @param annotator the annotator to remove.
   */
  public final synchronized void removeAnnotator(@NotNull Annotator annotator) {
    myAnnotatorsSmartEP.getValue().removeExplicitExtension(annotator);
  }

  /**
   * Returns a list containing the language's own annotator and injected annotators.
   *
   * @return a list of all annotators for the language.
   */
  @NotNull
  public final synchronized List<Annotator> getAnnotators() {
    final SmartExtensionPoint<AnnotatorEP, Annotator> smartEP = myAnnotatorsSmartEP.getValue();
    Annotator annotator = getAnnotator();
    if (annotator != myLastAnnotator) {
      if (myLastAnnotator != null) {
        smartEP.removeExplicitExtension(myLastAnnotator);
      }
      if (annotator != null) {
        smartEP.addExplicitExtension(annotator);
      }
      myLastAnnotator = annotator;
    }
    return smartEP.getExtensions();
  }

  /**
   * Same as {@link #getAnnotator()} but is being run once against whole file. It's most proper to use when integrating external
   * validation tools like xerces schema validator for XML.
   *
   * @return external annotator for a whole file.
   *         Since this annotating is expensive due to nonincrementality, it is run last
   */
  @Nullable
  public ExternalAnnotator getExternalAnnotator() {
    return null;
  }

  /**
   * Registers an external annotator to provide additional error highlighting for files in the language.
   * Can be used, for example, to provide additional highlighting in Java files.
   *
   * @param annotator the annotator to inject.
   */
  public final synchronized void injectExternalAnnotator(@NotNull ExternalAnnotator annotator) {
    if (myInjectedExternalAnnotators == null) {
      myInjectedExternalAnnotators = new THashSet<ExternalAnnotator>();
    }
    myInjectedExternalAnnotators.add(annotator);
    myCachedExternalAnnotators = null;
  }

  /**
   * Unregisters an injected annotator.
   *
   * @param annotator the annotator to remove.
   */
  public final synchronized void removeExternalAnnotator(@NotNull ExternalAnnotator annotator) {
    if (myInjectedExternalAnnotators != null) {
      myInjectedExternalAnnotators.remove(annotator);
      myCachedExternalAnnotators = null;
    }
  }

  /**
   * Returns a list containing the language's own annotator and injected annotators.
   *
   * @return a list of all annotators for the language.
   */
  @NotNull
  public final synchronized List<ExternalAnnotator> getExternalAnnotators() {
    ExternalAnnotator annotator = getExternalAnnotator();
    if (annotator == myLastExternalAnnotator && myCachedExternalAnnotators != null) {
      return myCachedExternalAnnotators;
    }
    myLastExternalAnnotator = annotator;
    int injectCount = myInjectedExternalAnnotators == null ? 0 : myInjectedExternalAnnotators.size();
    if (annotator == null && injectCount == 0) {
      myCachedExternalAnnotators = ExternalAnnotator.EMPTY_LIST;
    }
    else {
      myCachedExternalAnnotators = new ArrayList<ExternalAnnotator>();
      if (annotator != null) {
        myCachedExternalAnnotators.add(annotator);
      }
      if (myInjectedExternalAnnotators != null) {
        myCachedExternalAnnotators.addAll(myInjectedExternalAnnotators);
      }

    }
    return myCachedExternalAnnotators;
  }

  /**
   * Override this method to provide find usages capability for the elements of your language
   * For this functionality to work properly {@link ParserDefinition} implementation is necessary.
   * <p/>
   * Default implementation returns mock find usages provider uncapable to search anything.
   *
   * @return <code>FindUsagesProvider</code> interface implementation for this particular language.
   */
  @NotNull
  public FindUsagesProvider getFindUsagesProvider() {
    return EMPTY_FIND_USAGES_PROVIDER;
  }

  /**
   * Override this method to provide structure view and file structure popup content for the files of your language.
   *
   * @param psiFile
   * @return <code>StructureViewBuilder</code> interface implementation for this particular language or <code>null</code>
   *         if no file structure implementation.
   */
  @Nullable
  public StructureViewBuilder getStructureViewBuilder(PsiFile psiFile) {
    return null;
  }

  /**
   * Override this method to provide common refactorings implementation for the elements of your language.
   * Derive your implementation from @see{com.intellij.lang.refactoring.DefaultRefactoringSupportProvider} to avoid
   * binary incompatibility caused by expanding this interface.
   * Note that rename refactoring will be automatically enabled with <code>FindUsagesProvider</code> and <code>ParserDefinition</code>.
   *
   * @return <code>RefactoringSupportProvider</code> interface implementation for this particular language
   */
  @NotNull
  public RefactoringSupportProvider getRefactoringSupportProvider() {
    return new DefaultRefactoringSupportProvider();
  }

  /**
   * Override this method to customize algorithm of identifier validation and language keyword set.
   * Default implementation provides java language identifier validation and java language keyword set.
   * For the time being the information provided is used in rename refactoring only.
   *
   * @return <code>NamesValidator</code> interface implementation for this particular language. <code>null</code> value must
   *         not be returned.
   * @since 5.0.1
   */
  @NotNull
  public NamesValidator getNamesValidator() {
    return new JavaNamesValidator();
  }

  @Nullable public PsiReferenceAdjuster getReferenceAdjuster() { return null; }

  /**
   * Override this method to provide 'surround with...' feature implementation for editors of the files in your language.
   * <p/>
   * Default implementation returns empty array of SurroundDescriptor implementations thus disabling the feature.
   *
   * @return <code>SurroundDescriptor</code> interface implementations for this particular language.
   */
  @NotNull
  public SurroundDescriptor[] getSurroundDescriptors() {
    return EMPTY_SURROUND_DESCRIPTORS_ARRAY;
  }

  /**
   * Override this method to provide 'optimize imports' feature implementation
   * @return <code>ImportOptimizer</code> interface implementations for this particular language.
   */
  @Nullable
  public ImportOptimizer getImportOptimizer() {
    return null;
  }

  public String toString() {
    //noinspection HardCodedStringLiteral
    return "Language: " + myID;
  }

  /**
   * Returns the list of MIME types corresponding to the language. The language MIME type is used for specifying the base language
   * of a JSP page.
   *
   * @return The list of MIME types.
   */
  public String[] getMimeTypes() {
    return myMimeTypes;
  }

  /**
   * Returns a user-readable name of the language.
   *
   * @return the name of the language.
   */
  @NotNull
  public String getID() {
    return myID;
  }

  @Nullable
  public FileType getAssociatedFileType() {
    return myFileType;
  }

  public void associateFileType(FileType type) {
    myFileType = type;
  }

  @Nullable
  public FileViewProvider createViewProvider(final VirtualFile file, final PsiManager manager, final boolean physical) {
    return null;
  }

  @Nullable
  protected DocumentationProvider createDocumentationProvider() {
    return null;
  }

  /**
   * Calls {@link Language#createDocumentationProvider} lazily and returns {@link com.intellij.lang.documentation.DocumentationProvider }
   * @return instance of DocumentationProvider associated with this language
   */
  @Nullable
  public DocumentationProvider getDocumentationProvider() {
    if ( myDocumentationProvider == null ) {
      myDocumentationProvider = createDocumentationProvider();
    }
    return myDocumentationProvider;
  }

  /**
   * Replaces the documentation provider.<br>
   * One interesting use of this method is passing an instance of
   * {@link com.intellij.lang.documentation.CompositeDocumentationProvider}.<br>
   * Here is how to set up some sort of Chain of Responsibility:
   * <blockquote>
   * language.setDocumentationProvider ( new CompositeDocumentationProvider ( new InjectedProvider (), language.getDocumentationProvider () ));
   * </blockquote>
   * @param documentationProvider new documentation provider
   */
  public void setDocumentationProvider(DocumentationProvider documentationProvider) {
    myDocumentationProvider = documentationProvider;
  }

  @Nullable
  public LanguageDialect[] getAvailableLanguageDialects() {
    return null;
  }

  @Nullable
  public ParameterInfoHandler[] getParameterInfoHandlers() {
    return null;
  }

  @Nullable
  public LanguageCodeInsightActionHandler getGotoSuperHandler() {
    return null;
  }

  @Nullable
  public LanguageCodeInsightActionHandler getImplementMethodsHandler() {
    return null;
  }

  @Nullable
  public LanguageCodeInsightActionHandler getOverrideMethodsHandler() {
    return null;
  }
}
TOP

Related Classes of com.intellij.lang.Language

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.