X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=teditor%2FDocument.java;h=2abfef6635f3c1877fc733ee36ea8c67d01160b6;hb=HEAD;hp=b4a9a3bfb6b3ab476d29b45b3a01b4fbdca77816;hpb=cd7e80930437e18ac719f8e2c5d93c2ca0d23cd8;p=nikiroo-utils.git diff --git a/teditor/Document.java b/teditor/Document.java deleted file mode 100644 index b4a9a3b..0000000 --- a/teditor/Document.java +++ /dev/null @@ -1,823 +0,0 @@ -/* - * Jexer - Java Text User Interface - * - * The MIT License (MIT) - * - * Copyright (C) 2019 Kevin Lamonte - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * @author Kevin Lamonte [kevin.lamonte@gmail.com] - * @version 1 - */ -package jexer.teditor; - -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.util.ArrayList; -import java.util.List; - -import jexer.bits.CellAttributes; - -/** - * A Document represents a text file, as a collection of lines. - */ -public class Document { - - // ------------------------------------------------------------------------ - // Variables -------------------------------------------------------------- - // ------------------------------------------------------------------------ - - /** - * The list of lines. - */ - private ArrayList lines = new ArrayList(); - - /** - * The current line number being edited. Note that this is 0-based, the - * first line is line number 0. - */ - private int lineNumber = 0; - - /** - * The overwrite flag. When true, characters overwrite data. - */ - private boolean overwrite = false; - - /** - * If true, the document has been edited. - */ - private boolean dirty = false; - - /** - * The default color for the TEditor class. - */ - private CellAttributes defaultColor = null; - - /** - * The text highlighter to use. - */ - private Highlighter highlighter = new Highlighter(); - - /** - * The tab stop size. - */ - private int tabSize = 8; - - /** - * If true, backspace at an indent level goes back a full indent level. - * If false, backspace always goes back one column. - */ - private boolean backspaceUnindents = false; - - /** - * If true, save files with tab characters. If false, convert tabs to - * spaces when saving files. - */ - private boolean saveWithTabs = false; - - // ------------------------------------------------------------------------ - // Constructors ----------------------------------------------------------- - // ------------------------------------------------------------------------ - - /** - * Construct a new Document from an existing text string. - * - * @param str the text string - * @param defaultColor the color for unhighlighted text - */ - public Document(final String str, final CellAttributes defaultColor) { - this.defaultColor = defaultColor; - - // Set colors to resemble the Borland IDE colors, but for Java - // language keywords. - highlighter.setJavaColors(); - - String [] rawLines = str.split("\n"); - for (int i = 0; i < rawLines.length; i++) { - lines.add(new Line(rawLines[i], this.defaultColor, highlighter)); - } - } - - /** - * Private constructor used by dup(). - */ - private Document() { - // NOP - } - - // ------------------------------------------------------------------------ - // Document --------------------------------------------------------------- - // ------------------------------------------------------------------------ - - /** - * Create a duplicate instance. - * - * @return duplicate intance - */ - public Document dup() { - Document other = new Document(); - for (Line line: lines) { - other.lines.add(line.dup()); - } - other.lineNumber = lineNumber; - other.overwrite = overwrite; - other.dirty = dirty; - other.defaultColor = defaultColor; - other.highlighter.setTo(highlighter); - return other; - } - - /** - * Get the overwrite flag. - * - * @return true if addChar() overwrites data, false if it inserts - */ - public boolean isOverwrite() { - return overwrite; - } - - /** - * Get the dirty value. - * - * @return true if the buffer is dirty - */ - public boolean isDirty() { - return dirty; - } - - /** - * Unset the dirty flag. - */ - public void setNotDirty() { - dirty = false; - } - - /** - * Save contents to file. - * - * @param filename file to save to - * @throws IOException if a java.io operation throws - */ - public void saveToFilename(final String filename) throws IOException { - OutputStreamWriter output = null; - try { - output = new OutputStreamWriter(new FileOutputStream(filename), - "UTF-8"); - - for (Line line: lines) { - if (saveWithTabs) { - output.write(convertSpacesToTabs(line.getRawString())); - } else { - output.write(line.getRawString()); - } - output.write("\n"); - } - - dirty = false; - } - finally { - if (output != null) { - output.close(); - } - } - } - - /** - * Set the overwrite flag. - * - * @param overwrite true if addChar() should overwrite data, false if it - * should insert - */ - public void setOverwrite(final boolean overwrite) { - this.overwrite = overwrite; - } - - /** - * Get the current line number being edited. - * - * @return the line number. Note that this is 0-based: 0 is the first - * line. - */ - public int getLineNumber() { - return lineNumber; - } - - /** - * Get the current editing line. - * - * @return the line - */ - public Line getCurrentLine() { - return lines.get(lineNumber); - } - - /** - * Get a specific line by number. - * - * @param lineNumber the line number. Note that this is 0-based: 0 is - * the first line. - * @return the line - */ - public Line getLine(final int lineNumber) { - return lines.get(lineNumber); - } - - /** - * Set the current line number being edited. - * - * @param n the line number. Note that this is 0-based: 0 is the first - * line. - */ - public void setLineNumber(final int n) { - if ((n < 0) || (n > lines.size())) { - throw new IndexOutOfBoundsException("Lines array size is " + - lines.size() + ", requested index " + n); - } - lineNumber = n; - } - - /** - * Get the current cursor position of the editing line. - * - * @return the cursor position - */ - public int getCursor() { - return lines.get(lineNumber).getCursor(); - } - - /** - * Get the character at the current cursor position in the text. - * - * @return the character, or -1 if the cursor is at the end of the line - */ - public int getChar() { - return lines.get(lineNumber).getChar(); - } - - /** - * Set the current cursor position of the editing line. 0-based. - * - * @param cursor the new cursor position - */ - public void setCursor(final int cursor) { - if (cursor >= lines.get(lineNumber).getDisplayLength()) { - lines.get(lineNumber).end(); - } else { - lines.get(lineNumber).setCursor(cursor); - } - } - - /** - * Increment the line number by one. If at the last line, do nothing. - * - * @return true if the editing line changed - */ - public boolean down() { - if (lineNumber < lines.size() - 1) { - int x = lines.get(lineNumber).getCursor(); - lineNumber++; - if (x >= lines.get(lineNumber).getDisplayLength()) { - lines.get(lineNumber).end(); - } else { - lines.get(lineNumber).setCursor(x); - } - return true; - } - return false; - } - - /** - * Increment the line number by n. If n would go past the last line, - * increment only to the last line. - * - * @param n the number of lines to increment by - * @return true if the editing line changed - */ - public boolean down(final int n) { - if (lineNumber < lines.size() - 1) { - int x = lines.get(lineNumber).getCursor(); - lineNumber += n; - if (lineNumber > lines.size() - 1) { - lineNumber = lines.size() - 1; - } - if (x >= lines.get(lineNumber).getDisplayLength()) { - lines.get(lineNumber).end(); - } else { - lines.get(lineNumber).setCursor(x); - } - return true; - } - return false; - } - - /** - * Decrement the line number by one. If at the first line, do nothing. - * - * @return true if the editing line changed - */ - public boolean up() { - if (lineNumber > 0) { - int x = lines.get(lineNumber).getCursor(); - lineNumber--; - if (x >= lines.get(lineNumber).getDisplayLength()) { - lines.get(lineNumber).end(); - } else { - lines.get(lineNumber).setCursor(x); - } - return true; - } - return false; - } - - /** - * Decrement the line number by n. If n would go past the first line, - * decrement only to the first line. - * - * @param n the number of lines to decrement by - * @return true if the editing line changed - */ - public boolean up(final int n) { - if (lineNumber > 0) { - int x = lines.get(lineNumber).getCursor(); - lineNumber -= n; - if (lineNumber < 0) { - lineNumber = 0; - } - if (x >= lines.get(lineNumber).getDisplayLength()) { - lines.get(lineNumber).end(); - } else { - lines.get(lineNumber).setCursor(x); - } - return true; - } - return false; - } - - /** - * Decrement the cursor by one. If at the first column on the first - * line, do nothing. - * - * @return true if the cursor position changed - */ - public boolean left() { - if (!lines.get(lineNumber).left()) { - // We are on the leftmost column, wrap - if (up()) { - end(); - } else { - return false; - } - } - return true; - } - - /** - * Increment the cursor by one. If at the last column on the last line, - * do nothing. - * - * @return true if the cursor position changed - */ - public boolean right() { - if (!lines.get(lineNumber).right()) { - // We are on the rightmost column, wrap - if (down()) { - home(); - } else { - return false; - } - } - return true; - } - - /** - * Go back to the beginning of this word if in the middle, or the - * beginning of the previous word. - */ - public void backwardsWord() { - - // If at the beginning of a word already, push past it. - if ((getChar() != -1) - && (getRawLine().length() > 0) - && !Character.isWhitespace((char) getChar()) - ) { - left(); - } - - // int line = lineNumber; - while ((getChar() == -1) - || (getRawLine().length() == 0) - || Character.isWhitespace((char) getChar()) - ) { - if (left() == false) { - return; - } - } - - - assert (getChar() != -1); - - if (!Character.isWhitespace((char) getChar()) - && (getRawLine().length() > 0) - ) { - // Advance until at the beginning of the document or a whitespace - // is encountered. - while (!Character.isWhitespace((char) getChar())) { - int line = lineNumber; - if (left() == false) { - // End of document, bail out. - return; - } - if (lineNumber != line) { - // We wrapped a line. Here that counts as whitespace. - right(); - return; - } - } - } - - // We went one past the word, push back to the first character of - // that word. - right(); - return; - } - - /** - * Go to the beginning of the next word. - */ - public void forwardsWord() { - int line = lineNumber; - while ((getChar() == -1) - || (getRawLine().length() == 0) - ) { - if (right() == false) { - return; - } - if (lineNumber != line) { - // We wrapped a line. Here that counts as whitespace. - if (!Character.isWhitespace((char) getChar())) { - // We found a character immediately after the line. - // Done! - return; - } - // Still looking... - line = lineNumber; - } - } - assert (getChar() != -1); - - if (!Character.isWhitespace((char) getChar()) - && (getRawLine().length() > 0) - ) { - // Advance until at the end of the document or a whitespace is - // encountered. - while (!Character.isWhitespace((char) getChar())) { - line = lineNumber; - if (right() == false) { - // End of document, bail out. - return; - } - if (lineNumber != line) { - // We wrapped a line. Here that counts as whitespace. - if (!Character.isWhitespace((char) getChar()) - && (getRawLine().length() > 0) - ) { - // We found a character immediately after the line. - // Done! - return; - } - break; - } - } - } - - while ((getChar() == -1) - || (getRawLine().length() == 0) - ) { - if (right() == false) { - return; - } - if (lineNumber != line) { - // We wrapped a line. Here that counts as whitespace. - if (!Character.isWhitespace((char) getChar())) { - // We found a character immediately after the line. - // Done! - return; - } - // Still looking... - line = lineNumber; - } - } - assert (getChar() != -1); - - if (Character.isWhitespace((char) getChar())) { - // Advance until at the end of the document or a non-whitespace - // is encountered. - while (Character.isWhitespace((char) getChar())) { - if (right() == false) { - // End of document, bail out. - return; - } - } - return; - } - - // We wrapped the line to get here. - return; - } - - /** - * Get the raw string that matches this line. - * - * @return the string - */ - public String getRawLine() { - return lines.get(lineNumber).getRawString(); - } - - /** - * Go to the first column of this line. - * - * @return true if the cursor position changed - */ - public boolean home() { - return lines.get(lineNumber).home(); - } - - /** - * Go to the last column of this line. - * - * @return true if the cursor position changed - */ - public boolean end() { - return lines.get(lineNumber).end(); - } - - /** - * Delete the character under the cursor. - */ - public void del() { - dirty = true; - int cursor = lines.get(lineNumber).getCursor(); - if (cursor < lines.get(lineNumber).getDisplayLength() - 1) { - lines.get(lineNumber).del(); - } else if (lineNumber < lines.size() - 2) { - // Join two lines - StringBuilder newLine = new StringBuilder(lines. - get(lineNumber).getRawString()); - newLine.append(lines.get(lineNumber + 1).getRawString()); - lines.set(lineNumber, new Line(newLine.toString(), - defaultColor, highlighter)); - lines.get(lineNumber).setCursor(cursor); - lines.remove(lineNumber + 1); - } - } - - /** - * Delete the character immediately preceeding the cursor. - */ - public void backspace() { - dirty = true; - int cursor = lines.get(lineNumber).getCursor(); - if (cursor > 0) { - lines.get(lineNumber).backspace(tabSize, backspaceUnindents); - } else if (lineNumber > 0) { - // Join two lines - lineNumber--; - String firstLine = lines.get(lineNumber).getRawString(); - if (firstLine.length() > 0) { - // Backspacing combining two lines - StringBuilder newLine = new StringBuilder(firstLine); - newLine.append(lines.get(lineNumber + 1).getRawString()); - lines.set(lineNumber, new Line(newLine.toString(), - defaultColor, highlighter)); - lines.get(lineNumber).setCursor(firstLine.length()); - lines.remove(lineNumber + 1); - } else { - // Backspacing an empty line - lines.remove(lineNumber); - lines.get(lineNumber).setCursor(0); - } - } - } - - /** - * Split the current line into two, like pressing the enter key. - */ - public void enter() { - dirty = true; - int cursor = lines.get(lineNumber).getRawCursor(); - String original = lines.get(lineNumber).getRawString(); - String firstLine = original.substring(0, cursor); - String secondLine = original.substring(cursor); - lines.add(lineNumber + 1, new Line(secondLine, defaultColor, - highlighter)); - lines.set(lineNumber, new Line(firstLine, defaultColor, highlighter)); - lineNumber++; - lines.get(lineNumber).home(); - } - - /** - * Replace or insert a character at the cursor, depending on overwrite - * flag. - * - * @param ch the character to replace or insert - */ - public void addChar(final int ch) { - dirty = true; - if (overwrite) { - lines.get(lineNumber).replaceChar(ch); - } else { - lines.get(lineNumber).addChar(ch); - } - } - - /** - * Get the tab stop size. - * - * @return the tab stop size - */ - public int getTabSize() { - return tabSize; - } - - /** - * Set the tab stop size. - * - * @param tabSize the new tab stop size - */ - public void setTabSize(final int tabSize) { - this.tabSize = tabSize; - } - - /** - * Set the backspace unindent option. - * - * @param backspaceUnindents If true, backspace at an indent level goes - * back a full indent level. If false, backspace always goes back one - * column. - */ - public void setBackspaceUnindents(final boolean backspaceUnindents) { - this.backspaceUnindents = backspaceUnindents; - } - - /** - * Set the save with tabs option. - * - * @param saveWithTabs If true, save files with tab characters. If - * false, convert tabs to spaces when saving files. - */ - public void setSaveWithTabs(final boolean saveWithTabs) { - this.saveWithTabs = saveWithTabs; - } - - /** - * Handle the tab character. - */ - public void tab() { - if (overwrite) { - del(); - } - lines.get(lineNumber).tab(tabSize); - } - - /** - * Handle the backtab (shift-tab) character. - */ - public void backTab() { - lines.get(lineNumber).backTab(tabSize); - } - - /** - * Get a (shallow) copy of the list of lines. - * - * @return the list of lines - */ - public List getLines() { - return new ArrayList(lines); - } - - /** - * Get the number of lines. - * - * @return the number of lines - */ - public int getLineCount() { - return lines.size(); - } - - /** - * Compute the maximum line length for this document. - * - * @return the number of cells needed to display the longest line - */ - public int getLineLengthMax() { - int n = 0; - for (Line line : lines) { - if (line.getDisplayLength() > n) { - n = line.getDisplayLength(); - } - } - return n; - } - - /** - * Get the current line length. - * - * @return the number of cells needed to display the current line - */ - public int getLineLength() { - return lines.get(lineNumber).getDisplayLength(); - } - - /** - * Get the entire contents of the document as one string. - * - * @return the document contents - */ - public String getText() { - StringBuilder sb = new StringBuilder(); - for (Line line: getLines()) { - sb.append(line.getRawString()); - sb.append("\n"); - } - return sb.toString(); - } - - /** - * Trim trailing whitespace from lines and trailing empty - * lines from the document. - */ - public void cleanWhitespace() { - for (Line line: getLines()) { - line.trimRight(); - } - if (lines.size() == 0) { - return; - } - while (lines.get(lines.size() - 1).length() == 0) { - lines.remove(lines.size() - 1); - } - if (lineNumber > lines.size() - 1) { - lineNumber = lines.size() - 1; - } - } - - /** - * Set keyword highlighting. - * - * @param enabled if true, enable keyword highlighting - */ - public void setHighlighting(final boolean enabled) { - highlighter.setEnabled(enabled); - for (Line line: getLines()) { - line.scanLine(); - } - } - - /** - * Convert a string with leading spaces to a mix of tabs and spaces. - * - * @param string the string to convert - */ - private String convertSpacesToTabs(final String string) { - if (string.length() == 0) { - return string; - } - - int start = 0; - while (string.charAt(start) == ' ') { - start++; - } - int tabCount = start / 8; - if (tabCount == 0) { - return string; - } - - StringBuilder sb = new StringBuilder(string.length()); - - for (int i = 0; i < tabCount; i++) { - sb.append('\t'); - } - sb.append(string.substring(tabCount * 8)); - return sb.toString(); - } - -}