File groovy-2.4.21-jansi.patch of Package groovy

--- groovy-2.4.21/src/main/org/codehaus/groovy/tools/shell/IO.java	2022-05-13 01:20:10.870359670 +0200
+++ groovy-2.4.21/src/main/org/codehaus/groovy/tools/shell/IO.java	2022-05-13 01:21:28.170851904 +0200
@@ -18,8 +18,8 @@
  */
 package org.codehaus.groovy.tools.shell;
 
+import org.codehaus.groovy.runtime.InvokerHelper;
 import org.codehaus.groovy.tools.shell.util.Preferences;
-import org.fusesource.jansi.AnsiRenderWriter;
 
 import java.io.Closeable;
 import java.io.IOException;
@@ -36,6 +36,8 @@
  */
 public class IO implements Closeable
 {
+    private static final String ANSI_RENDER_WRITER = "org.fusesource.jansi.AnsiRenderWriter";
+
     /** Raw input stream. */
     public final InputStream inputStream;
 
@@ -55,6 +57,11 @@
     public final PrintWriter err;
 
     /**
+     * Whether ansi support is available
+     */
+    public final boolean ansiSupported;
+
+    /**
      * Construct a new IO container.
      */
     public IO(final InputStream inputStream, final OutputStream outputStream, final OutputStream errorStream) {
@@ -67,8 +74,36 @@
         this.errorStream = errorStream;
 
         this.in = new InputStreamReader(inputStream);
-        this.out = new AnsiRenderWriter(outputStream, true);
-        this.err = new AnsiRenderWriter(errorStream, true);
+        boolean ansiSupported = false;
+        try {
+            Class.forName(ANSI_RENDER_WRITER, false, IO.class.getClassLoader());
+            ansiSupported = true;
+        } catch (ClassNotFoundException ignore) {
+        }
+        this.ansiSupported = ansiSupported;
+        PrintWriter out = null;
+        PrintWriter err = null;
+        if (ansiSupported) {
+            out = tryConstructRenderWriter(outputStream);
+            err = tryConstructRenderWriter(errorStream);
+        }
+        if (out == null) {
+            out = new PrintWriter(outputStream, true);
+        }
+        if (err == null) {
+            err = new PrintWriter(errorStream, true);
+        }
+        this.out = out;
+        this.err = err;
+    }
+
+    protected PrintWriter tryConstructRenderWriter(OutputStream stream) {
+        // load via reflection to avoid hard-coded dependency on jansi jar
+        try {
+            return (PrintWriter) InvokerHelper.invokeConstructorOf(ANSI_RENDER_WRITER, new Object[]{stream, true});
+        } catch (ClassNotFoundException ignore) {
+            return null;
+        }
     }
 
     /**
--- groovy-2.4.21/src/main/org/codehaus/groovy/tools/shell/util/Logger.java	2022-05-13 01:20:10.870359670 +0200
+++ groovy-2.4.21/src/main/org/codehaus/groovy/tools/shell/util/Logger.java	2022-05-13 01:21:28.170851904 +0200
@@ -60,13 +60,12 @@
             }
         }
 
-        Color color = GREEN;
-        if (WARN.equals(level) || ERROR.equals(level)) {
-            color = RED;
+        if (io.ansiSupported) {
+            logWithAnsi(level, msg);
+        } else {
+            logDefault(level, msg);
         }
 
-        io.out.println(ansi().a(INTENSITY_BOLD).fg(color).a(level).reset().a(" [").a(name).a("] ").a(msg));
-
         if (cause != null) {
             cause.printStackTrace(io.out);
         }
@@ -74,6 +73,18 @@
         io.flush();
     }
     
+    private void logDefault(String level, Object msg) {
+        io.out.println(level + " [" + name + "] " + msg);
+    }
+
+    private void logWithAnsi(String level, Object msg) {
+        Color color = GREEN;
+        if (WARN.equals(level) || ERROR.equals(level)) {
+            color = RED;
+        }
+        io.out.println(ansi().a(INTENSITY_BOLD).fg(color).a(level).reset().a(" [").a(name).a("] ").a(msg));
+    }
+
     //
     // Level helpers
     //
--- groovy-2.4.21/subprojects/groovy-groovysh/src/main/groovy/org/codehaus/groovy/tools/shell/InteractiveShellRunner.groovy	2022-05-13 01:20:10.938360103 +0200
+++ groovy-2.4.21/subprojects/groovy-groovysh/src/main/groovy/org/codehaus/groovy/tools/shell/InteractiveShellRunner.groovy	2022-05-13 01:21:28.174851930 +0200
@@ -20,6 +20,8 @@
 
 import jline.console.ConsoleReader
 import jline.console.completer.AggregateCompleter
+import jline.console.completer.CandidateListCompletionHandler
+import jline.console.completer.CompletionHandler
 import jline.console.history.FileHistory
 import org.codehaus.groovy.tools.shell.completion.CustomClassSyntaxCompletor
 import org.codehaus.groovy.tools.shell.completion.FileNameCompleter
@@ -53,8 +55,16 @@
 
         this.prompt = prompt
         this.wrappedInputStream = new WrappedInputStream(shell.io.inputStream)
-        this.reader = new PatchedConsoleReader(wrappedInputStream, shell.io.outputStream)
-        this.reader.setCompletionHandler(new PatchedCandidateListCompletionHandler())
+        this.reader = new ConsoleReader(wrappedInputStream, shell.io.outputStream)
+
+        CompletionHandler currentCompletionHandler = this.reader.getCompletionHandler()
+        if (currentCompletionHandler instanceof CandidateListCompletionHandler) {
+            // have to downcast because methods not part of the interface
+            ((CandidateListCompletionHandler) currentCompletionHandler).setStripAnsi(true)
+            ((CandidateListCompletionHandler) currentCompletionHandler).setPrintSpaceAfterFullCompletion(false)
+        }
+
+
         // expand events ia an advanced feature of JLine that clashes with Groovy syntax (e.g. invoke "2!=3")
         this.reader.expandEvents = false
 
--- groovy-2.4.21/subprojects/groovy-groovysh/src/main/groovy/org/codehaus/groovy/tools/shell/Main.groovy	2022-05-13 01:20:10.938360103 +0200
+++ groovy-2.4.21/subprojects/groovy-groovysh/src/main/groovy/org/codehaus/groovy/tools/shell/Main.groovy	2022-05-13 01:23:33.135645959 +0200
@@ -22,7 +22,6 @@
 import jline.UnixTerminal
 import jline.UnsupportedTerminal
 import jline.WindowsTerminal
-import org.codehaus.groovy.tools.shell.util.HelpFormatter
 import org.codehaus.groovy.tools.shell.util.Logger
 import org.codehaus.groovy.tools.shell.util.MessageSource
 import org.codehaus.groovy.tools.shell.util.NoExitSecurityManager
@@ -66,8 +64,9 @@
      * @param main must have a Groovysh member that has an IO member.
      */
     static void main(final String[] args) {
-        CliBuilder cli = new CliBuilder(usage: 'groovysh [options] [...]', formatter: new HelpFormatter(), stopAtNonOption: false)
         MessageSource messages = new MessageSource(Main)
+        CliBuilder cli = new CliBuilder(usage: 'groovysh [options] [...]', stopAtNonOption: false,
+                header: messages['cli.option.header'])
         cli.with {
             classpath(messages['cli.option.classpath.description'])
             cp(longOpt: 'classpath', messages['cli.option.cp.description'])
--- groovy-2.4.21/subprojects/groovy-groovysh/src/main/groovy/org/codehaus/groovy/tools/shell/PatchedCandidateListCompletionHandler.groovy	2022-05-13 01:20:10.938360103 +0200
+++ groovy-2.4.21/subprojects/groovy-groovysh/src/main/groovy/org/codehaus/groovy/tools/shell/PatchedCandidateListCompletionHandler.groovy	1970-01-01 01:00:00.000000000 +0100
@@ -1,112 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.codehaus.groovy.tools.shell
-
-import groovy.transform.CompileStatic
-import jline.console.ConsoleReader
-import jline.console.CursorBuffer
-import jline.console.completer.CandidateListCompletionHandler
-import org.codehaus.groovy.tools.shell.util.JAnsiHelper
-
-/**
- * jline completion handler displays ANSIfied candidates nicely,
- * but does not de-ANSIfy when adding to the prompt :-(
- *
- * So this class just adds this functionality.
- *
- * See https://github.com/jline/jline2/issues/132
- */
-@CompileStatic
-class PatchedCandidateListCompletionHandler extends CandidateListCompletionHandler {
-
-    public boolean complete(final ConsoleReader reader, final List<CharSequence> candidates, final int pos) throws
-            IOException
-    {
-        CursorBuffer buf = reader.getCursorBuffer();
-        final List<CharSequence> deAnsifiedcandidates = candidates.collect({CharSequence candidate -> JAnsiHelper.stripAnsi(candidate) })
-
-        // if there is only one completion, then fill in the buffer
-        if (candidates.size() == 1) {
-            CharSequence value = deAnsifiedcandidates.get(0);
-
-            // fail if the only candidate is the same as the current buffer
-            if (value.equals(buf.toString())) {
-                return false;
-            }
-
-            setBuffer(reader, value, pos);
-
-            return true;
-        }
-        else if (candidates.size() > 1) {
-            String value = this.getUnambiguousCompletions(deAnsifiedcandidates);
-            setBuffer(reader, value, pos);
-        }
-
-        printCandidates(reader, candidates);
-
-        // redraw the current console buffer
-        reader.drawLine();
-
-        return true;
-    }
-
-    /**
-     * copied from CandidateListCompletionHandler because it was private :-(
-     * Returns a root that matches all the {@link String} elements of the specified {@link List},
-     * or null if there are no commonalities. For example, if the list contains
-     * <i>foobar</i>, <i>foobaz</i>, <i>foobuz</i>, the method will return <i>foob</i>.
-     */
-    private String getUnambiguousCompletions(final List<CharSequence> candidates) {
-        if (candidates == null || candidates.isEmpty()) {
-            return null;
-        }
-
-        // convert to an array for speed
-        String[] strings = candidates.toArray(new String[candidates.size()]);
-
-        String first = strings[0];
-        StringBuilder candidate = new StringBuilder();
-
-        for (int i = 0; i < first.length(); i++) {
-            if (startsWith(first.substring(0, i + 1), strings)) {
-                candidate.append(first.charAt(i));
-            }
-            else {
-                break;
-            }
-        }
-
-        return candidate.toString();
-    }
-
-    /**
-     * copied from CandidateListCompletionHandler because it was private :-(
-     * @return true is all the elements of <i>candidates</i> start with <i>starts</i>
-     */
-    private boolean startsWith(final String starts, final String[] candidates) {
-        for (String candidate : candidates) {
-            if (!candidate.startsWith(starts)) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-}
--- groovy-2.4.21/subprojects/groovy-groovysh/src/main/groovy/org/codehaus/groovy/tools/shell/PatchedConsoleReader.groovy	2022-05-13 01:20:10.938360103 +0200
+++ groovy-2.4.21/subprojects/groovy-groovysh/src/main/groovy/org/codehaus/groovy/tools/shell/PatchedConsoleReader.groovy	1970-01-01 01:00:00.000000000 +0100
@@ -1,107 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.codehaus.groovy.tools.shell
-
-import groovy.transform.CompileStatic
-import jline.console.ConsoleReader
-import jline.internal.Log
-import org.codehaus.groovy.tools.shell.util.JAnsiHelper
-
-@CompileStatic
-class PatchedConsoleReader extends ConsoleReader {
-
-
-    public PatchedConsoleReader(final InputStream inStream, final OutputStream out) throws IOException {
-        super(inStream, out);
-    }
-
-    /**
-     * copied from jline2.0 and modified to invoke stripAnsi() for length calculations
-     * Output the specified {@link Collection} in proper columns.
-     * See https://github.com/jline/jline2/issues/132
-     */
-    public void printColumns(final Collection<? extends CharSequence> items) throws IOException {
-        if (items == null || items.isEmpty()) {
-            return;
-        }
-
-        int width = getTerminal().getWidth();
-        int height = getTerminal().getHeight();
-
-        int maxWidth = 0;
-        for (CharSequence item : items) {
-            maxWidth = Math.max(maxWidth, JAnsiHelper.stripAnsi(item).length());
-        }
-        maxWidth = maxWidth + 3;
-        Log.debug("Max width: ", maxWidth);
-
-        int showLines;
-        if (isPaginationEnabled()) {
-            showLines = height - 1; // page limit
-        }
-        else {
-            showLines = Integer.MAX_VALUE;
-        }
-
-        StringBuilder buff = new StringBuilder();
-        int realLength = 0;
-        for (CharSequence item : items) {
-            if ((realLength + maxWidth) > width) {
-                println(buff);
-                buff.setLength(0);
-                realLength = 0;
-
-                if (--showLines == 0) {
-                    // Overflow
-                    print(resources.getString("DISPLAY_MORE"));
-                    flush();
-                    int c = readCharacter();
-                    if (c == '\r' || c == '\n') {
-                        // one step forward
-                        showLines = 1;
-                    }
-                    else if (c != 'q') {
-                        // page forward
-                        showLines = height - 1;
-                    }
-
-                    back(resources.getString("DISPLAY_MORE").length());
-                    if (c == 'q') {
-                        // cancel
-                        break;
-                    }
-                }
-            }
-
-            // NOTE: toString() is important here due to AnsiString being retarded
-            buff.append(item.toString());
-            int strippedItemLength = JAnsiHelper.stripAnsi(item).length()
-            realLength += strippedItemLength
-            for (int i = 0; i < (maxWidth - strippedItemLength); i++) {
-                buff.append(' ');
-            }
-            realLength += maxWidth - strippedItemLength;
-        }
-
-        if (buff.length() > 0) {
-            println(buff);
-        }
-    }
-
-}
--- groovy-2.4.21/subprojects/groovy-groovysh/src/main/groovy/org/codehaus/groovy/tools/shell/util/HelpFormatter.groovy	2022-05-13 01:20:10.938360103 +0200
+++ groovy-2.4.21/subprojects/groovy-groovysh/src/main/groovy/org/codehaus/groovy/tools/shell/util/HelpFormatter.groovy	1970-01-01 01:00:00.000000000 +0100
@@ -1,119 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.codehaus.groovy.tools.shell.util
-
-import jline.Terminal
-import org.apache.commons.cli.Option
-import org.apache.commons.cli.Options
-
-//
-// NOTE: Some code duplicated and augmented from commons-cli (1.0) sources to
-//       properly render options w/arguments.
-//
-
-
-/**
- * Custom CLI help formatter to render things correctly.
- *
- * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
- */
-class HelpFormatter
-    extends org.apache.commons.cli.HelpFormatter
-{
-    HelpFormatter() {
-        leftPadding = 2
-        descPadding = 4
-    }
-
-    // Detect the terminal width late
-    int getDefaultWidth() {
-        return Terminal.terminal.terminalWidth - 1
-    }
-
-    @Override
-    protected StringBuffer renderOptions(final StringBuffer sb, final int width, final Options options, final int leftPad, final int descPad) {
-        assert sb != null
-        assert options
-
-        List<StringBuffer> prefixes = []
-        String lpad = ' ' * leftPad
-
-        List<Option> opts = options.shortOpts.values().sort {Option a, Option b ->
-            return (a.opt == ' ' ? a.longOpt : a.opt) <=> (b.opt == ' ' ? b.longOpt : b.opt)
-        }
-
-        // Render the prefixes (-X,--xxxx muck)
-        opts.each {Option option ->
-            StringBuffer buff = new StringBuffer(8)
-
-            if (option.opt == ' ') {
-                buff << "${lpad}    ${longOptPrefix}${option.longOpt}"
-            }
-            else {
-                buff << "${lpad}${optPrefix}${option.opt}"
-
-                if (option.hasLongOpt()) {
-                    buff << ", ${longOptPrefix}${option.longOpt}"
-                }
-            }
-
-            if (option.hasArg()) {
-                if (option.hasArgName()) {
-                    if (option.hasOptionalArg()) {
-                        buff << "[=${option.argName}]"
-                    }
-                    else {
-                        buff << "=${option.argName}"
-                    }
-                }
-                else {
-                    buff << ' '
-                }
-            }
-
-            prefixes << buff
-        }
-
-        // Figure out how long the biggest prefix is
-        int maxPrefix = prefixes.max {StringBuffer a, StringBuffer b -> a.size() <=> b.size() }.size()
-
-        String dpad = ' ' * descPad
-
-        // And then render each option's prefix with its description
-        opts.eachWithIndex {Option option, int i ->
-            def buff = new StringBuffer(prefixes[i].toString())
-
-            if (buff.size() < maxPrefix) {
-                buff << ' ' * (maxPrefix - buff.size())
-            }
-            buff << dpad
-
-            int nextLineTabStop = maxPrefix + descPad
-            String text = buff << option.description
-
-            renderWrappedText(sb, width, nextLineTabStop, text)
-
-            if (i < opts.size() - 1) {
-                sb << newLine
-            }
-        }
-
-        return sb
-    }
-}
--- groovy-2.4.21/subprojects/groovy-groovysh/src/main/groovy/org/codehaus/groovy/tools/shell/util/JAnsiHelper.groovy	2022-05-13 01:20:10.938360103 +0200
+++ groovy-2.4.21/subprojects/groovy-groovysh/src/main/groovy/org/codehaus/groovy/tools/shell/util/JAnsiHelper.groovy	1970-01-01 01:00:00.000000000 +0100
@@ -1,42 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.codehaus.groovy.tools.shell.util
-
-import groovy.transform.CompileStatic
-import org.fusesource.jansi.AnsiOutputStream
-
-@CompileStatic
-class JAnsiHelper {
-
-    /**
-     * copied from jline2 ConsoleReader
-     */
-    static CharSequence stripAnsi(final CharSequence str) {
-        if (str == null) return ''
-        try {
-            ByteArrayOutputStream baos = new ByteArrayOutputStream()
-            AnsiOutputStream aos = new AnsiOutputStream(baos)
-            aos.write(str.toString().bytes)
-            aos.flush()
-            return baos.toString()
-        } catch (IOException e) {
-            return str
-        }
-    }
-}
openSUSE Build Service is sponsored by