| /*=============================================================================# |
| # Copyright (c) 2012, 2018 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.internal.docmlet.wikitext.ui.sourceediting; |
| |
| import static org.eclipse.statet.docmlet.wikitext.core.source.IWikitextDocumentConstants.WIKIDOC_DEFAULT_CONTENT_CONSTRAINT; |
| import static org.eclipse.statet.ecommons.text.ui.BracketLevel.AUTODELETE; |
| |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.jface.text.AbstractDocument; |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.BadPartitioningException; |
| import org.eclipse.jface.text.Document; |
| import org.eclipse.jface.text.DocumentCommand; |
| import org.eclipse.jface.text.DocumentRewriteSession; |
| import org.eclipse.jface.text.DocumentRewriteSessionType; |
| import org.eclipse.jface.text.IPositionUpdater; |
| import org.eclipse.jface.text.IRegion; |
| import org.eclipse.jface.text.Position; |
| import org.eclipse.jface.text.TextUtilities; |
| import org.eclipse.jface.text.link.LinkedModeModel; |
| import org.eclipse.jface.text.link.LinkedModeUI; |
| import org.eclipse.jface.text.link.LinkedPosition; |
| import org.eclipse.jface.text.link.LinkedPositionGroup; |
| import org.eclipse.swt.events.KeyEvent; |
| import org.eclipse.text.edits.TextEdit; |
| |
| import org.eclipse.statet.jcommons.collections.ImCollections; |
| import org.eclipse.statet.jcommons.text.core.BasicTextRegion; |
| import org.eclipse.statet.jcommons.text.core.TextRegion; |
| |
| import org.eclipse.statet.ecommons.preferences.core.PreferenceAccess; |
| import org.eclipse.statet.ecommons.text.IIndentSettings; |
| import org.eclipse.statet.ecommons.text.IndentUtil; |
| import org.eclipse.statet.ecommons.text.IndentUtil.ILineIndent; |
| import org.eclipse.statet.ecommons.text.IndentUtil.IndentEditAction; |
| import org.eclipse.statet.ecommons.text.core.treepartitioner.ITreePartitionNode; |
| import org.eclipse.statet.ecommons.text.core.treepartitioner.TreePartition; |
| import org.eclipse.statet.ecommons.text.core.treepartitioner.TreePartitionUtils; |
| import org.eclipse.statet.ecommons.text.core.util.NonDeletingPositionUpdater; |
| import org.eclipse.statet.ecommons.ui.ISettingsChangedHandler; |
| |
| import org.eclipse.statet.docmlet.wikitext.core.IWikitextCoreAccess; |
| import org.eclipse.statet.docmlet.wikitext.core.WikitextCodeStyleSettings; |
| import org.eclipse.statet.docmlet.wikitext.core.ast.Block; |
| import org.eclipse.statet.docmlet.wikitext.core.ast.SourceComponent; |
| import org.eclipse.statet.docmlet.wikitext.core.ast.WikidocParser; |
| import org.eclipse.statet.docmlet.wikitext.core.ast.WikitextAstNode; |
| import org.eclipse.statet.docmlet.wikitext.core.markup.IMarkupConfig; |
| import org.eclipse.statet.docmlet.wikitext.core.markup.IMarkupLanguage; |
| import org.eclipse.statet.docmlet.wikitext.core.source.HardLineWrap; |
| import org.eclipse.statet.docmlet.wikitext.core.source.IMarkupSourceFormatAdapter; |
| import org.eclipse.statet.docmlet.wikitext.core.source.IWikitextDocumentConstants; |
| import org.eclipse.statet.docmlet.wikitext.core.source.WikidocDocumentSetupParticipant; |
| import org.eclipse.statet.docmlet.wikitext.core.source.WikitextHeuristicTokenScanner; |
| import org.eclipse.statet.docmlet.wikitext.core.source.WikitextPartitionNodeType; |
| import org.eclipse.statet.docmlet.wikitext.core.source.extdoc.AbstractMarkupConfig; |
| import org.eclipse.statet.docmlet.wikitext.core.source.extdoc.IExtdocMarkupLanguage; |
| import org.eclipse.statet.docmlet.wikitext.ui.sourceediting.WikitextEditingSettings; |
| import org.eclipse.statet.ltk.ast.core.AstNode; |
| import org.eclipse.statet.ltk.ast.core.util.AstSelection; |
| import org.eclipse.statet.ltk.core.SourceContent; |
| import org.eclipse.statet.ltk.ui.sourceediting.AbstractAutoEditStrategy; |
| import org.eclipse.statet.ltk.ui.sourceediting.ISmartInsertSettings; |
| import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditor; |
| |
| |
| /** |
| * Auto edit strategy for Wikitext markup |
| */ |
| public class MarkupAutoEditStrategy extends AbstractAutoEditStrategy { |
| |
| |
| public static final class Settings implements ISmartInsertSettings, ISettingsChangedHandler { |
| |
| private final IWikitextCoreAccess coreAccess; |
| |
| private boolean enabledByDefault; |
| private TabAction tabAction; |
| private boolean closeBrackets; |
| private boolean closeParenthesis; |
| private boolean closeMathDollar; |
| |
| private boolean hardWrapText; |
| private HardWrapMode hardWrapMode; |
| |
| |
| public Settings(final IWikitextCoreAccess coreAccess) { |
| this.coreAccess= coreAccess; |
| updateSettings(); |
| } |
| |
| |
| @Override |
| public void handleSettingsChanged(final Set<String> groupIds, final Map<String, Object> options) { |
| if (groupIds == null || groupIds.contains(WikitextEditingSettings.SMARTINSERT_GROUP_ID)) { |
| updateSettings(); |
| } |
| } |
| |
| private void updateSettings() { |
| final PreferenceAccess prefs= this.coreAccess.getPrefs(); |
| this.enabledByDefault= prefs.getPreferenceValue(WikitextEditingSettings.SMARTINSERT_BYDEFAULT_ENABLED_PREF); |
| this.tabAction= prefs.getPreferenceValue(WikitextEditingSettings.SMARTINSERT_TAB_ACTION_PREF); |
| this.closeBrackets= prefs.getPreferenceValue(WikitextEditingSettings.SMARTINSERT_CLOSEBRACKETS_ENABLED_PREF); |
| this.closeParenthesis= prefs.getPreferenceValue(WikitextEditingSettings.SMARTINSERT_CLOSEPARENTHESIS_ENABLED_PREF); |
| this.closeMathDollar= prefs.getPreferenceValue(WikitextEditingSettings.SMARTINSERT_CLOSEMATHDOLLAR_ENABLED_PREF); |
| this.hardWrapText= prefs.getPreferenceValue(WikitextEditingSettings.SMARTINSERT_HARDWRAP_TEXT_ENABLED_PREF); |
| this.hardWrapMode= prefs.getPreferenceValue(WikitextEditingSettings.SMARTINSERT_HARDWRAP_MODE_PREF); |
| } |
| |
| @Override |
| public boolean isSmartInsertEnabledByDefault() { |
| return this.enabledByDefault; |
| } |
| |
| @Override |
| public TabAction getSmartInsertTabAction() { |
| return this.tabAction; |
| } |
| |
| public HardWrapMode getSmartInsertHardWrapMode() { |
| return this.hardWrapMode; |
| } |
| |
| } |
| |
| private static final char[] CURLY_BRACKET_TYPE= new char[] { '{', '}' }; |
| private static final char[] SQUARE_BRACKET_TYPE= new char[] { '[', ']' }; |
| private static final char[] PARATHESIS_TYPE= new char[] { '[', ']' }; |
| |
| private static final WikidocParser DEFAULT_PARSER= new WikidocParser(null); |
| |
| private static final String POSITION_CATEGORY= "org.eclipse.statet.docmlet.wikitext.MarkupAutoEdit"; //$NON-NLS-1$ |
| private static final IPositionUpdater POSITION_UPDATER= new NonDeletingPositionUpdater(POSITION_CATEGORY); |
| |
| |
| private final IWikitextCoreAccess wikitextCoreAccess; |
| private final Settings settings; |
| |
| private WikitextHeuristicTokenScanner scanner; |
| private WikitextCodeStyleSettings wikitextCodeStyle; |
| |
| private final HardLineWrap hardLineWrap; |
| |
| |
| public MarkupAutoEditStrategy(final IWikitextCoreAccess coreAccess, final ISourceEditor editor) { |
| super(editor); |
| assert (coreAccess != null); |
| |
| this.wikitextCoreAccess= coreAccess; |
| this.settings= new Settings(coreAccess); |
| this.hardLineWrap= new HardLineWrap(getDocumentContentInfo(), this.wikitextCoreAccess); |
| } |
| |
| |
| @Override |
| public Settings getSettings() { |
| return this.settings; |
| } |
| |
| @Override |
| protected IIndentSettings getCodeStyleSettings() { |
| return this.wikitextCodeStyle; |
| } |
| |
| |
| @Override |
| protected TreePartition initCustomization(final int offset, final int ch) |
| throws BadLocationException, BadPartitioningException { |
| if (this.scanner == null) { |
| this.scanner= createScanner(); |
| } |
| this.wikitextCodeStyle= this.wikitextCoreAccess.getWikitextCodeStyle(); |
| |
| return super.initCustomization(offset, ch); |
| } |
| |
| protected WikitextHeuristicTokenScanner createScanner() { |
| return WikitextHeuristicTokenScanner.create(getDocumentContentInfo()); |
| } |
| |
| @Override |
| protected TextRegion computeValidRange(final int offset, final TreePartition partition, final int ch) { |
| ITreePartitionNode node= partition.getTreeNode(); |
| if (node.getType() instanceof WikitextPartitionNodeType) { |
| if (getDocumentContentInfo().getPrimaryType() == IWikitextDocumentConstants.WIKIDOC_PARTITIONING) { |
| return super.computeValidRange(offset, partition, ch); |
| } |
| else { |
| ITreePartitionNode parent; |
| while ((parent= node.getParent()) != null |
| && parent.getType() instanceof WikitextPartitionNodeType) { |
| node= parent; |
| } |
| return node; |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| protected WikitextHeuristicTokenScanner getScanner() { |
| return this.scanner; |
| } |
| |
| @Override |
| protected void quitCustomization() { |
| super.quitCustomization(); |
| |
| this.wikitextCodeStyle= null; |
| } |
| |
| |
| private final boolean isClosedBracket(final int backwardOffset, final int forwardOffset, |
| final String currentPartition, final char[] type) { |
| try { |
| final AbstractDocument doc= getDocument(); |
| this.scanner.configure(doc, currentPartition); |
| final IRegion line= doc.getLineInformationOfOffset(forwardOffset); |
| final int balance= this.scanner.computePairBalance( |
| backwardOffset, line.getOffset(), |
| forwardOffset, line.getOffset() + line.getLength(), |
| 1, type, '\\' ); |
| return (balance <= 0); |
| } |
| catch (final BadLocationException e) { |
| return true; |
| } |
| } |
| |
| private boolean isValueChar(final int offset) throws BadLocationException { |
| final int ch= getChar(offset); |
| return (ch != -1 && Character.isLetterOrDigit(ch)); |
| } |
| |
| protected final IMarkupLanguage getMarkupLanguage() { |
| return WikidocDocumentSetupParticipant.getMarkupLanguage(getDocument(), |
| getDocumentContentInfo().getPartitioning() ); |
| } |
| |
| protected final IMarkupConfig getMarkupConfig() { |
| final IMarkupLanguage markupLanguage= getMarkupLanguage(); |
| if (markupLanguage != null) { |
| return markupLanguage.getMarkupConfig(); |
| } |
| return null; |
| } |
| |
| protected final IExtdocMarkupLanguage getExtdocMarkupLanguage() { |
| final IMarkupLanguage markupLanguage= getMarkupLanguage(); |
| if (markupLanguage instanceof IExtdocMarkupLanguage) { |
| return (IExtdocMarkupLanguage) markupLanguage; |
| } |
| return null; |
| } |
| |
| |
| @Override |
| protected char isCustomizeKey(final KeyEvent event) { |
| switch (event.character) { |
| case '{': |
| case '(': |
| case '[': |
| case '$': |
| return event.character; |
| case '\t': |
| if (event.stateMask == 0) { |
| return '\t'; |
| } |
| break; |
| case 0x0A: |
| case 0x0D: |
| if (getEditor3() != null) { |
| return '\n'; |
| } |
| break; |
| default: |
| break; |
| } |
| return 0; |
| } |
| |
| @Override |
| protected void doCustomizeKeyCommand(final char ch, final DocumentCommand command, |
| final TreePartition partition) throws Exception { |
| final String contentType= partition.getType(); |
| final int cEnd= command.offset + command.length; |
| int linkedModeType= -1; |
| int linkedModeOffset= -1; |
| |
| KEY: switch (ch) { |
| case '\t': |
| if (WIKIDOC_DEFAULT_CONTENT_CONSTRAINT.matches(contentType) |
| && isRegularTabCommand(command) ) { |
| command.text= "\t"; //$NON-NLS-1$ |
| smartInsertOnTab(command, true); |
| break KEY; |
| } |
| return; |
| case '{': |
| if (WIKIDOC_DEFAULT_CONTENT_CONSTRAINT.matches(contentType) |
| && !WikitextHeuristicTokenScanner.isEscaped(getDocument(), command.offset) ) { |
| command.text= "{"; //$NON-NLS-1$ |
| if (this.settings.closeBrackets && !isValueChar(cEnd)) { |
| if (!isClosedBracket(command.offset, cEnd, contentType, CURLY_BRACKET_TYPE)) { |
| command.text= "{}"; //$NON-NLS-1$ |
| linkedModeType= 2 | AUTODELETE; |
| } |
| else if (getChar(cEnd) == '}') { |
| linkedModeType= 2; |
| } |
| } |
| break KEY; |
| } |
| return; |
| case '[': |
| if (WIKIDOC_DEFAULT_CONTENT_CONSTRAINT.matches(contentType) |
| && !WikitextHeuristicTokenScanner.isEscaped(getDocument(), command.offset) ) { |
| command.text= "["; //$NON-NLS-1$ |
| if (this.settings.closeBrackets && !isValueChar(cEnd)) { |
| if (!isClosedBracket(command.offset, cEnd, contentType, SQUARE_BRACKET_TYPE)) { |
| command.text= "[]"; //$NON-NLS-1$ |
| linkedModeType= 2 | AUTODELETE; |
| } |
| else if (getChar(cEnd) == ']') { |
| linkedModeType= 2; |
| } |
| } |
| break KEY; |
| } |
| return; |
| case '(': |
| if (WIKIDOC_DEFAULT_CONTENT_CONSTRAINT.matches(contentType) |
| && !WikitextHeuristicTokenScanner.isEscaped(getDocument(), command.offset) ) { |
| command.text= "("; //$NON-NLS-1$ |
| if (this.settings.closeParenthesis && !isValueChar(cEnd)) { |
| if (!isClosedBracket(command.offset, cEnd, contentType, PARATHESIS_TYPE)) { |
| command.text= "()"; //$NON-NLS-1$ |
| linkedModeType= 2 | AUTODELETE; |
| } |
| else if (getChar(cEnd) == ')') { |
| linkedModeType= 2; |
| } |
| } |
| break KEY; |
| } |
| return; |
| case '$': |
| if ((WIKIDOC_DEFAULT_CONTENT_CONSTRAINT.matches(contentType)) |
| && !WikitextHeuristicTokenScanner.isEscaped(getDocument(), command.offset) ) { |
| command.text= "$"; //$NON-NLS-1$ |
| if (this.settings.closeMathDollar && !isValueChar(cEnd)) { |
| final IMarkupConfig markupConfig= getMarkupConfig(); |
| if (markupConfig instanceof AbstractMarkupConfig<?> |
| && ((AbstractMarkupConfig) markupConfig).isTexMathDollarsEnabled() ) { |
| command.text= "$$"; //$NON-NLS-1$ |
| linkedModeType= 2 | AUTODELETE; |
| } |
| } |
| break KEY; |
| } |
| return; |
| case '\n': |
| if (WIKIDOC_DEFAULT_CONTENT_CONSTRAINT.matches(contentType)) { |
| command.text= TextUtilities.getDefaultLineDelimiter(getDocument()); |
| smartIndentOnNewLine(command); |
| break KEY; |
| } |
| break; |
| default: |
| assert (false); |
| return; |
| } |
| |
| if (command.doit && command.text.length() > 0 && getEditor().isEditable(true)) { |
| getViewer().getTextWidget().setRedraw(false); |
| try { |
| applyCommand(command); |
| updateSelection(command); |
| |
| if (linkedModeType >= 0) { |
| if (linkedModeOffset < 0) { |
| linkedModeOffset= command.offset; |
| } |
| createLinkedMode(linkedModeOffset, ch, linkedModeType).enter(); |
| } |
| } |
| finally { |
| getViewer().getTextWidget().setRedraw(true); |
| } |
| } |
| } |
| |
| @Override |
| protected void doCustomizeOtherCommand(final DocumentCommand command, final TreePartition partition) |
| throws Exception { |
| final String contentType= partition.getType(); |
| |
| if (WIKIDOC_DEFAULT_CONTENT_CONSTRAINT.matches(contentType)) { |
| if (command.length == 0 && TextUtilities.equals(getDocument().getLegalLineDelimiters(), command.text) != -1) { |
| smartIndentOnNewLine(command); |
| } |
| else if (this.settings.hardWrapText) { |
| smartLineWrap(command); |
| } |
| } |
| } |
| |
| protected void smartIndentOnNewLine(final DocumentCommand command) throws Exception { |
| customizeCommandDefault(command); |
| } |
| |
| protected void smartLineWrap(final DocumentCommand command) |
| throws Exception { |
| |
| final AbstractDocument doc= getDocument(); |
| final int lineNum= doc.getLineOfOffset(command.offset); |
| final IRegion lineInfo= doc.getLineInformation(lineNum); |
| IndentUtil indentUtil= null; |
| |
| byte processMode= 0; |
| switch (this.settings.getSmartInsertHardWrapMode()) { |
| case UPTO_CURSOR: |
| if (!containsControl(command.text)) { |
| indentUtil= createIndentUtil(doc); |
| int column= indentUtil.getColumn(lineNum, command.offset); |
| column= indentUtil.getColumn(command.text, command.text.length(), column); |
| if (column > this.wikitextCodeStyle.getLineWidth()) { |
| processMode= HardLineWrap.SELECTION_STRICT; |
| } |
| } |
| break; |
| case MERGE: |
| if (containsControl(command.text) // multiline command |
| || (command.offset + command.length > lineInfo.getOffset() + lineInfo.getLength()) ) { |
| processMode= HardLineWrap.SELECTION_MERGE1; |
| } |
| else { |
| indentUtil= createIndentUtil(doc); |
| int column= indentUtil.getColumn(lineNum, command.offset); |
| column= indentUtil.getColumn(command.text, command.text.length(), column); |
| final String tail= doc.get(command.offset + command.length, |
| (lineInfo.getOffset() + lineInfo.getLength()) - (command.offset + command.length)); |
| column= indentUtil.getColumn(tail, tail.length(), column); |
| if (column > this.wikitextCodeStyle.getLineWidth()) { |
| processMode= HardLineWrap.SELECTION_MERGE1; |
| } |
| } |
| break; |
| default: |
| break; |
| } |
| |
| if (processMode != 0) { |
| if (indentUtil == null) { |
| indentUtil= createIndentUtil(doc); |
| } |
| wrapLine(command, processMode, indentUtil); |
| } |
| } |
| |
| protected void wrapLine(final DocumentCommand command, final byte mode, |
| final IndentUtil indentUtil) throws Exception { |
| final IExtdocMarkupLanguage markupLanguage= getExtdocMarkupLanguage(); |
| final IMarkupSourceFormatAdapter formatAdapter; |
| if (markupLanguage == null |
| || (formatAdapter= markupLanguage.getSourceFormatAdapter()) == null) { |
| return; |
| } |
| final AbstractDocument doc= getDocument(); |
| |
| final TextEdit textEdit; |
| { final TextRegion workRegion= getFastParseRegion(command); |
| final SourceContent sourceContent= createSourceContent(doc, workRegion, command); |
| final Document workDoc= new Document(sourceContent.getText()); |
| |
| final WikidocParser parser= DEFAULT_PARSER; |
| parser.setMarkupLanguage(markupLanguage); |
| final SourceComponent sourceNode= parser.parse(sourceContent); |
| |
| textEdit= this.hardLineWrap.createTextEdit(workDoc, sourceNode, new BasicTextRegion( |
| doc.getLineOffset(doc.getLineOfOffset(command.offset)) - workRegion.getStartOffset(), |
| command.offset + command.text.length() - workRegion.getStartOffset() ), |
| mode, formatAdapter, createIndentUtil(workDoc) ); |
| if (textEdit == null) { |
| return; |
| } |
| textEdit.moveTree(workRegion.getStartOffset()); |
| } |
| |
| final DocumentRewriteSession rewriteSession= doc.startRewriteSession(DocumentRewriteSessionType.UNRESTRICTED_SMALL); |
| try { |
| doc.addPositionCategory(POSITION_CATEGORY); |
| doc.addPositionUpdater(POSITION_UPDATER); |
| |
| applyCommand(command); |
| |
| final Position offsetPosition= new Position(command.offset, doc.getLength() - command.offset); |
| doc.addPosition(POSITION_CATEGORY, offsetPosition); |
| if (command.caretOffset == -1) { |
| command.caretOffset= command.offset + command.text.length(); |
| } |
| final Position caretPosition= new Position(command.caretOffset, doc.getLength() - command.caretOffset); |
| doc.addPosition(POSITION_CATEGORY, caretPosition); |
| |
| textEdit.apply(doc, TextEdit.NONE); |
| |
| command.offset= offsetPosition.offset; |
| command.caretOffset= caretPosition.offset; |
| updateSelection(command); |
| } |
| finally { |
| doc.stopRewriteSession(rewriteSession); |
| doc.removePositionUpdater(POSITION_UPDATER); |
| doc.removePositionCategory(POSITION_CATEGORY); |
| } |
| } |
| |
| @Override |
| protected void correctIndent(final DocumentCommand command, final int minColumn, |
| final IndentUtil indentUtil) throws Exception { |
| // At moment only single line for tab indent |
| final IExtdocMarkupLanguage markupLanguage= getExtdocMarkupLanguage(); |
| final IMarkupSourceFormatAdapter formatAdapter; |
| if (markupLanguage == null |
| || (formatAdapter= markupLanguage.getSourceFormatAdapter()) == null) { |
| return; |
| } |
| final AbstractDocument doc= getDocument(); |
| final int lineNum= doc.getLineOfOffset(command.offset); |
| final int lineOffset= doc.getLineOffset(lineNum); |
| |
| final String prefixText; |
| { final TextRegion workRegion= getFastParseRegion(command); |
| final SourceContent sourceContent= createSourceContent(doc, workRegion, command); |
| final Document workDoc= new Document(sourceContent.getText()); |
| |
| final WikidocParser parser= DEFAULT_PARSER; |
| parser.setMarkupLanguage(markupLanguage); |
| final SourceComponent sourceNode= parser.parse(sourceContent); |
| |
| final int offsetInAst= lineOffset - workRegion.getStartOffset(); |
| final AstSelection astSelection= AstSelection.search(sourceNode, offsetInAst, offsetInAst, AstSelection.MODE_COVERING_SAME_LAST); |
| final WikitextAstNode blockNode= getBlockNode(astSelection.getCovering(), offsetInAst); |
| if (blockNode == null) { |
| return; |
| } |
| |
| prefixText= formatAdapter.getPrefixCont(blockNode, createIndentUtil(workDoc)); |
| if (prefixText == null) { |
| return; |
| } |
| } |
| |
| final ILineIndent indent= indentUtil.getIndent(prefixText); |
| if (indent.getIndentColumn() < minColumn) { |
| return; |
| } |
| indentUtil.changeIndent(lineNum, lineNum, new IndentEditAction(indent.getIndentColumn()) { |
| @Override |
| public void doEdit(final int line, final int lineOffset, final int length, final StringBuilder text) |
| throws BadLocationException { |
| command.offset= lineOffset; |
| command.length= length; |
| command.text= (text != null) ? text.toString() : ""; |
| } |
| }); |
| } |
| |
| private TextRegion getFastParseRegion(final DocumentCommand command) { |
| final AbstractDocument doc= getDocument(); |
| final TextRegion validRange= getValidRange(); |
| final ITreePartitionNode rootNode= TreePartitionUtils.getRootNode(doc, |
| getDocumentContentInfo().getPartitioning() ); |
| int childIdx= rootNode.indexOfChild(command.offset); |
| if (childIdx < 0) { |
| childIdx= -(childIdx + 1); |
| } |
| |
| int startOffset= 0; |
| { ITreePartitionNode child; |
| if (childIdx > 0) { |
| child= rootNode.getChild(childIdx - 1); |
| if (child.getType() instanceof WikitextPartitionNodeType) { |
| startOffset= child.getStartOffset(); |
| } |
| else { |
| startOffset= child.getEndOffset(); |
| } |
| } |
| } |
| if (startOffset < validRange.getStartOffset()) { |
| startOffset= validRange.getStartOffset(); |
| } |
| |
| int endOffset= Integer.MAX_VALUE; |
| if (childIdx < rootNode.getChildCount()) { |
| final ITreePartitionNode child= rootNode.getChild(childIdx); |
| if (command.offset + command.length > child.getEndOffset()) { |
| childIdx= rootNode.indexOfChild(command.offset + command.length); |
| if (childIdx < 0) { |
| childIdx= -(childIdx + 1); |
| } |
| } |
| } |
| { ITreePartitionNode child; |
| if (childIdx + 1 < rootNode.getChildCount()) { |
| child= rootNode.getChild(childIdx + 1); |
| if (child.getType() instanceof WikitextPartitionNodeType) { |
| endOffset= child.getEndOffset(); |
| } |
| else { |
| endOffset= child.getStartOffset(); |
| } |
| } |
| } |
| if (endOffset > validRange.getEndOffset()) { |
| endOffset= validRange.getEndOffset(); |
| } |
| return new BasicTextRegion(startOffset, endOffset); |
| } |
| |
| private WikitextAstNode getBlockNode(AstNode node, final int offset) { |
| while (node != null) { |
| if (node instanceof Block && node.getStartOffset() < offset) { |
| return (Block) node; |
| } |
| node= node.getParent(); |
| } |
| return null; |
| } |
| |
| |
| private LinkedModeUI createLinkedMode(final int offset, final char type, final int mode) |
| throws BadLocationException { |
| final LinkedModeModel model= new LinkedModeModel(); |
| int pos= 0; |
| |
| final LinkedPositionGroup group= new LinkedPositionGroup(); |
| final LinkedPosition position= WikitextBracketLevel.createPosition(type, getDocument(), |
| offset + 1, 0, pos++ ); |
| group.addPosition(position); |
| model.addGroup(group); |
| |
| model.forceInstall(); |
| |
| final WikitextBracketLevel level= new WikitextBracketLevel(model, |
| getDocument(), getDocumentContentInfo(), |
| ImCollections.newList(position), (mode & 0xffff0000) ); |
| |
| /* create UI */ |
| final LinkedModeUI ui= new LinkedModeUI(model, getViewer()); |
| ui.setCyclingMode(LinkedModeUI.CYCLE_NEVER); |
| ui.setExitPosition(getViewer(), offset + (mode & 0xff), 0, pos); |
| ui.setSimpleMode(true); |
| ui.setExitPolicy(level); |
| return ui; |
| } |
| |
| } |