| /*=============================================================================# |
| # Copyright (c) 2005, 2019 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.nico.ui.console; |
| |
| import java.util.EnumSet; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.core.commands.AbstractHandler; |
| import org.eclipse.core.commands.ExecutionEvent; |
| import org.eclipse.core.commands.ExecutionException; |
| import org.eclipse.core.commands.IHandler2; |
| import org.eclipse.core.filebuffers.IDocumentSetupParticipant; |
| import org.eclipse.core.runtime.content.IContentType; |
| import org.eclipse.jface.action.IStatusLineManager; |
| import org.eclipse.jface.layout.PixelConverter; |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.DocumentEvent; |
| import org.eclipse.jface.text.IDocumentListener; |
| import org.eclipse.jface.text.ITextOperationTarget; |
| import org.eclipse.jface.text.source.AnnotationModel; |
| import org.eclipse.jface.text.source.ISourceViewer; |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.StyledText; |
| import org.eclipse.swt.custom.VerifyKeyListener; |
| import org.eclipse.swt.events.MouseAdapter; |
| import org.eclipse.swt.events.MouseEvent; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.events.SelectionListener; |
| import org.eclipse.swt.events.VerifyEvent; |
| import org.eclipse.swt.graphics.Font; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.widgets.Button; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Event; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.swt.widgets.Listener; |
| import org.eclipse.swt.widgets.Slider; |
| import org.eclipse.ui.IWorkbenchPart; |
| import org.eclipse.ui.editors.text.EditorsUI; |
| import org.eclipse.ui.handlers.IHandlerService; |
| import org.eclipse.ui.internal.editors.text.EditorsPlugin; |
| import org.eclipse.ui.services.IServiceLocator; |
| import org.eclipse.ui.texteditor.AnnotationPreference; |
| import org.eclipse.ui.texteditor.IEditorStatusLine; |
| import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; |
| import org.eclipse.ui.texteditor.MarkerAnnotationPreferences; |
| import org.eclipse.ui.texteditor.SourceViewerDecorationSupport; |
| |
| import org.eclipse.statet.jcommons.status.Status; |
| |
| import org.eclipse.statet.ecommons.io.FileUtil; |
| import org.eclipse.statet.ecommons.preferences.core.util.PreferenceUtils; |
| import org.eclipse.statet.ecommons.text.ICharPairMatcher; |
| import org.eclipse.statet.ecommons.text.core.sections.IDocContentSections; |
| import org.eclipse.statet.ecommons.text.core.util.AbstractFragmentDocument; |
| import org.eclipse.statet.ecommons.text.ui.InformationDispatchHandler; |
| import org.eclipse.statet.ecommons.text.ui.TextHandlerUtil; |
| import org.eclipse.statet.ecommons.text.ui.TextViewerCustomCaretSupport; |
| import org.eclipse.statet.ecommons.text.ui.TextViewerEditorColorUpdater; |
| import org.eclipse.statet.ecommons.ui.ISettingsChangedHandler; |
| import org.eclipse.statet.ecommons.ui.util.UIAccess; |
| import org.eclipse.statet.ecommons.ui.workbench.ContextHandlers; |
| import org.eclipse.statet.ecommons.ui.workbench.WorkbenchUIUtils; |
| |
| import org.eclipse.statet.internal.nico.ui.Messages; |
| import org.eclipse.statet.internal.nico.ui.NicoUIPlugin; |
| import org.eclipse.statet.internal.nico.ui.console.InputDocument; |
| import org.eclipse.statet.internal.nico.ui.console.PromptHighlighter; |
| import org.eclipse.statet.internal.nico.ui.preferences.ConsolePreferences; |
| import org.eclipse.statet.ltk.model.core.elements.ISourceUnit; |
| import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditor; |
| import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditorCommandIds; |
| import org.eclipse.statet.ltk.ui.sourceediting.ITextEditToolSynchronizer; |
| import org.eclipse.statet.ltk.ui.sourceediting.SourceEditorViewerConfiguration; |
| import org.eclipse.statet.ltk.ui.sourceediting.SourceEditorViewerConfigurator; |
| import org.eclipse.statet.ltk.ui.sourceediting.StructureSelectHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.StructureSelectionHistory; |
| import org.eclipse.statet.ltk.ui.sourceediting.StructureSelectionHistoryBackHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.CutLineHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.CutToLineBeginHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.CutToLineEndHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.DeleteLineHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.DeleteNextWordHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.DeletePreviousWordHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.DeleteToLineBeginHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.DeleteToLineEndHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.GotoLineBeginHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.GotoLineEndHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.GotoMatchingBracketHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.GotoNextWordHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.GotoPreviousWordHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.SelectLineBeginHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.SelectLineEndHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.SelectNextWordHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.SelectPreviousWordHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.SourceEditorOperationHandler; |
| import org.eclipse.statet.ltk.ui.sourceediting.actions.SpecificContentAssistHandler; |
| import org.eclipse.statet.nico.core.runtime.History; |
| import org.eclipse.statet.nico.core.runtime.History.Entry; |
| import org.eclipse.statet.nico.core.runtime.IHistoryListener; |
| import org.eclipse.statet.nico.core.runtime.Prompt; |
| import org.eclipse.statet.nico.core.runtime.SubmitType; |
| import org.eclipse.statet.nico.core.runtime.ToolController; |
| import org.eclipse.statet.nico.core.runtime.ToolProcess; |
| import org.eclipse.statet.nico.core.runtime.ToolWorkspace; |
| |
| |
| /** |
| * The input line (prompt, input, submit button) of the console page. |
| */ |
| public class ConsolePageEditor implements ISettingsChangedHandler, ISourceEditor { |
| |
| |
| final static int KEY_HIST_UP= SWT.ARROW_UP; |
| final static int KEY_HIST_DOWN= SWT.ARROW_DOWN; |
| final static int KEY_HIST_SPREFIX_UP= SWT.ALT | SWT.ARROW_UP; |
| final static int KEY_HIST_SPREFIX_DOWN= SWT.ALT | SWT.ARROW_DOWN; |
| final static int KEY_SUBMIT_DEFAULT= SWT.CR; |
| final static int KEY_SUBMIT_KEYPAD= SWT.KEYPAD_CR; |
| |
| final static int KEY_OUTPUT_LINEUP= SWT.SHIFT | SWT.ARROW_UP; |
| final static int KEY_OUTPUT_LINEDOWN= SWT.SHIFT | SWT.ARROW_DOWN; |
| final static int KEY_OUTPUT_PAGEUP= SWT.SHIFT | SWT.PAGE_UP; |
| final static int KEY_OUTPUT_PAGEDOWN= SWT.SHIFT | SWT.PAGE_DOWN; |
| final static int KEY_OUTPUT_START= SWT.MOD1 | SWT.SHIFT | SWT.HOME; |
| final static int KEY_OUTPUT_END= SWT.MOD1 | SWT.SHIFT | SWT.END; |
| |
| |
| private final class ScrollControl implements Listener { |
| |
| private static final int MAX= 150; |
| private static final int SPECIAL= 80; |
| private final Slider fSlider; |
| private int fLastPos= 0; |
| |
| private ScrollControl(final Slider slider) { |
| this.fSlider= slider; |
| this.fSlider.setMaximum(MAX); |
| this.fSlider.addListener(SWT.Selection, this); |
| } |
| |
| @Override |
| public void handleEvent(final Event event) { |
| final int selection= this.fSlider.getSelection(); |
| int newIndex; |
| final StyledText output= ConsolePageEditor.this.consolePage.getOutputViewer().getTextWidget(); |
| final StyledText input= ConsolePageEditor.this.sourceViewer.getTextWidget(); |
| int current= Math.max(output.getHorizontalIndex(), input.getHorizontalIndex()); |
| if (event.detail == SWT.DRAG) { |
| if (this.fLastPos < 0) { |
| this.fLastPos= current; |
| } |
| if (current > SPECIAL || this.fLastPos > SPECIAL) { |
| current= this.fLastPos; |
| final double diff= selection-SPECIAL; |
| newIndex= current + (int) ( |
| Math.signum(diff) * Math.max(Math.exp(Math.abs(diff)/7.5)-1.0, 0.0) * 2.0 ); |
| if (newIndex < selection) { |
| newIndex= selection; |
| } |
| } |
| else { |
| newIndex= selection; |
| } |
| } |
| else { |
| if (current > SPECIAL) { |
| newIndex= current + selection-SPECIAL; |
| } |
| else { |
| newIndex= selection; |
| } |
| } |
| output.setHorizontalIndex(newIndex); |
| input.setHorizontalIndex(newIndex); |
| current= Math.max(output.getHorizontalIndex(), input.getHorizontalIndex()); |
| if (event.detail != SWT.DRAG) { |
| this.fSlider.setSelection(current > 80 ? 80 : current); |
| this.fLastPos= -1; |
| } |
| } |
| |
| public void reset() { |
| this.fSlider.setSelection(0); |
| final StyledText output= ConsolePageEditor.this.consolePage.getOutputViewer().getTextWidget(); |
| output.setHorizontalIndex(0); |
| this.fLastPos= -1; |
| } |
| } |
| |
| private class StatusLine implements IEditorStatusLine { |
| |
| private boolean messageSetted; |
| private String message; |
| |
| @Override |
| public void setMessage(final boolean error, final String message, final Image image) { |
| final IStatusLineManager manager= ConsolePageEditor.this.consolePage.getSite().getActionBars().getStatusLineManager(); |
| if (manager != null) { |
| this.messageSetted= true; |
| if (error) { |
| manager.setErrorMessage(image, message); |
| } |
| else { |
| manager.setMessage(image, message); |
| } |
| } |
| } |
| |
| void cleanStatusLine() { |
| if (this.messageSetted) { |
| final IStatusLineManager manager= ConsolePageEditor.this.consolePage.getSite().getActionBars().getStatusLineManager(); |
| if (manager != null) { |
| this.messageSetted= false; |
| manager.setErrorMessage(null); |
| updateWD(); |
| } |
| |
| } |
| } |
| |
| void updateWD() { |
| if (!this.messageSetted) { |
| final IStatusLineManager manager= ConsolePageEditor.this.consolePage.getSite().getActionBars().getStatusLineManager(); |
| if (manager != null) { |
| final String path= FileUtil.toString(ConsolePageEditor.this.consolePage.getTool().getWorkspaceData().getWorkspaceDir()); |
| this.message= path; |
| manager.setMessage(path); |
| } |
| } |
| } |
| |
| void refresh() { |
| final IStatusLineManager manager= ConsolePageEditor.this.consolePage.getSite().getActionBars().getStatusLineManager(); |
| if (manager != null) { |
| manager.setMessage(this.message); |
| } |
| } |
| |
| } |
| |
| private class ThisKeyListener implements VerifyKeyListener { |
| |
| @Override |
| public void verifyKey(final VerifyEvent e) { |
| final int key= (e.stateMask | e.keyCode); |
| switch (key) { |
| case KEY_HIST_UP: |
| doHistoryOlder(null); |
| break; |
| case KEY_HIST_DOWN: |
| doHistoryNewer(null); |
| break; |
| case KEY_SUBMIT_DEFAULT: |
| case KEY_SUBMIT_KEYPAD: |
| doSubmit(); |
| return; // e.doit= true, to exit linked mode |
| |
| case KEY_OUTPUT_LINEUP: |
| doOutputLineUp(); |
| break; |
| case KEY_OUTPUT_LINEDOWN: |
| doOutputLineDown(); |
| break; |
| case KEY_OUTPUT_PAGEUP: |
| doOutputPageUp(); |
| break; |
| case KEY_OUTPUT_PAGEDOWN: |
| doOutputPageDown(); |
| break; |
| default: |
| // non-constant values |
| if (key == KEY_OUTPUT_START) { |
| doOutputFirstLine(); |
| break; |
| } |
| if (key == KEY_OUTPUT_END) { |
| doOutputLastLine(); |
| break; |
| } |
| // no special key |
| return; |
| } |
| e.doit= false; |
| } |
| |
| private void doOutputLineUp() { |
| final StyledText output= (StyledText) ConsolePageEditor.this.consolePage.getOutputViewer().getControl(); |
| final int next= output.getTopIndex() - 1; |
| if (next < 0) { |
| return; |
| } |
| output.setTopIndex(next); |
| } |
| |
| private void doOutputLineDown() { |
| final StyledText output= (StyledText) ConsolePageEditor.this.consolePage.getOutputViewer().getControl(); |
| final int next= output.getTopIndex() + 1; |
| if (next >= output.getLineCount()) { |
| return; |
| } |
| output.setTopIndex(next); |
| } |
| |
| private void doOutputPageUp() { |
| final StyledText output= (StyledText) ConsolePageEditor.this.consolePage.getOutputViewer().getControl(); |
| final int current= output.getTopIndex(); |
| final int move= Math.max(1, (output.getClientArea().height / output.getLineHeight()) - 1); |
| final int next= Math.max(0, current - move); |
| if (next == current) { |
| return; |
| } |
| output.setTopIndex(next); |
| } |
| |
| private void doOutputPageDown() { |
| final StyledText output= (StyledText) ConsolePageEditor.this.consolePage.getOutputViewer().getControl(); |
| final int current= output.getTopIndex(); |
| final int move= Math.max(1, (output.getClientArea().height / output.getLineHeight()) - 1); |
| final int next= Math.min(current + move, output.getLineCount() - 1); |
| if (next == current) { |
| return; |
| } |
| output.setTopIndex(next); |
| } |
| |
| private void doOutputFirstLine() { |
| final StyledText output= (StyledText) ConsolePageEditor.this.consolePage.getOutputViewer().getControl(); |
| final int next= 0; |
| output.setTopIndex(next); |
| } |
| |
| private void doOutputLastLine() { |
| final StyledText output= (StyledText) ConsolePageEditor.this.consolePage.getOutputViewer().getControl(); |
| final int next= output.getLineCount()-1; |
| if (next < 0) { |
| return; |
| } |
| output.setTopIndex(next); |
| } |
| |
| } |
| |
| private NIConsolePage consolePage; |
| private ToolProcess process; |
| |
| private final IContentType contentType; |
| |
| private final ISourceUnit sourceUnit; |
| |
| private Composite composite; |
| private Label prompt; |
| private PromptHighlighter promptHighlighter; |
| private InputSourceViewer sourceViewer; |
| private final InputDocument document; |
| private Button submitButton; |
| private ScrollControl scroller; |
| |
| private final StatusLine statusLine= new StatusLine(); |
| private SourceViewerDecorationSupport sourceViewerDecorationSupport; |
| private SourceEditorViewerConfigurator configurator; |
| private TextViewerCustomCaretSupport customCarretSupport; |
| |
| |
| private History.Entry submitHistoryCurrentEntry; |
| private IHistoryListener submitHistoryListener; |
| private EnumSet<SubmitType> submitHistoryTypesFilter; |
| |
| /** |
| * Saves the selection before starting a history navigation session. |
| * non-null value indicates in history session */ |
| private Point historyCompoundChange; |
| /** There are no caret listener */ |
| private Point historyCaretWorkaround; |
| /** Indicates that the document is change by a history action */ |
| private boolean inHistoryChange= false; |
| |
| private StructureSelectionHistory selectionHistory; |
| |
| private ToolWorkspace.Listener workspaceListener; |
| |
| |
| public ConsolePageEditor(final NIConsolePage page, final IContentType contentType) { |
| this.consolePage= page; |
| this.process= page.getConsole().getProcess(); |
| |
| this.contentType= contentType; |
| |
| this.document= new InputDocument(); |
| this.sourceUnit= createSourceUnit(); |
| |
| updateSettings(); |
| } |
| |
| |
| private void updateSettings() { |
| this.submitHistoryTypesFilter= PreferenceUtils.getInstancePrefs().getPreferenceValue(ConsolePreferences.HISTORYNAVIGATION_SUBMIT_TYPES_PREF); |
| } |
| |
| |
| public AbstractFragmentDocument getDocument() { |
| return this.document; |
| } |
| |
| protected ISourceUnit createSourceUnit() { |
| return null; |
| } |
| |
| public Composite createControl(final Composite parent, final SourceEditorViewerConfigurator editorConfig) { |
| this.composite= new Composite(parent, SWT.NONE); |
| final GridLayout layout= new GridLayout(3, false); |
| layout.marginHeight= 0; |
| layout.horizontalSpacing= 0; |
| layout.marginWidth= 0; |
| layout.verticalSpacing= 3; |
| this.composite.setLayout(layout); |
| |
| this.prompt= new Label(this.composite, SWT.LEFT); |
| GridData gd= new GridData(SWT.LEFT, SWT.FILL, false, false); |
| gd.verticalIndent= 1; |
| this.prompt.setLayoutData(gd); |
| this.prompt.setText("> "); //$NON-NLS-1$ |
| this.prompt.addMouseListener(new MouseAdapter() { |
| @Override |
| public void mouseDown(final MouseEvent e) { |
| if (UIAccess.isOkToUse(ConsolePageEditor.this.sourceViewer)) { |
| ConsolePageEditor.this.sourceViewer.doOperation(ITextOperationTarget.SELECT_ALL); |
| } |
| } |
| }); |
| |
| createSourceViewer(editorConfig); |
| gd= new GridData(SWT.FILL, SWT.FILL, true, true); |
| gd.verticalIndent= 1; |
| this.sourceViewer.getControl().setLayoutData(gd); |
| this.sourceViewer.appendVerifyKeyListener(new ThisKeyListener()); |
| final StyledText textWidget= this.sourceViewer.getTextWidget(); |
| textWidget.setKeyBinding(KEY_HIST_UP, SWT.NULL); |
| textWidget.setKeyBinding(KEY_HIST_DOWN, SWT.NULL); |
| textWidget.setKeyBinding(KEY_SUBMIT_DEFAULT, SWT.NULL); |
| textWidget.setKeyBinding(KEY_SUBMIT_KEYPAD, SWT.NULL); |
| textWidget.setKeyBinding(KEY_OUTPUT_LINEUP, SWT.NULL); |
| textWidget.setKeyBinding(KEY_OUTPUT_LINEDOWN, SWT.NULL); |
| textWidget.setKeyBinding(KEY_OUTPUT_PAGEUP, SWT.NULL); |
| textWidget.setKeyBinding(KEY_OUTPUT_PAGEDOWN, SWT.NULL); |
| textWidget.setKeyBinding(KEY_OUTPUT_START, SWT.NULL); |
| textWidget.setKeyBinding(KEY_OUTPUT_END, SWT.NULL); |
| |
| this.submitButton= new Button(this.composite, SWT.NONE); |
| gd= new GridData(SWT.FILL, SWT.FILL, false, true); |
| gd.horizontalIndent= layout.verticalSpacing; |
| gd.heightHint= new PixelConverter(this.submitButton).convertHeightInCharsToPixels(1)+4; |
| gd.verticalSpan= 2; |
| this.submitButton.setLayoutData(gd); |
| this.submitButton.setText(Messages.Console_SubmitButton_label); |
| this.submitButton.addSelectionListener(new SelectionListener() { |
| @Override |
| public void widgetSelected(final SelectionEvent e) { |
| doSubmit(); |
| ConsolePageEditor.this.sourceViewer.getControl().setFocus(); |
| } |
| @Override |
| public void widgetDefaultSelected(final SelectionEvent e) { |
| } |
| }); |
| |
| this.promptHighlighter= new PromptHighlighter(this.prompt, |
| PreferenceUtils.getInstancePrefs(), |
| this.configurator.getSourceViewerConfiguration().getPreferences() ); |
| |
| final Slider slider= new Slider(this.composite, SWT.HORIZONTAL); |
| slider.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 2, 1)); |
| this.scroller= new ScrollControl(slider); |
| |
| setFont(this.consolePage.getConsole().getFont()); |
| |
| this.submitHistoryListener= new IHistoryListener() { |
| @Override |
| public void entryAdded(final History source, final Entry e) { |
| } |
| @Override |
| public void entryRemoved(final History source, final Entry e) { |
| } |
| @Override |
| public void completeChange(final History source, final Entry[] es) { |
| ConsolePageEditor.this.submitHistoryCurrentEntry= null; |
| } |
| }; |
| this.process.getHistory().addListener(this.submitHistoryListener); |
| |
| this.document.addPrenotifiedDocumentListener(new IDocumentListener() { |
| @Override |
| public void documentAboutToBeChanged(final DocumentEvent event) { |
| if (ConsolePageEditor.this.historyCompoundChange != null && !ConsolePageEditor.this.inHistoryChange) { |
| ConsolePageEditor.this.historyCompoundChange= null; |
| ConsolePageEditor.this.sourceViewer.getUndoManager().endCompoundChange(); |
| } |
| } |
| @Override |
| public void documentChanged(final DocumentEvent event) { |
| } |
| }); |
| |
| this.workspaceListener= new ToolWorkspace.Listener() { |
| @Override |
| public void propertyChanged(final ToolWorkspace workspace, final Map<String, Object> properties) { |
| if (properties.containsKey("wd")) { |
| UIAccess.getDisplay(null).asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| ConsolePageEditor.this.statusLine.updateWD(); |
| } |
| }); |
| } |
| } |
| }; |
| this.consolePage.getControl().addListener(SWT.Activate, new Listener() { |
| @Override |
| public void handleEvent(final Event event) { |
| ConsolePageEditor.this.statusLine.refresh(); |
| } |
| }); |
| this.consolePage.getTool().getWorkspaceData().addPropertyListener(this.workspaceListener); |
| this.statusLine.updateWD(); |
| |
| return this.composite; |
| } |
| |
| protected void createSourceViewer(final SourceEditorViewerConfigurator editorConfigurator) { |
| this.configurator= editorConfigurator; |
| this.sourceViewer= new InputSourceViewer(this.composite); |
| this.configurator.setTarget(this); |
| final SourceEditorViewerConfiguration configuration= this.configurator.getSourceViewerConfiguration(); |
| |
| this.sourceViewerDecorationSupport= new org.eclipse.statet.ltk.ui.sourceediting.SourceViewerDecorationSupport( |
| this.sourceViewer, null, null, EditorsUI.getSharedTextColors()); |
| this.configurator.configureSourceViewerDecorationSupport(this.sourceViewerDecorationSupport); |
| // fSourceViewerDecorationSupport.setCursorLinePainterPreferenceKeys( |
| // AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE, |
| // AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE_COLOR); |
| final MarkerAnnotationPreferences markerAnnotationPreferences= EditorsPlugin.getDefault().getMarkerAnnotationPreferences(); |
| for (final Object pref : markerAnnotationPreferences.getAnnotationPreferences()) { |
| this.sourceViewerDecorationSupport.setAnnotationPreference((AnnotationPreference) pref); |
| } |
| this.sourceViewerDecorationSupport.install(configuration.getPreferences()); |
| |
| final IDocumentSetupParticipant docuSetup= this.configurator.getDocumentSetupParticipant(); |
| if (docuSetup != null) { |
| docuSetup.setup(this.document.getMasterDocument()); |
| } |
| |
| new TextViewerEditorColorUpdater(this.sourceViewer, configuration.getPreferences()); |
| |
| final AnnotationModel annotationModel= new AnnotationModel(); |
| // annotationModel.setLockObject(document.getLockObject()); |
| this.sourceViewer.setDocument(this.document, annotationModel); |
| this.sourceViewer.addPostSelectionChangedListener(new ISelectionChangedListener() { |
| @Override |
| public void selectionChanged(final SelectionChangedEvent event) { |
| ConsolePageEditor.this.statusLine.cleanStatusLine(); |
| } |
| }); |
| this.sourceViewer.getTextWidget().addListener(SWT.FocusOut, new Listener() { |
| @Override |
| public void handleEvent(final Event event) { |
| ConsolePageEditor.this.statusLine.cleanStatusLine(); |
| } |
| }); |
| |
| this.customCarretSupport= new TextViewerCustomCaretSupport(this.sourceViewer, |
| configuration.getPreferences() ); |
| } |
| |
| @Override |
| public void handleSettingsChanged(final Set<String> groupIds, final Map<String, Object> options) { |
| this.configurator.handleSettingsChanged(groupIds, options); |
| if (groupIds.contains(ConsolePreferences.GROUP_ID)) { |
| updateSettings(); |
| } |
| if (groupIds.contains(ConsolePreferences.OUTPUT_TEXTSTYLE_GROUP_ID) |
| && this.promptHighlighter != null) { |
| this.promptHighlighter.updateSettings(); |
| this.promptHighlighter.updateControl(); |
| } |
| } |
| |
| public void initActions(final IServiceLocator serviceLocator, final ContextHandlers handlers) { |
| final StyledText textWidget= this.sourceViewer.getTextWidget(); |
| |
| WorkbenchUIUtils.activateContext(serviceLocator, |
| "org.eclipse.statet.nico.contexts.ConsoleEditor" ); //$NON-NLS-1$ |
| |
| final IHandlerService handlerService= serviceLocator.getService(IHandlerService.class); |
| |
| TextHandlerUtil.disable(textWidget, ITextEditorActionDefinitionIds.DELETE_NEXT); |
| |
| // Line actions: goto, select, cut, delete |
| { final IHandler2 handler= new GotoLineBeginHandler(this); |
| handlers.addActivate(ITextEditorActionDefinitionIds.LINE_START, handler); |
| } |
| { final IHandler2 handler= new GotoLineEndHandler(this); |
| handlers.addActivate(ITextEditorActionDefinitionIds.LINE_END, handler); |
| } |
| { final IHandler2 handler= new SelectLineBeginHandler(this); |
| handlers.addActivate(ITextEditorActionDefinitionIds.SELECT_LINE_START, handler); |
| } |
| { final IHandler2 handler= new SelectLineEndHandler(this); |
| handlers.addActivate(ITextEditorActionDefinitionIds.SELECT_LINE_END, handler); |
| } |
| { final IHandler2 handler= new CutLineHandler(this); |
| handlers.addActivate(ITextEditorActionDefinitionIds.CUT_LINE, handler); |
| } |
| { final IHandler2 handler= new CutToLineBeginHandler(this); |
| handlers.addActivate(ITextEditorActionDefinitionIds.CUT_LINE_TO_BEGINNING, handler); |
| } |
| { final IHandler2 handler= new CutToLineEndHandler(this); |
| handlers.addActivate(ITextEditorActionDefinitionIds.CUT_LINE_TO_END, handler); |
| } |
| { final IHandler2 handler= new DeleteLineHandler(this); |
| handlers.addActivate(ITextEditorActionDefinitionIds.DELETE_LINE, handler); |
| } |
| { final IHandler2 handler= new DeleteToLineBeginHandler(this); |
| handlers.addActivate(ITextEditorActionDefinitionIds.DELETE_LINE_TO_BEGINNING, handler); |
| } |
| { final IHandler2 handler= new DeleteToLineEndHandler(this); |
| handlers.addActivate(ITextEditorActionDefinitionIds.DELETE_LINE_TO_END, handler); |
| } |
| |
| // Word actions: goto, select, delete |
| { final IHandler2 handler= new GotoPreviousWordHandler(this); |
| TextHandlerUtil.disable(textWidget, ITextEditorActionDefinitionIds.WORD_PREVIOUS); |
| handlerService.activateHandler(ITextEditorActionDefinitionIds.WORD_PREVIOUS, handler); |
| } |
| { final IHandler2 handler= new GotoNextWordHandler(this); |
| TextHandlerUtil.disable(textWidget, ITextEditorActionDefinitionIds.WORD_NEXT); |
| handlerService.activateHandler(ITextEditorActionDefinitionIds.WORD_NEXT, handler); |
| } |
| { final IHandler2 handler= new SelectPreviousWordHandler(this); |
| TextHandlerUtil.disable(textWidget, ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS); |
| handlerService.activateHandler(ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS, handler); |
| } |
| { final IHandler2 handler= new SelectNextWordHandler(this); |
| TextHandlerUtil.disable(textWidget, ITextEditorActionDefinitionIds.SELECT_WORD_NEXT); |
| handlerService.activateHandler(ITextEditorActionDefinitionIds.SELECT_WORD_NEXT, handler); |
| } |
| { final IHandler2 handler= new DeletePreviousWordHandler(this); |
| TextHandlerUtil.disable(textWidget, ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD); |
| handlerService.activateHandler(ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD, handler); |
| } |
| { final IHandler2 handler= new DeleteNextWordHandler(this); |
| TextHandlerUtil.disable(textWidget, ITextEditorActionDefinitionIds.DELETE_NEXT_WORD); |
| handlerService.activateHandler(ITextEditorActionDefinitionIds.DELETE_NEXT_WORD, handler); |
| } |
| |
| final ICharPairMatcher matcher= this.configurator.getSourceViewerConfiguration().getPairMatcher(); |
| if (matcher != null) { |
| final IHandler2 handler= new GotoMatchingBracketHandler(matcher, this); |
| handlers.addActivate(ISourceEditorCommandIds.GOTO_MATCHING_BRACKET, handler); |
| } |
| |
| // Assists |
| { final IHandler2 handler= new SourceEditorOperationHandler(this, ISourceViewer.CONTENTASSIST_PROPOSALS); |
| handlers.addActivate(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS, handler); |
| } |
| { final IHandler2 handler= new SpecificContentAssistHandler(this, |
| this.configurator.getSourceViewerConfiguration().getContentAssist() ); |
| handlers.addActivate(ISourceEditorCommandIds.SPECIFIC_CONTENT_ASSIST_COMMAND_ID, handler); |
| } |
| { final IHandler2 handler= new SourceEditorOperationHandler(this, ISourceViewer.CONTENTASSIST_CONTEXT_INFORMATION); |
| handlers.addActivate(ITextEditorActionDefinitionIds.CONTENT_ASSIST_CONTEXT_INFORMATION, handler); |
| } |
| { final IHandler2 handler= new InformationDispatchHandler(getViewer()); |
| handlers.addActivate(ITextEditorActionDefinitionIds.SHOW_INFORMATION, handler); |
| } |
| |
| this.selectionHistory= new StructureSelectionHistory(this); |
| handlerService.activateHandler(ISourceEditorCommandIds.SELECT_ENCLOSING, |
| new StructureSelectHandler.Enclosing(this, this.selectionHistory)); |
| handlerService.activateHandler(ISourceEditorCommandIds.SELECT_PREVIOUS, |
| new StructureSelectHandler.Previous(this, this.selectionHistory)); |
| handlerService.activateHandler(ISourceEditorCommandIds.SELECT_NEXT, |
| new StructureSelectHandler.Next(this, this.selectionHistory)); |
| final StructureSelectionHistoryBackHandler backHandler= new StructureSelectionHistoryBackHandler(this, this.selectionHistory); |
| handlerService.activateHandler(ISourceEditorCommandIds.SELECT_LAST, backHandler); |
| this.selectionHistory.addUpdateListener(backHandler); |
| |
| { final IHandler2 handler= new AbstractHandler() { |
| @Override |
| public Object execute(final ExecutionEvent arg0) |
| throws ExecutionException { |
| doHistoryOlder(getLineStart()); |
| return null; |
| } |
| }; |
| textWidget.setKeyBinding(KEY_HIST_SPREFIX_UP, SWT.NULL); |
| handlerService.activateHandler("org.eclipse.statet.nico.commands.SearchHistoryOlder", handler); |
| } |
| { final IHandler2 handler= new AbstractHandler() { |
| @Override |
| public Object execute(final ExecutionEvent arg0) |
| throws ExecutionException { |
| doHistoryNewer(getLineStart()); |
| return null; |
| } |
| }; |
| textWidget.setKeyBinding(KEY_HIST_SPREFIX_DOWN, SWT.NULL); |
| handlerService.activateHandler("org.eclipse.statet.nico.commands.SearchHistoryNewer", handler); |
| } |
| { final IHandler2 handler= new AbstractHandler() { |
| @Override |
| public Object execute(final ExecutionEvent arg0) |
| throws ExecutionException { |
| doHistoryNewest(); |
| return null; |
| } |
| }; |
| handlerService.activateHandler("org.eclipse.statet.nico.commands.GotoHistoryNewest", handler); |
| } |
| |
| this.customCarretSupport.initActions(handlerService); |
| } |
| |
| |
| void setFont(final Font font) { |
| this.prompt.setFont(font); |
| this.sourceViewer.getControl().setFont(font); |
| } |
| |
| void updateBusy(final boolean isBusy) { |
| final PromptHighlighter prefixRenderer= this.promptHighlighter; |
| if (prefixRenderer != null) { |
| prefixRenderer.setHighlight(!isBusy); |
| } |
| } |
| |
| /** |
| * @param prompt new prompt or null. |
| */ |
| void updatePrompt(final Prompt prompt) { |
| final Prompt p= (prompt != null) ? prompt : this.process.getWorkspaceData().getPrompt(); |
| if (UIAccess.isOkToUse(this.prompt)) { |
| int start= 0; |
| for (int i= 0; i < p.text.length(); i++) { |
| final char c= p.text.charAt(i); |
| if (c < 32 && c != '\t') { |
| start= i+1; |
| } |
| else { |
| break; |
| } |
| } |
| final String newText= (start == 0) ? p.text : p.text.substring(start); |
| final String oldText= this.prompt.getText(); |
| if (!oldText.equals(newText)) { |
| this.prompt.setText(newText); |
| if (oldText.length() != newText.length()) { // assuming monospace font |
| getComposite().layout(new Control[] { this.prompt }); |
| } |
| } |
| onPromptUpdate(p); |
| } |
| } |
| |
| protected void onPromptUpdate(final Prompt prompt) { |
| } |
| |
| protected void setInputPrefix(final String source) { |
| this.document.setPrefix(source); |
| } |
| |
| private String getLineStart() { |
| try { |
| return this.document.get(0, this.sourceViewer.getSelectedRange().x); |
| } catch (final BadLocationException e) { |
| NicoUIPlugin.logError(-1, "Error while extracting prefix for history search", e); //$NON-NLS-1$ |
| return ""; //$NON-NLS-1$ |
| } |
| } |
| |
| |
| public void doHistoryNewer(final String prefix) { |
| if (this.submitHistoryCurrentEntry == null) { |
| return; |
| } |
| |
| History.Entry next= this.submitHistoryCurrentEntry.getNewer(); |
| final EnumSet<SubmitType> filter= this.submitHistoryTypesFilter; |
| SubmitType type; |
| while (next != null |
| && ( ((type= next.getSubmitType()) != null && !filter.contains(type)) |
| || (next.getCommandMarker() < 0) |
| || (prefix != null && !next.getCommand().startsWith(prefix)) )) { |
| next= next.getNewer(); |
| } |
| |
| if (next == null && prefix != null) { |
| Display.getCurrent().beep(); |
| return; |
| } |
| |
| this.submitHistoryCurrentEntry= next; |
| setHistoryContent((this.submitHistoryCurrentEntry != null) ? |
| this.submitHistoryCurrentEntry.getCommand() : ""); //$NON-NLS-1$ |
| } |
| |
| public void doHistoryOlder(final String prefix) { |
| History.Entry next; |
| if (this.submitHistoryCurrentEntry != null) { |
| next= this.submitHistoryCurrentEntry.getOlder(); |
| } |
| else { |
| next= this.process.getHistory().getNewest(); |
| } |
| final EnumSet<SubmitType> filter= this.submitHistoryTypesFilter; |
| SubmitType type; |
| while (next != null |
| && ( ((type= next.getSubmitType()) != null && !filter.contains(type)) |
| || (next.getCommandMarker() < 0) |
| || (prefix != null && !next.getCommand().startsWith(prefix)) )) { |
| next= next.getOlder(); |
| } |
| |
| if (next == null) { |
| Display.getCurrent().beep(); |
| return; |
| } |
| |
| this.submitHistoryCurrentEntry= next; |
| setHistoryContent(this.submitHistoryCurrentEntry.getCommand()); |
| } |
| |
| public void doHistoryNewest() { |
| final History.Entry next= this.process.getHistory().getNewest(); |
| if (next == null) { |
| Display.getCurrent().beep(); |
| return; |
| } |
| this.submitHistoryCurrentEntry= next; |
| setHistoryContent(this.submitHistoryCurrentEntry.getCommand()); |
| } |
| |
| protected void setHistoryContent(final String text) { |
| if (this.historyCompoundChange == null) { |
| this.historyCompoundChange= this.sourceViewer.getSelectedRange(); |
| this.sourceViewer.getUndoManager().beginCompoundChange(); |
| } |
| else { |
| final Point current= this.sourceViewer.getSelectedRange(); |
| if (!current.equals(this.historyCaretWorkaround)) { |
| this.historyCompoundChange= current; |
| } |
| } |
| this.inHistoryChange= true; |
| try { |
| final StyledText widget= this.sourceViewer.getTextWidget(); |
| if (UIAccess.isOkToUse(widget)) { |
| widget.setRedraw(false); |
| this.document.set(text); |
| this.sourceViewer.setSelectedRange(this.historyCompoundChange.x, this.historyCompoundChange.y); |
| widget.setRedraw(true); |
| } |
| else { |
| this.document.set(text); |
| } |
| |
| this.historyCaretWorkaround= this.sourceViewer.getSelectedRange(); |
| } |
| finally { |
| this.inHistoryChange= false; |
| } |
| } |
| |
| public void doSubmit() { |
| final String content= this.document.get(); |
| final ToolController controller= this.process.getController(); |
| |
| if (controller != null) { |
| final Status status= controller.submit(content, SubmitType.CONSOLE); |
| if (status.getSeverity() >= Status.ERROR) { |
| this.statusLine.setMessage(true, status.getMessage(), null); |
| Display.getCurrent().beep(); |
| return; |
| } |
| clear(); |
| } |
| } |
| |
| public void clear() { |
| this.sourceViewer.clear(); |
| this.submitHistoryCurrentEntry= null; |
| this.historyCompoundChange= null; |
| this.sourceViewer.resetPlugins(); |
| this.scroller.reset(); |
| this.selectionHistory.flush(); |
| } |
| |
| public Composite getComposite() { |
| return this.composite; |
| } |
| |
| public Button getSubmitButton() { |
| return this.submitButton; |
| } |
| |
| protected NIConsolePage getConsolePage() { |
| return this.consolePage; |
| } |
| |
| public void dispose() { |
| this.process.getHistory().removeListener(this.submitHistoryListener); |
| this.submitHistoryListener= null; |
| this.submitHistoryCurrentEntry= null; |
| |
| if (this.workspaceListener != null) { |
| this.consolePage.getTool().getWorkspaceData().removePropertyListener(this.workspaceListener); |
| this.workspaceListener= null; |
| } |
| |
| if (this.sourceViewerDecorationSupport != null) { |
| this.sourceViewerDecorationSupport.dispose(); |
| this.sourceViewerDecorationSupport= null; |
| } |
| this.process= null; |
| this.consolePage= null; |
| |
| if (this.promptHighlighter != null) { |
| this.promptHighlighter.dispose(); |
| this.promptHighlighter= null; |
| } |
| } |
| |
| |
| /*- Complete ISourceEditor --------------------------------------------------*/ |
| |
| @Override |
| public IContentType getContentType() { |
| return this.contentType; |
| } |
| |
| public String getModelTypeId() { |
| return this.sourceUnit.getModelTypeId(); |
| } |
| |
| @Override |
| public ISourceUnit getSourceUnit() { |
| return this.sourceUnit; |
| } |
| |
| @Override |
| public IWorkbenchPart getWorkbenchPart() { |
| return this.consolePage.getView(); |
| } |
| |
| @Override |
| public IServiceLocator getServiceLocator() { |
| return this.consolePage.inputServices.getLocator(); |
| } |
| |
| @Override |
| public InputSourceViewer getViewer() { |
| return this.sourceViewer; |
| } |
| |
| @Override |
| public IDocContentSections getDocumentContentInfo() { |
| return this.configurator.getDocumentContentInfo(); |
| } |
| |
| @Override |
| public boolean isEditable(final boolean validate) { |
| return true; |
| } |
| |
| @Override |
| public void selectAndReveal(final int offset, final int length) { |
| if (UIAccess.isOkToUse(this.sourceViewer)) { |
| this.sourceViewer.setSelectedRange(offset, length); |
| this.sourceViewer.revealRange(offset, length); |
| } |
| } |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public <T> T getAdapter(final Class<T> adapterType) { |
| if (adapterType == ISourceEditor.class) { |
| return (T) this; |
| } |
| if (adapterType == IEditorStatusLine.class) { |
| return (T) this.statusLine; |
| } |
| if (adapterType == ITextOperationTarget.class) { |
| return (T) this.sourceViewer; |
| } |
| |
| if (adapterType == IContentType.class) { |
| return (T) this.contentType; |
| } |
| |
| return this.consolePage.getAdapter(adapterType); |
| } |
| |
| @Override |
| public ITextEditToolSynchronizer getTextEditToolSynchronizer() { |
| return null; |
| } |
| |
| } |