/*
* Copyright 2000-2014 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.openapi.editor.actionSystem;
import com.intellij.codeInsight.hint.HintManager;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.command.UndoConfirmationPolicy;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.project.Project;
import com.intellij.reporting.FreezeLogger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Provides services for registering actions which are activated by typing in the editor.
*
* @see EditorActionManager#getTypedAction()
*/
public class TypedAction {
@NotNull
private TypedActionHandler myRawHandler;
private TypedActionHandler myHandler;
private boolean myHandlersLoaded;
public TypedAction() {
myHandler = new Handler();
myRawHandler = new DefaultRawHandler();
}
private void ensureHandlersLoaded() {
if (!myHandlersLoaded) {
myHandlersLoaded = true;
for (EditorTypedHandlerBean handlerBean : Extensions.getExtensions(EditorTypedHandlerBean.EP_NAME)) {
myHandler = handlerBean.getHandler(myHandler);
}
}
}
private static class Handler implements TypedActionHandler {
@Override
public void execute(@NotNull final Editor editor, char charTyped, @NotNull DataContext dataContext) {
if (editor.isViewer()) return;
Document doc = editor.getDocument();
doc.startGuardedBlockChecking();
try {
final String str = String.valueOf(charTyped);
CommandProcessor.getInstance().setCurrentCommandName(EditorBundle.message("typing.in.editor.command.name"));
EditorModificationUtil.typeInStringAtCaretHonorMultipleCarets(editor, str, true);
}
catch (ReadOnlyFragmentModificationException e) {
EditorActionManager.getInstance().getReadonlyFragmentModificationHandler(doc).handle(e);
}
finally {
doc.stopGuardedBlockChecking();
}
}
}
/**
* Gets the current typing handler.
*
* @return the current typing handler.
*/
public TypedActionHandler getHandler() {
ensureHandlersLoaded();
return myHandler;
}
/**
* Replaces the typing handler with the specified handler. The handler should pass
* unprocessed typing to the previously registered handler.
*
* @param handler the handler to set.
* @return the previously registered handler.
*/
public TypedActionHandler setupHandler(TypedActionHandler handler) {
ensureHandlersLoaded();
TypedActionHandler tmp = myHandler;
myHandler = handler;
return tmp;
}
/**
* Gets the current 'raw' typing handler.
*
* @see #setupRawHandler(TypedActionHandler)
*/
@NotNull
public TypedActionHandler getRawHandler() {
return myRawHandler;
}
/**
* Replaces current 'raw' typing handler with the specified handler. The handler should pass unprocessed typing to the
* previously registered 'raw' handler.
* <p>
* 'Raw' handler is a handler directly invoked by the code which handles typing in editor. Default 'raw' handler
* performs some generic logic that has to be done on typing (like checking whether file has write access, creating a command
* instance for undo subsystem, initiating write action, etc), but delegates to 'normal' handler for actual typing logic.
*
* @param handler the handler to set.
* @return the previously registered handler.
* @see #getRawHandler()
* @see #getHandler()
* @see #setupHandler(TypedActionHandler)
*/
@NotNull
public TypedActionHandler setupRawHandler(@NotNull TypedActionHandler handler) {
TypedActionHandler tmp = myRawHandler;
myRawHandler = handler;
return tmp;
}
public void beforeActionPerformed(@NotNull Editor editor, char c, @NotNull DataContext context, @NotNull ActionPlan plan) {
if (myRawHandler instanceof TypedActionHandlerEx) {
((TypedActionHandlerEx)myRawHandler).beforeExecute(editor, c, context, plan);
}
}
public final void actionPerformed(@Nullable final Editor editor, final char charTyped, final DataContext dataContext) {
if (editor == null) return;
Project project = CommonDataKeys.PROJECT.getData(dataContext);
FreezeLogger.getInstance().runUnderPerformanceMonitor(project, () -> myRawHandler.execute(editor, charTyped, dataContext));
}
private class DefaultRawHandler implements TypedActionHandlerEx {
@Override
public void beforeExecute(@NotNull Editor editor, char c, @NotNull DataContext context, @NotNull ActionPlan plan) {
if (editor.isViewer() || !editor.getDocument().isWritable()) return;
TypedActionHandler handler = getHandler();
if (handler instanceof TypedActionHandlerEx) {
((TypedActionHandlerEx)handler).beforeExecute(editor, c, context, plan);
}
}
@Override
public void execute(@NotNull final Editor editor, final char charTyped, @NotNull final DataContext dataContext) {
CommandProcessor.getInstance().executeCommand(CommonDataKeys.PROJECT.getData(dataContext), () -> {
if (!EditorModificationUtil.requestWriting(editor)) {
HintManager.getInstance().showInformationHint(editor, "File is not writable");
return;
}
ApplicationManager.getApplication().runWriteAction(new DocumentRunnable(editor.getDocument(), editor.getProject()) {
@Override
public void run() {
Document doc = editor.getDocument();
doc.startGuardedBlockChecking();
try {
getHandler().execute(editor, charTyped, dataContext);
}
catch (ReadOnlyFragmentModificationException e) {
EditorActionManager.getInstance().getReadonlyFragmentModificationHandler(doc).handle(e);
}
finally {
doc.stopGuardedBlockChecking();
}
}
});
}, "", editor.getDocument(), UndoConfirmationPolicy.DEFAULT, editor.getDocument());
}
}
}