From 787fb25673e45a68bc9ba1644d89e80e42e6a29d Mon Sep 17 00:00:00 2001 From: quiver2k Date: Sun, 7 Jun 2009 14:48:26 +0900 Subject: [PATCH 1/1] first commit --- jp.sourceforge.moreemacs.test/.classpath | 7 + jp.sourceforge.moreemacs.test/.project | 28 ++ .../.settings/org.eclipse.jdt.core.prefs | 7 + jp.sourceforge.moreemacs.test/META-INF/MANIFEST.MF | 9 + jp.sourceforge.moreemacs.test/build.properties | 4 + .../moreemacs/utils/CharacterUtilsTest.java | 15 + jp.sourceforge.moreemacs/.classpath | 7 + jp.sourceforge.moreemacs/.project | 28 ++ .../.settings/org.eclipse.jdt.core.prefs | 7 + jp.sourceforge.moreemacs/LICENCE.txt | 26 ++ jp.sourceforge.moreemacs/META-INF/MANIFEST.MF | 14 + jp.sourceforge.moreemacs/build.properties | 5 + jp.sourceforge.moreemacs/plugin.xml | 307 +++++++++++++++++++++ .../src/jp/sourceforge/moreemacs/MoreEmacs.java | 50 ++++ .../handlers/BackwardKillWordExecution.java | 25 ++ .../moreemacs/handlers/BackwardWordExecution.java | 37 +++ .../moreemacs/handlers/CommandHandler.java | 63 +++++ .../jp/sourceforge/moreemacs/handlers/Cursor.java | 42 +++ .../handlers/DeleteHorizontalSpaceExecution.java | 55 ++++ .../sourceforge/moreemacs/handlers/Execution.java | 8 + .../moreemacs/handlers/ForwardWordExecution.java | 39 +++ .../moreemacs/handlers/KillLineExecution.java | 51 ++++ .../moreemacs/handlers/KillRectangleExecution.java | 107 +++++++ .../moreemacs/handlers/KillWordExecution.java | 25 ++ .../handlers/MoveBeginningOfLineExecution.java | 13 + .../moreemacs/handlers/MoveEndOfLineExecution.java | 14 + .../moreemacs/handlers/OpenLineExecution.java | 16 ++ .../moreemacs/handlers/RectangleExecution.java | 15 + .../moreemacs/handlers/TextEditorExecution.java | 84 ++++++ .../handlers/TransposeCharsExecution.java | 52 ++++ .../handlers/TransposeWordsExecution.java | 31 +++ .../moreemacs/handlers/YankRectangleExecution.java | 97 +++++++ .../moreemacs/utils/CharacterUtils.java | 44 +++ .../moreemacs/utils/CodePointIterator.java | 69 +++++ .../sourceforge/moreemacs/utils/ColumnUtils.java | 31 +++ .../moreemacs/utils/DocumentCharSequence.java | 64 +++++ .../moreemacs/utils/DocumentTransaction.java | 43 +++ 37 files changed, 1539 insertions(+) create mode 100755 jp.sourceforge.moreemacs.test/.classpath create mode 100755 jp.sourceforge.moreemacs.test/.project create mode 100755 jp.sourceforge.moreemacs.test/.settings/org.eclipse.jdt.core.prefs create mode 100755 jp.sourceforge.moreemacs.test/META-INF/MANIFEST.MF create mode 100755 jp.sourceforge.moreemacs.test/build.properties create mode 100755 jp.sourceforge.moreemacs.test/src/jp/sourceforge/moreemacs/utils/CharacterUtilsTest.java create mode 100755 jp.sourceforge.moreemacs/.classpath create mode 100755 jp.sourceforge.moreemacs/.project create mode 100755 jp.sourceforge.moreemacs/.settings/org.eclipse.jdt.core.prefs create mode 100755 jp.sourceforge.moreemacs/LICENCE.txt create mode 100755 jp.sourceforge.moreemacs/META-INF/MANIFEST.MF create mode 100755 jp.sourceforge.moreemacs/build.properties create mode 100755 jp.sourceforge.moreemacs/plugin.xml create mode 100755 jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/MoreEmacs.java create mode 100755 jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/BackwardKillWordExecution.java create mode 100755 jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/BackwardWordExecution.java create mode 100755 jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/CommandHandler.java create mode 100755 jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/Cursor.java create mode 100755 jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/DeleteHorizontalSpaceExecution.java create mode 100755 jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/Execution.java create mode 100755 jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/ForwardWordExecution.java create mode 100755 jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/KillLineExecution.java create mode 100755 jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/KillRectangleExecution.java create mode 100755 jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/KillWordExecution.java create mode 100755 jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/MoveBeginningOfLineExecution.java create mode 100755 jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/MoveEndOfLineExecution.java create mode 100755 jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/OpenLineExecution.java create mode 100755 jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/RectangleExecution.java create mode 100755 jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/TextEditorExecution.java create mode 100755 jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/TransposeCharsExecution.java create mode 100755 jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/TransposeWordsExecution.java create mode 100755 jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/YankRectangleExecution.java create mode 100755 jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/utils/CharacterUtils.java create mode 100755 jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/utils/CodePointIterator.java create mode 100755 jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/utils/ColumnUtils.java create mode 100755 jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/utils/DocumentCharSequence.java create mode 100755 jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/utils/DocumentTransaction.java diff --git a/jp.sourceforge.moreemacs.test/.classpath b/jp.sourceforge.moreemacs.test/.classpath new file mode 100755 index 0000000..8a8f166 --- /dev/null +++ b/jp.sourceforge.moreemacs.test/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/jp.sourceforge.moreemacs.test/.project b/jp.sourceforge.moreemacs.test/.project new file mode 100755 index 0000000..b162ec4 --- /dev/null +++ b/jp.sourceforge.moreemacs.test/.project @@ -0,0 +1,28 @@ + + + jp.sourceforge.moreemacs.test + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/jp.sourceforge.moreemacs.test/.settings/org.eclipse.jdt.core.prefs b/jp.sourceforge.moreemacs.test/.settings/org.eclipse.jdt.core.prefs new file mode 100755 index 0000000..db8c259 --- /dev/null +++ b/jp.sourceforge.moreemacs.test/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +#Thu Jun 04 00:56:56 JST 2009 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/jp.sourceforge.moreemacs.test/META-INF/MANIFEST.MF b/jp.sourceforge.moreemacs.test/META-INF/MANIFEST.MF new file mode 100755 index 0000000..c60f676 --- /dev/null +++ b/jp.sourceforge.moreemacs.test/META-INF/MANIFEST.MF @@ -0,0 +1,9 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Test fragment +Bundle-SymbolicName: jp.sourceforge.moreemacs.test +Bundle-Version: 1.0.0 +Bundle-Vendor: quiver2k +Fragment-Host: jp.sourceforge.moreemacs +Bundle-RequiredExecutionEnvironment: JavaSE-1.6 +Require-Bundle: org.junit4;bundle-version="4.3.1" diff --git a/jp.sourceforge.moreemacs.test/build.properties b/jp.sourceforge.moreemacs.test/build.properties new file mode 100755 index 0000000..41eb6ad --- /dev/null +++ b/jp.sourceforge.moreemacs.test/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/jp.sourceforge.moreemacs.test/src/jp/sourceforge/moreemacs/utils/CharacterUtilsTest.java b/jp.sourceforge.moreemacs.test/src/jp/sourceforge/moreemacs/utils/CharacterUtilsTest.java new file mode 100755 index 0000000..43cdbb8 --- /dev/null +++ b/jp.sourceforge.moreemacs.test/src/jp/sourceforge/moreemacs/utils/CharacterUtilsTest.java @@ -0,0 +1,15 @@ +package jp.sourceforge.moreemacs.utils; + +import java.util.Locale; +import static org.junit.Assert.*; +import org.junit.Test; + +public class CharacterUtilsTest { + @Test + public void testGetWidth() { + String str = "α■"; + for(int codePoint : CodePointIterator.each(str)) { + assertEquals(2, CharacterUtils.getWidth(codePoint, Locale.JAPANESE)); + } + } +} diff --git a/jp.sourceforge.moreemacs/.classpath b/jp.sourceforge.moreemacs/.classpath new file mode 100755 index 0000000..8a8f166 --- /dev/null +++ b/jp.sourceforge.moreemacs/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/jp.sourceforge.moreemacs/.project b/jp.sourceforge.moreemacs/.project new file mode 100755 index 0000000..d069284 --- /dev/null +++ b/jp.sourceforge.moreemacs/.project @@ -0,0 +1,28 @@ + + + jp.sourceforge.moreemacs + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/jp.sourceforge.moreemacs/.settings/org.eclipse.jdt.core.prefs b/jp.sourceforge.moreemacs/.settings/org.eclipse.jdt.core.prefs new file mode 100755 index 0000000..7a7d1f2 --- /dev/null +++ b/jp.sourceforge.moreemacs/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +#Thu Jun 04 00:52:23 JST 2009 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/jp.sourceforge.moreemacs/LICENCE.txt b/jp.sourceforge.moreemacs/LICENCE.txt new file mode 100755 index 0000000..ddc98eb --- /dev/null +++ b/jp.sourceforge.moreemacs/LICENCE.txt @@ -0,0 +1,26 @@ +Copyright (c) 2009, quiver2k +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the authors nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/jp.sourceforge.moreemacs/META-INF/MANIFEST.MF b/jp.sourceforge.moreemacs/META-INF/MANIFEST.MF new file mode 100755 index 0000000..8d41486 --- /dev/null +++ b/jp.sourceforge.moreemacs/META-INF/MANIFEST.MF @@ -0,0 +1,14 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: MoreEmacs plugin +Bundle-SymbolicName: jp.sourceforge.moreemacs;singleton:=true +Bundle-Version: 1.0.0 +Bundle-Activator: jp.sourceforge.moreemacs.MoreEmacs +Bundle-Vendor: quiver2k +Require-Bundle: org.eclipse.ui, + org.eclipse.core.runtime, + org.eclipse.ui.editors;bundle-version="3.4.0", + com.ibm.icu;bundle-version="3.8.1", + org.eclipse.jface.text;bundle-version="3.4.2" +Bundle-RequiredExecutionEnvironment: JavaSE-1.6 +Bundle-ActivationPolicy: lazy diff --git a/jp.sourceforge.moreemacs/build.properties b/jp.sourceforge.moreemacs/build.properties new file mode 100755 index 0000000..6f20375 --- /dev/null +++ b/jp.sourceforge.moreemacs/build.properties @@ -0,0 +1,5 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.xml diff --git a/jp.sourceforge.moreemacs/plugin.xml b/jp.sourceforge.moreemacs/plugin.xml new file mode 100755 index 0000000..2e9bfda --- /dev/null +++ b/jp.sourceforge.moreemacs/plugin.xml @@ -0,0 +1,307 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/MoreEmacs.java b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/MoreEmacs.java new file mode 100755 index 0000000..e606a31 --- /dev/null +++ b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/MoreEmacs.java @@ -0,0 +1,50 @@ +package jp.sourceforge.moreemacs; + +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class MoreEmacs extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "jp.sourceforge.moreemacs"; + + // The shared instance + private static MoreEmacs plugin; + + /** + * The constructor + */ + public MoreEmacs() { + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static MoreEmacs getDefault() { + return plugin; + } + +} diff --git a/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/BackwardKillWordExecution.java b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/BackwardKillWordExecution.java new file mode 100755 index 0000000..f9e416b --- /dev/null +++ b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/BackwardKillWordExecution.java @@ -0,0 +1,25 @@ +package jp.sourceforge.moreemacs.handlers; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; + +public final class BackwardKillWordExecution extends TextEditorExecution { + + @Override + public void execute()throws BadLocationException { + if(!textEditor.isEditable()) { + return; + } + + int current = cursor.offset(); + int previous = BackwardWordExecution.getPreviousWordPosition(doc, current); + String word = doc.get(previous, current - previous); + Clipboard c = new Clipboard(window.getShell().getDisplay()); + c.setContents(new String[] { word }, + new Transfer[] { TextTransfer.getInstance() }); + doc.replace(previous, current - previous, ""); + + } +} diff --git a/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/BackwardWordExecution.java b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/BackwardWordExecution.java new file mode 100755 index 0000000..d0f88b8 --- /dev/null +++ b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/BackwardWordExecution.java @@ -0,0 +1,37 @@ +package jp.sourceforge.moreemacs.handlers; + +import jp.sourceforge.moreemacs.utils.CodePointIterator; +import jp.sourceforge.moreemacs.utils.DocumentCharSequence; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; + +public final class BackwardWordExecution extends TextEditorExecution { + + @Override + public void execute() throws BadLocationException { + int current = cursor.offset(); + cursor.move(getPreviousWordPosition(doc, current)); + } + + public static int getPreviousWordPosition(IDocument doc, int offset) throws BadLocationException { + CharSequence seq = new DocumentCharSequence(doc, 0, offset); + CodePointIterator itr = new CodePointIterator(seq, seq.length()); + + for(; itr.hasPrevious(); ) { + if (Character.isLetterOrDigit(itr.previous())) { + itr.next(); + break; + } + } + for(; itr.hasPrevious(); ) { + if (!Character.isLetterOrDigit(itr.previous())) { + itr.next(); + break; + } + } + + return itr.index(); + } + +} diff --git a/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/CommandHandler.java b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/CommandHandler.java new file mode 100755 index 0000000..fff2ca0 --- /dev/null +++ b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/CommandHandler.java @@ -0,0 +1,63 @@ +package jp.sourceforge.moreemacs.handlers; + +import java.util.regex.Pattern; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.Command; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.handlers.HandlerUtil; + +public final class CommandHandler extends AbstractHandler { + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindow(event); + + Execution exe = newExecution(event); + if(!exe.init(window)) { + return null; + } + try { + exe.execute(); + } catch (Exception e) { + throw new ExecutionException(e.getMessage(), e); + } + + return null; + } + + private Execution newExecution(ExecutionEvent event) throws ExecutionException { + try { + String className = getExecutionClassName(event); + Class clazz = Class.forName(className); + Object obj = clazz.newInstance(); + if(!(obj instanceof Execution)) { + throw new ExecutionException("the class "+clazz.getName()+ + " does not implements Execution."); + } + return (Execution)obj; + } catch (ClassNotFoundException e) { + throw new ExecutionException(e.getMessage(), e); + } catch (InstantiationException e) { + throw new ExecutionException(e.getMessage(), e); + } catch (IllegalAccessException e) { + throw new ExecutionException(e.getMessage(), e); + } + + } + + // naming strategy will be separated to another class. + private static final String COMMAND_PREFIX_QUOTED = Pattern.quote("jp.sourceforge.moreemacs."); + private static final String HANDLER_PREFIX = "jp.sourceforge.moreemacs.handlers."; + private static final String HANDLER_SUFFIX = "Execution"; + private static String getExecutionClassName(ExecutionEvent event) { + Command command = event.getCommand(); + String className = command.getId() + .replaceFirst(COMMAND_PREFIX_QUOTED, HANDLER_PREFIX) + +HANDLER_SUFFIX; + return className; + } + +} diff --git a/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/Cursor.java b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/Cursor.java new file mode 100755 index 0000000..f01f763 --- /dev/null +++ b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/Cursor.java @@ -0,0 +1,42 @@ +package jp.sourceforge.moreemacs.handlers; + +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.ITextViewerExtension5; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.ui.texteditor.ITextEditor; + +final class Cursor { + private final ITextEditor textEditor; + private final ITextViewerExtension5 textViewerEx5; + private final StyledText styledText; + + Cursor(ITextEditor textEditor, ITextViewer textViewer) { + this.textEditor = textEditor; + this.styledText = textViewer.getTextWidget(); + this.textViewerEx5 = (textViewer instanceof ITextViewerExtension5) + ? (ITextViewerExtension5) textViewer: null; + } + + int offset() { + if(textViewerEx5 != null) { + return textViewerEx5.widgetOffset2ModelOffset( + styledText.getCaretOffset()); + } + + ITextSelection selectoin = + (ITextSelection) textEditor.getSelectionProvider().getSelection(); + int selectionBegin = selectoin.getOffset(); + return selectionBegin; + } + + void move(int offset) { +// if(textViewerEx5 != null) { +// styledText.setCaretOffset(textViewerEx5.modelOffset2WidgetOffset(offset)); +// return; +// } + + textEditor.resetHighlightRange(); + textEditor.setHighlightRange(offset, 0, true); + } +} diff --git a/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/DeleteHorizontalSpaceExecution.java b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/DeleteHorizontalSpaceExecution.java new file mode 100755 index 0000000..9cc0ca2 --- /dev/null +++ b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/DeleteHorizontalSpaceExecution.java @@ -0,0 +1,55 @@ +package jp.sourceforge.moreemacs.handlers; + +import jp.sourceforge.moreemacs.utils.CodePointIterator; +import jp.sourceforge.moreemacs.utils.DocumentCharSequence; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; + +public final class DeleteHorizontalSpaceExecution extends TextEditorExecution { + + @Override + public void execute() throws BadLocationException { + if(!textEditor.isEditable()) { + return; + } + + int current = cursor.offset(); + int start = skipBackwardSpaces(doc, current); + int end = skipForwardSpaces(doc, current); + doc.replace(start, end - start, ""); + } + + int skipBackwardSpaces(IDocument doc, int offset) throws BadLocationException { + IRegion line = doc.getLineInformationOfOffset(offset); + + CharSequence seq = new DocumentCharSequence(doc, + line.getOffset(), offset-line.getOffset()); + + int result = offset; + for(CodePointIterator itr = new CodePointIterator(seq, seq.length()); itr.hasPrevious(); ) { + int codePoint = itr.previous(); + if (!Character.isWhitespace(codePoint)) { + break; + } + result = line.getOffset() + itr.index(); + } + return result; + } + + int skipForwardSpaces(IDocument doc, int offset) throws BadLocationException { + IRegion line = doc.getLineInformationOfOffset(offset); + CharSequence seq = new DocumentCharSequence(doc, + offset,line.getOffset()+line.getLength()-offset); + int result = offset; + for(CodePointIterator itr = new CodePointIterator(seq); itr.hasNext(); ) { + int codePoint = itr.next(); + if (!Character.isWhitespace(codePoint)) { + break; + } + result = offset + itr.index(); + } + return result; + } +} diff --git a/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/Execution.java b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/Execution.java new file mode 100755 index 0000000..7c83984 --- /dev/null +++ b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/Execution.java @@ -0,0 +1,8 @@ +package jp.sourceforge.moreemacs.handlers; + +import org.eclipse.ui.IWorkbenchWindow; + +public interface Execution { + boolean init(IWorkbenchWindow window); + void execute() throws Exception; +} diff --git a/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/ForwardWordExecution.java b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/ForwardWordExecution.java new file mode 100755 index 0000000..345026b --- /dev/null +++ b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/ForwardWordExecution.java @@ -0,0 +1,39 @@ +package jp.sourceforge.moreemacs.handlers; + +import jp.sourceforge.moreemacs.utils.CodePointIterator; +import jp.sourceforge.moreemacs.utils.DocumentCharSequence; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; + +public final class ForwardWordExecution extends TextEditorExecution { + + @Override + public void execute() throws BadLocationException { + int current = cursor.offset(); + cursor.move(getNextWordPosition(doc, current)); + } + + public static int getNextWordPosition(IDocument doc, int offset) throws BadLocationException { + CharSequence seq = new DocumentCharSequence(doc, offset, doc.getLength()-offset); + CodePointIterator itr = new CodePointIterator(seq); + + + for(; itr.hasNext(); ) { + int codePoint = itr.next(); + if (Character.isLetterOrDigit(codePoint)) { + itr.previous(); + break; + } + } + for(; itr.hasNext(); ) { + if (!Character.isLetterOrDigit(itr.next())) { + itr.previous(); + break; + } + } + + return offset + itr.index(); + } + +} diff --git a/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/KillLineExecution.java b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/KillLineExecution.java new file mode 100755 index 0000000..cfe1942 --- /dev/null +++ b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/KillLineExecution.java @@ -0,0 +1,51 @@ +package jp.sourceforge.moreemacs.handlers; + +import jp.sourceforge.moreemacs.utils.CodePointIterator; +import jp.sourceforge.moreemacs.utils.DocumentCharSequence; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; + +public final class KillLineExecution extends TextEditorExecution { + + @Override + public void execute() throws BadLocationException { + if(!textEditor.isEditable()) { + return; + } + + int current = cursor.offset(); + int linePos = doc.getLineOfOffset(current); + IRegion line = doc.getLineInformation(linePos); + String delim = doc.getLineDelimiter(linePos); + + int length = line.getOffset() + line.getLength() - current; + boolean allSpaces = isAllSpaces(doc, current, length); + + int cutLength = length; + if (allSpaces && delim != null) { + cutLength += delim.length(); + } + + String cut = doc.get(current, cutLength); + Clipboard c = new Clipboard(window.getShell().getDisplay()); + c.setContents( + new String[] { cut }, + new Transfer[] { TextTransfer.getInstance() }); + doc.replace(current, cutLength, ""); + } + + private boolean isAllSpaces(IDocument doc, int offset, int length) throws BadLocationException { + CharSequence seq = new DocumentCharSequence(doc, offset, length); + for(int codePoint : CodePointIterator.each(seq)) { + if (!Character.isWhitespace(codePoint)) { + return false; + } + } + return true; + } +} diff --git a/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/KillRectangleExecution.java b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/KillRectangleExecution.java new file mode 100755 index 0000000..ac81e69 --- /dev/null +++ b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/KillRectangleExecution.java @@ -0,0 +1,107 @@ +package jp.sourceforge.moreemacs.handlers; + +import java.util.ArrayList; +import java.util.List; + +import jp.sourceforge.moreemacs.utils.CodePointIterator; +import jp.sourceforge.moreemacs.utils.ColumnUtils; +import jp.sourceforge.moreemacs.utils.DocumentCharSequence; +import jp.sourceforge.moreemacs.utils.DocumentTransaction; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.DocumentRewriteSessionType; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextSelection; + +public final class KillRectangleExecution extends RectangleExecution { + + @Override + public void execute() throws BadLocationException { + + if(!textEditor.isEditable()) { + return; + } + + ITextSelection selection = getSelection(true); + + int start = selection.getOffset(); + int startRow = doc.getLineOfOffset(start); + int startColumn = ColumnUtils.getColumn(doc, start, getTabStop()); + + int end = start + selection.getLength(); + int endRow = doc.getLineOfOffset(end); + int endColumn = ColumnUtils.getColumn(doc, end, getTabStop()); + + if(startColumn > endColumn) { + int work = startColumn; + startColumn = endColumn; + endColumn = work; + } + + DocumentTransaction transaction = new DocumentTransaction(doc); + transaction.begin(DocumentRewriteSessionType.UNRESTRICTED_SMALL); + try { + List rectangle = killRectangle(doc, startRow, startColumn, endRow, endColumn); + setRectangle(rectangle); + } finally { + transaction.end(); + } + } + private List killRectangle(IDocument doc, + int startRow, int startColumn, + int endRow, int endColumn) + throws BadLocationException { + + List rectangle = new ArrayList(); + + for(int i = startRow; i <= endRow; i++) { + String str = killString(doc, i, startColumn, endColumn); + rectangle.add(str); + } + return rectangle; + } + private String killString(IDocument doc, int row, + int startColumn, int endColumn) throws BadLocationException { + IRegion line = doc.getLineInformation(row); + + StringBuilder builder = new StringBuilder(); + int column = 0; + int cutOffset = 0; + int cutLength = 0; + + CharSequence seq = new DocumentCharSequence(doc, line.getOffset(), line.getLength()); + + for(CodePointIterator itr = new CodePointIterator(seq); itr.hasNext(); ) { + if(column >= endColumn) { + break; + } + int offset = line.getOffset() + itr.index(); + int codePoint = itr.next(); + + int nextColumn = ColumnUtils.getNextColumn(column, codePoint, getTabStop()); + + if(nextColumn < startColumn+1) { + column = nextColumn; + continue; + } + if(cutLength == 0) { + cutOffset = offset; + } + builder.appendCodePoint(codePoint); + cutLength += Character.charCount(codePoint); + column = nextColumn; + } + + doc.replace(cutOffset, cutLength, ""); + cursor.move(cutOffset); + + + for(int i = 0; i < endColumn-column; i++) { + builder.append(' '); + } + + return builder.toString(); + } + +} diff --git a/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/KillWordExecution.java b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/KillWordExecution.java new file mode 100755 index 0000000..321ae7c --- /dev/null +++ b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/KillWordExecution.java @@ -0,0 +1,25 @@ +package jp.sourceforge.moreemacs.handlers; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; + +public final class KillWordExecution extends TextEditorExecution { + + @Override + public void execute() throws BadLocationException { + if(!textEditor.isEditable()) { + return; + } + + int current = cursor.offset(); + int next = ForwardWordExecution.getNextWordPosition(doc, current); + String word = doc.get(current, next-current); + Clipboard c = new Clipboard(window.getShell().getDisplay()); + c.setContents( + new String[] { word }, + new Transfer[] { TextTransfer.getInstance() }); + doc.replace(current, next-current, ""); + } +} diff --git a/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/MoveBeginningOfLineExecution.java b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/MoveBeginningOfLineExecution.java new file mode 100755 index 0000000..be3aaee --- /dev/null +++ b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/MoveBeginningOfLineExecution.java @@ -0,0 +1,13 @@ +package jp.sourceforge.moreemacs.handlers; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IRegion; + +public final class MoveBeginningOfLineExecution extends TextEditorExecution { + @Override + public void execute() throws BadLocationException { + IRegion line = doc.getLineInformationOfOffset(cursor.offset()); + cursor.move(line.getOffset()); + } +} + diff --git a/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/MoveEndOfLineExecution.java b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/MoveEndOfLineExecution.java new file mode 100755 index 0000000..6131cc2 --- /dev/null +++ b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/MoveEndOfLineExecution.java @@ -0,0 +1,14 @@ +package jp.sourceforge.moreemacs.handlers; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IRegion; + +public final class MoveEndOfLineExecution extends TextEditorExecution { + + @Override + public void execute() throws BadLocationException { + IRegion line = doc.getLineInformationOfOffset(cursor.offset()); + cursor.move(line.getOffset()+line.getLength()); + } + +} diff --git a/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/OpenLineExecution.java b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/OpenLineExecution.java new file mode 100755 index 0000000..a572e06 --- /dev/null +++ b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/OpenLineExecution.java @@ -0,0 +1,16 @@ +package jp.sourceforge.moreemacs.handlers; + +import org.eclipse.jface.text.TextUtilities; + +public final class OpenLineExecution extends TextEditorExecution { + + @Override + public void execute() throws Exception { + if(!textEditor.isEditable()) { + return; + } + String delim = TextUtilities.getDefaultLineDelimiter(doc); + doc.replace(cursor.offset(), 0, delim); + } + +} diff --git a/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/RectangleExecution.java b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/RectangleExecution.java new file mode 100755 index 0000000..e49ce60 --- /dev/null +++ b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/RectangleExecution.java @@ -0,0 +1,15 @@ +package jp.sourceforge.moreemacs.handlers; + +import java.util.List; + +abstract class RectangleExecution extends TextEditorExecution { + private static List rectangle ; + + protected static void setRectangle(List rect) { + rectangle = rect; + } + + protected static List getRectangle() { + return rectangle; + } +} diff --git a/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/TextEditorExecution.java b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/TextEditorExecution.java new file mode 100755 index 0000000..56f01ae --- /dev/null +++ b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/TextEditorExecution.java @@ -0,0 +1,84 @@ +package jp.sourceforge.moreemacs.handlers; + +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.ITextOperationTarget; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.ITextViewerExtension; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.texteditor.ITextEditor; + +abstract class TextEditorExecution implements Execution { + protected IWorkbenchWindow window; + protected ITextEditor textEditor; + protected ITextViewer textViewer; + protected Cursor cursor; + protected IDocument doc; + + @Override + public boolean init(IWorkbenchWindow window) { + this.window = window; + + IEditorPart editor = window.getActivePage().getActiveEditor(); + if (editor instanceof ITextEditor) { + textEditor = (ITextEditor) editor; + } else { + textEditor = (ITextEditor) editor.getAdapter(ITextEditor.class); + } + if(textEditor == null) { + return false; + } + + doc = textEditor.getDocumentProvider().getDocument( + textEditor.getEditorInput()); + + ITextOperationTarget target = + (ITextOperationTarget)editor.getAdapter(ITextOperationTarget.class); + if(!(target instanceof ITextViewer)) { + return false; + } + textViewer = (ITextViewer) target; + + cursor = new Cursor(textEditor, textViewer); + + return true; + } + + protected ITextSelection getSelection(boolean fallbackToMark) { + ITextSelection selection = + (ITextSelection) textEditor.getSelectionProvider().getSelection(); + + if(!fallbackToMark && selection.getLength() != 0) { + return selection; + } + + IEditorPart editor = window.getActivePage().getActiveEditor(); + ITextOperationTarget target = + (ITextOperationTarget)editor.getAdapter(ITextOperationTarget.class); + + if(!(target instanceof ITextViewerExtension)) { + return selection; + } + + ITextViewerExtension viewerEx = (ITextViewerExtension) target; + + int mark = viewerEx.getMark(); + + if(mark == -1) { + return selection; + } + + int current = selection.getOffset(); + + int start = (mark < current) ? mark : current; + textEditor.selectAndReveal(start, Math.abs(mark - current)); + + return (ITextSelection) textEditor + .getSelectionProvider().getSelection(); + } + + protected final int getTabStop() { + return textViewer.getTextWidget().getTabs(); + } +} diff --git a/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/TransposeCharsExecution.java b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/TransposeCharsExecution.java new file mode 100755 index 0000000..47c9b59 --- /dev/null +++ b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/TransposeCharsExecution.java @@ -0,0 +1,52 @@ +package jp.sourceforge.moreemacs.handlers; + +import jp.sourceforge.moreemacs.utils.DocumentCharSequence; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IRegion; + +public final class TransposeCharsExecution extends TextEditorExecution { + @Override + public void execute() throws BadLocationException { + if(!textEditor.isEditable()) { + return; + } + + int current = cursor.offset(); + if(current == 0) { + // beginning of document + return; + } + + int linePos = doc.getLineOfOffset(current); + IRegion line = doc.getLineInformation(linePos); + + DocumentCharSequence seq = new DocumentCharSequence(doc); + + if(line.getOffset() + line.getLength() == current) { + // if end of line, adjust current position + current = (line.getOffset() == current) + ? current - doc.getLineDelimiter(linePos-1).length() + : seq.previousCodePointIndex(current); + linePos = doc.getLineOfOffset(current); + line = doc.getLineInformation(linePos); + } + if(current == 0) { + // beginning of document again + return; + } + + int nextIndex = seq.nextCodePointIndex(current); + String forwardChars = (line.getOffset() + line.getLength() == current) + ? doc.getLineDelimiter(linePos) : doc.get(current, nextIndex - current); + + int prevIndex = seq.previousCodePointIndex(current); + String backwardChars = (line.getOffset() == current) + ? doc.getLineDelimiter(linePos-1) : doc.get(prevIndex, current-prevIndex); + + doc.replace(current-backwardChars.length(), + backwardChars.length() + forwardChars.length(), + forwardChars+backwardChars); + } + +} diff --git a/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/TransposeWordsExecution.java b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/TransposeWordsExecution.java new file mode 100755 index 0000000..2b57aff --- /dev/null +++ b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/TransposeWordsExecution.java @@ -0,0 +1,31 @@ +package jp.sourceforge.moreemacs.handlers; + +import org.eclipse.jface.text.BadLocationException; + +public final class TransposeWordsExecution extends TextEditorExecution { + @Override + public void execute() throws BadLocationException { + if(!textEditor.isEditable()) { + return; + } + + int current = cursor.offset(); + int previousBegin = BackwardWordExecution.getPreviousWordPosition(doc, current); + int previousEnd = ForwardWordExecution.getNextWordPosition(doc, previousBegin); + int nextEnd = ForwardWordExecution.getNextWordPosition(doc, current); + int nextBegin = BackwardWordExecution.getPreviousWordPosition(doc, nextEnd); + + if(nextBegin <= previousEnd) { + return; + } + + String previous = doc.get(previousBegin, previousEnd-previousBegin); + String simbols = doc.get(previousEnd, nextBegin-previousEnd); + String next = doc.get(nextBegin, nextEnd-nextBegin); + + doc.replace(previousBegin, + nextEnd-previousBegin, + next+simbols+previous); + + } +} diff --git a/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/YankRectangleExecution.java b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/YankRectangleExecution.java new file mode 100755 index 0000000..f570c0f --- /dev/null +++ b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/handlers/YankRectangleExecution.java @@ -0,0 +1,97 @@ +package jp.sourceforge.moreemacs.handlers; + +import java.util.List; + +import jp.sourceforge.moreemacs.utils.CodePointIterator; +import jp.sourceforge.moreemacs.utils.ColumnUtils; +import jp.sourceforge.moreemacs.utils.DocumentCharSequence; +import jp.sourceforge.moreemacs.utils.DocumentTransaction; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.DocumentRewriteSessionType; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.TextUtilities; + +public final class YankRectangleExecution extends RectangleExecution { + @Override + public void execute() throws BadLocationException { + if(!textEditor.isEditable()) { + return; + } + + List rectangle = getRectangle(); + if(rectangle == null) { + return; + } + + int current = cursor.offset(); + int row = doc.getLineOfOffset(current); + int column = ColumnUtils.getColumn(doc, current, getTabStop()); + + ensureLines(doc, row + rectangle.size()); + + DocumentTransaction transaction = new DocumentTransaction(doc); + transaction.begin(DocumentRewriteSessionType.UNRESTRICTED_SMALL); + try { + yankRectangle(doc, row, column, rectangle); + } finally { + transaction.end(); + } + } + + private void ensureLines(IDocument doc, int lines) throws BadLocationException { + int n = lines - doc.getNumberOfLines(); + if(n <= 0) { + return; + } + + StringBuilder builder = new StringBuilder(); + String delim = TextUtilities.getDefaultLineDelimiter(doc); + for(int i = 0; i < n; i++) { + builder.append(delim); + } + doc.replace(doc.getLength(), 0, builder.toString()); + + } + + private void yankRectangle(IDocument doc, + int row, int column, List rectangle) + throws BadLocationException { + for(int i = 0; i < rectangle.size(); i++) { + yankString(doc, row+i, column, rectangle.get(i)); + } + } + + private void yankString(IDocument doc, int row, int column, String str) + throws BadLocationException + { + IRegion line = doc.getLineInformation(row); + int col = 0; + + CharSequence seq = new DocumentCharSequence(doc, line.getOffset(), line.getLength()); + + for(CodePointIterator itr = new CodePointIterator(seq); itr.hasNext(); ) { + int offset = line.getOffset() + itr.index(); + int codePoint = itr.next(); + if(col >= column) { + doc.replace(offset, 0, str); + cursor.move(offset+str.length()); + return; + } + col = ColumnUtils.getNextColumn(col, codePoint, getTabStop()); + } + + + StringBuilder builder = new StringBuilder(); + for(int i = 0; i < column-col; i++) { + builder.append(" "); + } + builder.append(str); + doc.replace(line.getOffset()+line.getLength(), 0, builder.toString()); + cursor.move(line.getOffset()+line.getLength()+builder.length()); + } + +} + + diff --git a/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/utils/CharacterUtils.java b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/utils/CharacterUtils.java new file mode 100755 index 0000000..fe3db82 --- /dev/null +++ b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/utils/CharacterUtils.java @@ -0,0 +1,44 @@ +package jp.sourceforge.moreemacs.utils; + +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +import com.ibm.icu.lang.UCharacter; +import com.ibm.icu.lang.UProperty; + +public final class CharacterUtils { + private CharacterUtils() {} + + private static List EAST_ASIAN_LANGS = + Arrays.asList("ja", "vi", "kr", "zh"); + + public static int getWidth(int codePoint) { + return getWidth(codePoint, Locale.getDefault()); + } + + public static int getWidth(int codePoint, Locale locale) { + if(locale == null) { + throw new NullPointerException("locale is null"); + } + int value = UCharacter.getIntPropertyValue(codePoint, + UProperty.EAST_ASIAN_WIDTH); + switch(value) { + case UCharacter.EastAsianWidth.NARROW: + case UCharacter.EastAsianWidth.NEUTRAL: + case UCharacter.EastAsianWidth.HALFWIDTH: + return 1; + case UCharacter.EastAsianWidth.FULLWIDTH: + case UCharacter.EastAsianWidth.WIDE: + return 2; + case UCharacter.EastAsianWidth.AMBIGUOUS: + if(EAST_ASIAN_LANGS.contains(locale.getLanguage())) { + return 2; + } else { + return 1; + } + default: + return 1; + } + } +} diff --git a/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/utils/CodePointIterator.java b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/utils/CodePointIterator.java new file mode 100755 index 0000000..0d3ed8b --- /dev/null +++ b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/utils/CodePointIterator.java @@ -0,0 +1,69 @@ +package jp.sourceforge.moreemacs.utils; + +import java.util.Iterator; + +public final class CodePointIterator implements Iterator { + private final CharSequence seq; + private int index; + + public CodePointIterator(CharSequence seq) { + this(seq, 0); + } + + public CodePointIterator(CharSequence seq, int index) { + if(seq == null) { + throw new NullPointerException("seq is null"); + } + + this.seq = seq; + setIndex(index); + } + + public void setIndex(int index) { + if(index < 0 || index > seq.length()) { + throw new IndexOutOfBoundsException(); + } + this.index = index; + } + + @Override + public boolean hasNext() { + return index < seq.length(); + } + + public boolean hasPrevious() { + return index > 0; + } + + public int index() { + return index; + } + + @Override + public Integer next() { + int codePoint = Character.codePointAt(seq, index); + index += Character.charCount(codePoint); + return codePoint; + } + + public Integer previous() { + int codePoint = Character.codePointBefore(seq, index); + index -= Character.charCount(codePoint); + return codePoint; + } + + @Override + public void remove() { + throw new UnsupportedOperationException("unsupported"); + } + + public static Iterable each(final CharSequence seq) { + return new Iterable() { + @Override + public Iterator iterator() { + return new CodePointIterator(seq); + } + + }; + } +} diff --git a/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/utils/ColumnUtils.java b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/utils/ColumnUtils.java new file mode 100755 index 0000000..8ad4470 --- /dev/null +++ b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/utils/ColumnUtils.java @@ -0,0 +1,31 @@ +package jp.sourceforge.moreemacs.utils; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; + +public final class ColumnUtils { + private ColumnUtils() {} + + public static int getColumn(IDocument doc, int offset, int tabStop) + throws BadLocationException { + IRegion line = doc.getLineInformationOfOffset(offset); + int column = 0; + + CharSequence seq = new DocumentCharSequence(doc, line.getOffset(), offset - line.getOffset()); + for(CodePointIterator itr = new CodePointIterator(seq); itr.hasNext(); ) { + int codePoint = itr.next(); + column = getNextColumn(column, codePoint, tabStop); + } + + return column; + } + + public static int getNextColumn(int column, int codePoint, int tabStop) { + if(codePoint == '\t') { + return column - (column%tabStop) + tabStop; + } else { + return column + CharacterUtils.getWidth(codePoint); + } + } +} diff --git a/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/utils/DocumentCharSequence.java b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/utils/DocumentCharSequence.java new file mode 100755 index 0000000..813b791 --- /dev/null +++ b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/utils/DocumentCharSequence.java @@ -0,0 +1,64 @@ +package jp.sourceforge.moreemacs.utils; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; + +public final class DocumentCharSequence implements CharSequence { + private final IDocument doc; + private final int offset; + private final int length; + + public DocumentCharSequence(IDocument doc) { + this(doc, 0, doc.getLength()); + } + + public DocumentCharSequence(IDocument doc, int offset, int length) { + if(doc == null) { + throw new NullPointerException("doc is null"); + } + + this.doc = doc; + this.offset = offset; + this.length = length; + + validate(offset, length, doc.getLength()); + } + + private static void validate(int offset, int length, int capacity) { + if(offset < 0 || length < 0 || offset+length > capacity) { + throw new IndexOutOfBoundsException(); + } + } + + @Override + public char charAt(int index) { + if(index < 0 || index >= length) { + throw new IndexOutOfBoundsException(); + } + try { + return doc.getChar(offset + index); + } catch (BadLocationException e) { + throw new IndexOutOfBoundsException(e.getMessage()); + } + } + + @Override + public int length() { + return length; + } + + @Override + public CharSequence subSequence(int start, int end) { + validate(start, end-start, length); + return new DocumentCharSequence(doc, offset+start, end - start); + } + + public int nextCodePointIndex(int index) { + int codePoint = Character.codePointAt(this, index); + return index + Character.charCount(codePoint); + } + public int previousCodePointIndex(int index) { + int codePoint = Character.codePointBefore(this, index); + return index - Character.charCount(codePoint); + } +} diff --git a/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/utils/DocumentTransaction.java b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/utils/DocumentTransaction.java new file mode 100755 index 0000000..f0749bd --- /dev/null +++ b/jp.sourceforge.moreemacs/src/jp/sourceforge/moreemacs/utils/DocumentTransaction.java @@ -0,0 +1,43 @@ +package jp.sourceforge.moreemacs.utils; + +import org.eclipse.jface.text.DocumentRewriteSession; +import org.eclipse.jface.text.DocumentRewriteSessionType; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IDocumentExtension4; + +public final class DocumentTransaction { + private IDocumentExtension4 sessionManager; + private DocumentRewriteSession session; + + public DocumentTransaction(IDocument doc) { + if(doc instanceof IDocumentExtension4) { + sessionManager = (IDocumentExtension4)doc; + } + } + + public boolean isAvailable() { + return sessionManager != null; + } + + public void begin(DocumentRewriteSessionType type) { + if(!isAvailable()) { + return; + } + if(session != null) { + throw new IllegalStateException("session already started"); + } + session = sessionManager.startRewriteSession(type); + } + + public void end() { + if(!isAvailable()) { + return; + } + if(session == null) { + throw new IllegalStateException("session is not started"); + } + sessionManager.stopRewriteSession(session); + session = null; + } + +} -- 2.11.0