...
 
Commits (3)
......@@ -381,7 +381,8 @@ be entered instead of expressions:
:bd
copy Copies the current paper to copy, cp, y
the clipboard.
the clipboard by default. Accepts
a type and a range.
new Creates a new paper. new, :new
next Switches to the next paper in next, right,
......@@ -423,6 +424,36 @@ Parameters are to be provided space separated and can be quoted, some examples:
open ./relative\ path\ with\ spaces/file.jmathpaper
open a.jmathpaper b.jmathpaper c.jmathpaper
#### copy
The copy command accepts additional parameters, namely a part and a range. If
no parameters are provided the whole paper will be copied, if parameters are
provided only the set parts of the given lines will be copied.
copy [part] [range]
The types available are:
Part
-----------------------------------------------------------------------
expression The expression of the selected lines. exp
id The ID of the selected lines.
line The whole lines.
paper The whole paper.
result The result of the selected lines. res
The range can either be nothing (for all), a single ID or index (starting at 1),
a comma separated list of ID's or indexes or a range specified with two dots.
copy abc
copy 4
copy expression #3,abc,#7
copy result 3..abc
### Options
There are various options available to change how jMathPaper behaves, they can
......
......@@ -21,8 +21,8 @@ package org.bonsaimind.jmathpaper;
import java.nio.file.Path;
import org.bonsaimind.jmathpaper.core.ConfigurationProcessor;
import org.bonsaimind.jmathpaper.core.configuration.Configuration;
import org.bonsaimind.jmathpaper.core.configuration.ConfigurationProcessor;
import org.bonsaimind.jmathpaper.core.configuration.Definitions;
import org.bonsaimind.jmathpaper.core.resources.ResourceLoader;
import org.bonsaimind.jmathpaper.core.ui.Ui;
......
......@@ -76,7 +76,7 @@ public class Paper {
}
public void evaluateFromText(String text) throws InvalidExpressionException {
evaluateLines(Arrays.asList(text.split("\\n")));
evaluateLines(Arrays.asList(text.split("\\r?\\n")));
}
public void evaluateLines(List<String> lines) throws InvalidExpressionException {
......
......@@ -17,7 +17,7 @@
* Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.bonsaimind.jmathpaper.core;
package org.bonsaimind.jmathpaper.core.configuration;
import java.io.BufferedReader;
import java.io.FileInputStream;
......
......@@ -22,7 +22,7 @@ package org.bonsaimind.jmathpaper.core.resources;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import org.bonsaimind.jmathpaper.core.ConfigurationProcessor;
import org.bonsaimind.jmathpaper.core.configuration.ConfigurationProcessor;
/**
* {@link ResourceLoader} is a static utility for loading embedded resources.
......
......@@ -30,6 +30,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.bonsaimind.jmathpaper.core.EvaluatedExpression;
import org.bonsaimind.jmathpaper.core.InvalidExpressionException;
import org.bonsaimind.jmathpaper.core.Paper;
import org.bonsaimind.jmathpaper.core.configuration.Definitions;
......@@ -183,7 +184,22 @@ public abstract class AbstractPapersUi implements Ui {
break;
case COPY:
copy();
if (parameters != null && parameters.length > 0) {
PaperPart paperPart = PaperPart.getPaperPart(parameters[0]);
if (paperPart == null) {
copy(PaperPart.LINE, String.join(" ", parameters));
} else if (parameters.length > 1) {
List<String> remainingParameters = new ArrayList<>(Arrays.asList(parameters));
remainingParameters.remove(0);
copy(paperPart, String.join(" ", remainingParameters));
} else {
copy(paperPart, null);
}
} else {
copy(PaperPart.PAPER, null);
}
break;
case NEXT:
......@@ -510,16 +526,82 @@ public abstract class AbstractPapersUi implements Ui {
}
}
/**
* Checks that the current {@link #paper} is not empty.
*
* @throws IllegalStateException If the current {@link #paper} is empty.
*/
protected void checkCurrentPaperNotEmpty() throws IllegalStateException {
if (paper == null || paper.getEvaluatedExpressions().isEmpty()) {
throw new IllegalStateException("This operation can only be performed with a paper with lines.");
}
}
/**
* Copies the current {@link #paper} to the clipboard of the system.
*
* @throws IllegalStateException If there is no current {@link #paper}.
*/
protected void copy() throws IllegalStateException {
protected void copy(PaperPart paperPart, String identification) throws IllegalStateException {
checkCurrentPaper();
switch (paperPart) {
case EXPRESSION:
case ID:
case LINE:
case RESULT:
checkCurrentPaperNotEmpty();
List<EvaluatedExpression> evaluatedExpressions = null;
if (identification != null && !identification.isEmpty()) {
evaluatedExpressions = getEvaluatedExpressions(identification);
} else {
evaluatedExpressions = paper.getEvaluatedExpressions();
}
if (evaluatedExpressions.isEmpty()) {
throw new IllegalArgumentException("No matching lines found for: " + identification);
}
StringBuilder content = new StringBuilder();
for (EvaluatedExpression evaluatedExpression : evaluatedExpressions) {
if (content.length() > 0) {
content.append("\n");
}
switch (paperPart) {
case EXPRESSION:
content.append(evaluatedExpression.getExpression());
break;
case ID:
content.append(evaluatedExpression.getId());
break;
case LINE:
content.append(evaluatedExpression.toString());
break;
case RESULT:
content.append(evaluatedExpression.getFormattedResult(paper.getNumberFormat()));
break;
}
}
copyToClipboard(content.toString());
break;
case PAPER:
copyToClipboard(paper.toString());
break;
}
}
protected void copyToClipboard(String value) {
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(
new StringSelection(paper.toString()),
new StringSelection(value),
null);
}
......@@ -619,6 +701,103 @@ public abstract class AbstractPapersUi implements Ui {
throw new IllegalArgumentException("\"" + name + "\" is not a value of " + enumClass.getSimpleName() + ".");
}
/**
* Gets the {@link EvaluatedExpression} from the current {@link Paper} which
* matches the given identifier.
* <p>
* The give identifier can either be the ID (case-sensitive), or the 1-based
* index of the expression.
*
* @param identifier The ID of the {@link EvaluatedExpression} to get or the
* 1-based index.
* @return The {@link EvaluatedExpression} which matches the given
* identifier.
*/
protected EvaluatedExpression getEvaluatedExpression(String identifier) {
if (identifier == null || identifier.isEmpty()) {
return null;
}
String trimmedIdentifier = identifier.trim();
try {
int number = Integer.parseInt(trimmedIdentifier);
if (number >= 0) {
return paper.getEvaluatedExpressions().get(number - 1);
} else {
return paper.getEvaluatedExpressions().get(paper.getEvaluatedExpressions().size() + number);
}
} catch (NumberFormatException e) {
// Ignore and continue.
}
for (EvaluatedExpression evaluatedExpression : paper.getEvaluatedExpressions()) {
if (evaluatedExpression.getId().equals(trimmedIdentifier)) {
return evaluatedExpression;
}
}
return null;
}
/**
* Gets the {@link EvaluatedExpression}s which are matching the given
* identification}.
* <p>
* The given identification can either be a single ID or 1-based index, a
* comma-separated list of IDs or 1-based indexes or a range which is
* separated by double-dots.
*
* @param identification The identification for the
* {@link EvaluatedExpression}s.
* @return The {@link List} of matching {@link EvaluatedExpression}s.
*/
protected List<EvaluatedExpression> getEvaluatedExpressions(String identification) {
if (identification == null || identification.isEmpty()) {
return Collections.emptyList();
}
List<EvaluatedExpression> evaluatedExpressions = new ArrayList<>();
if (identification.contains("..")) {
String[] identifiers = identification.split("\\.\\.");
EvaluatedExpression startEvaluatedExpression = getEvaluatedExpression(identifiers[0]);
EvaluatedExpression endEvaluatedExpression = getEvaluatedExpression(identifiers[1]);
if (startEvaluatedExpression != null || endEvaluatedExpression != null) {
boolean addEvaluatedExpressions = false;
for (EvaluatedExpression evaluatedExpression : paper.getEvaluatedExpressions()) {
if (evaluatedExpression == startEvaluatedExpression) {
addEvaluatedExpressions = true;
}
if (addEvaluatedExpressions) {
evaluatedExpressions.add(evaluatedExpression);
}
if (evaluatedExpression == endEvaluatedExpression) {
addEvaluatedExpressions = false;
}
}
}
} else if (identification.contains(",")) {
for (String identifier : identification.split(",")) {
EvaluatedExpression evaluatedExpression = getEvaluatedExpression(identifier);
if (evaluatedExpression != null) {
evaluatedExpressions.add(evaluatedExpression);
}
}
} else {
evaluatedExpressions.add(getEvaluatedExpression(identification));
}
return evaluatedExpressions;
}
/**
* Reevaluates the current {@link Paper}.
* <p>
......
......@@ -38,10 +38,7 @@ public enum Command {
/** All papers will be closed. */
CLOSEALL("closeall"),
/**
* The current paper will be copied to the clipboard in its text
* representation.
*/
/** The specified part will be copied to the clipboard. */
COPY("copy", "cp", "y"),
/** Start a new paper. */
......
/*
* Copyright 2018, Robert 'Bobby' Zenz
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>
* or write to the Free Software Foundation, Inc., 51 Franklin Street,
* Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.bonsaimind.jmathpaper.core.ui;
/**
* Defines the part of a paper.
*/
public enum PaperPart {
/** The expression of a line/expression. */
EXPRESSION("exp"),
/** The ID of a line/expression. */
ID(),
/** The whole line-expression. */
LINE(),
/** The whole paper. */
PAPER(),
/** The result of a line/expression. */
RESULT("res");
private String[] aliases = null;
private PaperPart(String... aliases) {
this.aliases = aliases;
}
public static PaperPart getPaperPart(String name) {
if (name == null || name.length() == 0) {
return null;
}
for (PaperPart paperPart : values()) {
if (paperPart.name().equalsIgnoreCase(name)) {
return paperPart;
}
for (String alias : paperPart.aliases) {
if (name.equalsIgnoreCase(alias)) {
return paperPart;
}
}
}
return null;
}
}
......@@ -34,6 +34,7 @@ import org.junit.Before;
import org.junit.Test;
public class TestAbstractPapersUi extends AbstractPapersUi {
private String clipboard = "";
private volatile boolean quitCalled = false;
public TestAbstractPapersUi() {
......@@ -81,6 +82,59 @@ public class TestAbstractPapersUi extends AbstractPapersUi {
assertLastResult(false);
}
@Test
public void testCommandCopy() throws CommandExecutionException, InvalidExpressionException {
process("1+1");
process("2+2");
process("3+3");
process("4+4");
process("5+5");
// Lines by ID
process("copy #1");
assertClipboard("#1 1+1 = 2");
process("copy #1, #2, #3");
assertClipboard("#1 1+1 = 2\n#2 2+2 = 4\n#3 3+3 = 6");
process("copy #1..#3");
assertClipboard("#1 1+1 = 2\n#2 2+2 = 4\n#3 3+3 = 6");
// Lines by index.
process("copy 1");
assertClipboard("#1 1+1 = 2");
process("copy -1");
assertClipboard("#5 5+5 = 10");
process("copy 1, 2, 3");
assertClipboard("#1 1+1 = 2\n#2 2+2 = 4\n#3 3+3 = 6");
process("copy 1..3");
assertClipboard("#1 1+1 = 2\n#2 2+2 = 4\n#3 3+3 = 6");
process("copy 1..-3");
assertClipboard("#1 1+1 = 2\n#2 2+2 = 4\n#3 3+3 = 6");
// IDs
process("copy ID 1..-3");
assertClipboard("#1\n#2\n#3");
// Expression
process("copy exp 1..-3");
assertClipboard("1+1\n2+2\n3+3");
// Result
process("copy res 1..-3");
assertClipboard("2\n4\n6");
// Copy everything
process("copy exp");
assertClipboard("1+1\n2+2\n3+3\n4+4\n5+5");
}
@Test
public void testCommandQuit() throws CommandExecutionException, InvalidExpressionException {
process("quit");
......@@ -183,6 +237,15 @@ public class TestAbstractPapersUi extends AbstractPapersUi {
assertLastResult("1000");
}
@Override
protected void copyToClipboard(String value) {
clipboard = value;
}
private final void assertClipboard(String expected) {
Assert.assertEquals(expected, clipboard);
}
private final void assertLastResult(boolean expected) {
EvaluatedExpression lastEvaluatedExpression = paper.evaluatedExpressions.get(paper.evaluatedExpressions.size() - 1);
......
......@@ -22,6 +22,7 @@ package org.bonsaimind.jmathpaper.core;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import org.bonsaimind.jmathpaper.core.configuration.ConfigurationProcessor;
import org.junit.Assert;
import org.junit.Test;
......