| Author | DaveJarvis <email> |
|---|---|
| Date | 2022-01-02 17:13:42 GMT-0800 |
| Commit | 479fbeb2fcef51efe6f6778d52b3a9655d983d9b |
| Parent | 46c7c78 |
| import com.keenwrite.cmdline.Arguments; | ||
| -import com.keenwrite.processors.ProcessorContext; | ||
| -import com.keenwrite.typesetting.Typesetter; | ||
| -import com.keenwrite.ui.dialogs.ThemePicker; | ||
| import com.keenwrite.util.AlphanumComparator; | ||
| import java.io.IOException; | ||
| import java.nio.file.Path; | ||
| import java.util.ArrayList; | ||
| import java.util.concurrent.Callable; | ||
| import java.util.concurrent.CompletableFuture; | ||
| import java.util.concurrent.ExecutorService; | ||
| +import java.util.concurrent.atomic.AtomicInteger; | ||
| -import static com.keenwrite.ExportFormat.*; | ||
| +import static com.keenwrite.Launcher.terminate; | ||
| import static com.keenwrite.processors.ProcessorFactory.createProcessors; | ||
| import static com.keenwrite.util.FileWalker.walk; | ||
| public static void run( final Arguments args ) { | ||
| - final var context = args.createProcessorContext(); | ||
| + final var exitCode = new AtomicInteger(); | ||
| + | ||
| + final var future = new CompletableFuture<Path>() { | ||
| + @Override | ||
| + public boolean complete( final Path path ) { | ||
| + System.out.println( " HURRAH! " + path ); | ||
| + return super.complete( path ); | ||
| + } | ||
| + | ||
| + @Override | ||
| + public boolean completeExceptionally( final Throwable ex ) { | ||
| + System.out.println( "PROBLEMO! " + ex.getMessage() ); | ||
| + exitCode.set( 1 ); | ||
| + | ||
| + return super.completeExceptionally( ex ); | ||
| + } | ||
| + }; | ||
| + | ||
| + file_export( args, future ); | ||
| + | ||
| + future.join(); | ||
| + terminate( exitCode.get() ); | ||
| } | ||
| /** | ||
| * Converts one or more files into the given file format. If {@code dir} | ||
| * is set to true, this will first append all files in the same directory | ||
| * as the actively edited file. | ||
| * | ||
| - * @param inputPath The source document to export in the given file format. | ||
| - * @param format The destination file format. | ||
| - * @param concat Export all files in the actively edited file's directory. | ||
| - * @param future Indicates whether the export succeeded or failed. | ||
| + * @param future Indicates whether the export succeeded or failed. | ||
| */ | ||
| - private void file_export( | ||
| - final Path inputPath, | ||
| - final ExportFormat format, | ||
| - final boolean concat, | ||
| - final CompletableFuture<Path> future ) { | ||
| + private static void file_export( | ||
| + final Arguments args, final CompletableFuture<Path> future ) { | ||
| + assert args != null; | ||
| + | ||
| final Callable<Path> callableTask = () -> { | ||
| try { | ||
| - final var context = ProcessorContext.create( inputPath, format ); | ||
| - final var outputPath = format.toExportPath( inputPath ); | ||
| + final var context = args.createProcessorContext(); | ||
| + final var concat = context.getConcatenate(); | ||
| + final var inputPath = context.getInputPath(); | ||
| + final var outputPath = context.getOutputPath(); | ||
| final var chain = createProcessors( context ); | ||
| final var inputDoc = read( inputPath, concat ); | ||
| final var outputDoc = chain.apply( inputDoc ); | ||
| // Processors can export binary files. In such cases, processors will | ||
| // return null to prevent further processing. | ||
| final var result = | ||
| outputDoc == null ? null : writeString( outputPath, outputDoc ); | ||
| - future.complete( result ); | ||
| + future.complete( outputPath ); | ||
| return result; | ||
| } catch( final Exception ex ) { | ||
| * | ||
| private void file_export_pdf( final Path theme, final boolean concat ) { | ||
| - if( Typesetter.canRun() ) { | ||
| - // If the typesetter is installed, allow the user to select a theme. If | ||
| - // the themes aren't installed, a status message will appear. | ||
| - if( ThemePicker.choose( themes, theme ) ) { | ||
| - file_export( APPLICATION_PDF, concat ); | ||
| - } | ||
| - } | ||
| - else { | ||
| - fireExportFailedEvent(); | ||
| - } | ||
| + if( Typesetter.canRun() ) { | ||
| + // If the typesetter is installed, allow the user to select a theme. If | ||
| + // the themes aren't installed, a status message will appear. | ||
| + if( ThemePicker.choose( themes, theme ) ) { | ||
| + file_export( APPLICATION_PDF, concat ); | ||
| + } | ||
| + } | ||
| + else { | ||
| + fireExportFailedEvent(); | ||
| + } | ||
| } | ||
| public void file_export_pdf() { | ||
| - file_export_pdf( false ); | ||
| + file_export_pdf( false ); | ||
| } | ||
| public void file_export_pdf_dir() { | ||
| - file_export_pdf( true ); | ||
| + file_export_pdf( true ); | ||
| } | ||
| public void file_export_html_svg() { | ||
| - file_export( HTML_TEX_SVG ); | ||
| + file_export( HTML_TEX_SVG ); | ||
| } | ||
| public void file_export_html_tex() { | ||
| - file_export( HTML_TEX_DELIMITED ); | ||
| + file_export( HTML_TEX_DELIMITED ); | ||
| } | ||
| public void file_export_xhtml_tex() { | ||
| - file_export( XHTML_TEX ); | ||
| + file_export( XHTML_TEX ); | ||
| } | ||
| public void file_export_markdown() { | ||
| - file_export( MARKDOWN_PLAIN ); | ||
| + file_export( MARKDOWN_PLAIN ); | ||
| } | ||
| -*/ | ||
| + */ | ||
| + | ||
| /** | ||
| * Concatenates all the files in the same directory as the given file into | ||
| * concatenated into a single string. | ||
| */ | ||
| - private String read( final Path inputPath, final boolean concat ) | ||
| + private static String read( final Path inputPath, final boolean concat ) | ||
| throws IOException { | ||
| final var parent = inputPath.getParent(); | ||
| private final String[] mArgs; | ||
| - private static void parse( final String[] args ) { | ||
| - assert args != null; | ||
| - | ||
| - final var arguments = new Arguments( new Launcher( args ) ); | ||
| - final var parser = new CommandLine( arguments ); | ||
| - | ||
| - parser.setColorScheme( ColourScheme.create() ); | ||
| - | ||
| - final var exitCode = parser.execute( args ); | ||
| - final var parseResult = parser.getParseResult(); | ||
| - | ||
| - if( parseResult.isUsageHelpRequested() ) { | ||
| - System.exit( exitCode ); | ||
| - } | ||
| - } | ||
| - | ||
| - /** | ||
| - * Suppress writing to standard error, suppresses writing log messages. | ||
| - */ | ||
| - private static void disableLogging() { | ||
| - LogManager.getLogManager().reset(); | ||
| - System.err.close(); | ||
| - } | ||
| - | ||
| - private static void showAppInfo() { | ||
| - out( "%n%s version %s", APP_TITLE, APP_VERSION ); | ||
| - out( "Copyright 2016-%s White Magic Software, Ltd.", APP_YEAR ); | ||
| - out( "Portions copyright 2015-2020 Karl Tauber.%n" ); | ||
| - } | ||
| - | ||
| /** | ||
| * Returns the application version number retrieved from the application | ||
| } catch( final Exception ex ) { | ||
| throw new RuntimeException( ex ); | ||
| + } | ||
| + } | ||
| + | ||
| + /** | ||
| + * Immediately exits the application. | ||
| + * | ||
| + * @param exitCode Code to provide back to the calling shell. | ||
| + */ | ||
| + public static void terminate( final int exitCode ) { | ||
| + System.exit( exitCode ); | ||
| + } | ||
| + | ||
| + private static void parse( final String[] args ) { | ||
| + assert args != null; | ||
| + | ||
| + final var arguments = new Arguments( new Launcher( args ) ); | ||
| + final var parser = new CommandLine( arguments ); | ||
| + | ||
| + parser.setColorScheme( ColourScheme.create() ); | ||
| + | ||
| + final var exitCode = parser.execute( args ); | ||
| + final var parseResult = parser.getParseResult(); | ||
| + | ||
| + if( parseResult.isUsageHelpRequested() ) { | ||
| + terminate( exitCode ); | ||
| } | ||
| } | ||
| log( t ); | ||
| } | ||
| + } | ||
| + | ||
| + /** | ||
| + * Suppress writing to standard error, suppresses writing log messages. | ||
| + */ | ||
| + private static void disableLogging() { | ||
| + LogManager.getLogManager().reset(); | ||
| + System.err.close(); | ||
| } | ||
| + private static void showAppInfo() { | ||
| + out( "%n%s version %s", APP_TITLE, APP_VERSION ); | ||
| + out( "Copyright 2016-%s White Magic Software, Ltd.", APP_YEAR ); | ||
| + out( "Portions copyright 2015-2020 Karl Tauber.%n" ); | ||
| + } | ||
| } | ||
| import static com.keenwrite.ExportFormat.NONE; | ||
| +import static com.keenwrite.Launcher.terminate; | ||
| import static com.keenwrite.Messages.get; | ||
| import static com.keenwrite.constants.Constants.*; | ||
| if( closeAll() ) { | ||
| Platform.exit(); | ||
| - System.exit( 0 ); | ||
| + terminate( 0 ); | ||
| } | ||
| name = "KeenWrite", | ||
| mixinStandardHelpOptions = true, | ||
| - description = "Plain text editor for editing with variables." | ||
| + description = "Plain text editor for editing with variables" | ||
| ) | ||
| @SuppressWarnings( "unused" ) | ||
| public final class Arguments implements Callable<Integer>, KeyConfiguration { | ||
| @CommandLine.Option( | ||
| names = {"-a", "--all"}, | ||
| description = | ||
| - "Concatenate files in directory before processing (${DEFAULT-VALUE}).", | ||
| + "Concatenate files in directory before processing (${DEFAULT-VALUE})", | ||
| defaultValue = "false" | ||
| ) | ||
| private boolean mAll; | ||
| + | ||
| + @CommandLine.Option( | ||
| + names = {"-b", "--base-path"}, | ||
| + description = | ||
| + "Set all other paths relative to this path", | ||
| + paramLabel = "PATH" | ||
| + ) | ||
| + private Path mBasePath; | ||
| @CommandLine.Option( | ||
| names = {"-k", "--keep-files"}, | ||
| description = | ||
| - "Keep temporary build files (${DEFAULT-VALUE}).", | ||
| + "Keep temporary build files (${DEFAULT-VALUE})", | ||
| defaultValue = "false" | ||
| ) | ||
| private boolean mKeepFiles; | ||
| @CommandLine.Option( | ||
| names = {"-d", "--debug"}, | ||
| description = | ||
| - "Enable logging to the console (${DEFAULT-VALUE}).", | ||
| + "Enable logging to the console (${DEFAULT-VALUE})", | ||
| defaultValue = "false" | ||
| ) | ||
| private boolean mDebug; | ||
| @CommandLine.Option( | ||
| names = {"-i", "--input"}, | ||
| description = | ||
| - "Set the file name to read.", | ||
| + "Set the file name to read", | ||
| paramLabel = "PATH", | ||
| defaultValue = "stdin", | ||
| names = {"-m", "--metadata"}, | ||
| description = | ||
| - "Map metadata keys to values, variable names allowed.", | ||
| + "Map metadata keys to values, variable names allowed", | ||
| paramLabel = "key=value" | ||
| ) | ||
| private Map<String, String> mMetadata; | ||
| @CommandLine.Option( | ||
| names = {"-o", "--output"}, | ||
| description = | ||
| - "Set the file name to write.", | ||
| + "Set the file name to write", | ||
| paramLabel = "PATH", | ||
| defaultValue = "stdout", | ||
| required = true | ||
| ) | ||
| private File mPathOutput; | ||
| @CommandLine.Option( | ||
| names = {"-p", "--images-path"}, | ||
| description = | ||
| - "Absolute path to images directory", | ||
| + "Directory containing images", | ||
| paramLabel = "PATH" | ||
| ) | ||
| private Path mPathImages; | ||
| @CommandLine.Option( | ||
| names = {"-q", "--quiet"}, | ||
| description = | ||
| - "Suppress all status messages (${DEFAULT-VALUE}).", | ||
| + "Suppress all status messages (${DEFAULT-VALUE})", | ||
| defaultValue = "false" | ||
| ) | ||
| names = {"-t", "--theme"}, | ||
| description = | ||
| - "Full theme name file path to use when exporting as a PDF file.", | ||
| + "File path to use when exporting as a PDF file", | ||
| paramLabel = "PATH" | ||
| ) | ||
| private Path mThemeName; | ||
| @CommandLine.Option( | ||
| names = {"-x", "--image-extensions"}, | ||
| description = | ||
| - "Space-separated image file name extensions (${DEFAULT-VALUE}).", | ||
| + "Space-separated image file name extensions (${DEFAULT-VALUE})", | ||
| paramLabel = "String", | ||
| defaultValue = "svg pdf png jpg tiff" | ||
| ) | ||
| private Set<String> mImageExtensions; | ||
| @CommandLine.Option( | ||
| names = {"-v", "--variables"}, | ||
| description = | ||
| - "Set the file name containing variable definitions (${DEFAULT-VALUE}).", | ||
| + "Set the file name containing variable definitions (${DEFAULT-VALUE})", | ||
| paramLabel = "FILE", | ||
| defaultValue = "variables.yaml" | ||
| mValues.put( KEY_IMAGES_DIR, mPathImages ); | ||
| mValues.put( KEY_TYPESET_CONTEXT_THEMES_PATH, mThemeName.getParent() ); | ||
| - mValues.put( KEY_TYPESET_CONTEXT_THEME_SELECTION, mThemeName.getFileName() ); | ||
| + mValues.put( KEY_TYPESET_CONTEXT_THEME_SELECTION, | ||
| + mThemeName.getFileName() ); | ||
| mValues.put( KEY_TYPESET_CONTEXT_CLEAN, !mKeepFiles ); | ||
| private Supplier<Caret> mCaret; | ||
| private Workspace mWorkspace; | ||
| + private boolean mConcatenate; | ||
| public void setInputPath( final Path inputPath ) { | ||
| + assert inputPath != null; | ||
| mInputPath = inputPath; | ||
| - } | ||
| - | ||
| - public void setInputPath( final File inputPath ) { | ||
| - setInputPath( inputPath.toPath() ); | ||
| } | ||
| public void setOutputPath( final Path outputPath ) { | ||
| + assert outputPath != null; | ||
| mOutputPath = outputPath; | ||
| } | ||
| public void setOutputPath( final File outputPath ) { | ||
| + assert outputPath != null; | ||
| setOutputPath( outputPath.toPath() ); | ||
| } | ||
| * prevents coupling the GUI to the CLI. | ||
| * | ||
| - * @param definitions Defines how to retrieve the definitions. | ||
| + * @param supplier Defines how to retrieve the definitions. | ||
| */ | ||
| - public void setDefinitions( | ||
| - final Supplier<Map<String, String>> definitions ) { | ||
| - mDefinitions = definitions; | ||
| + public void setDefinitions( final Supplier<Map<String, String>> supplier ) { | ||
| + assert supplier != null; | ||
| + mDefinitions = supplier; | ||
| } | ||
| /** | ||
| * Sets the source for deriving the {@link Caret}. Typically, this is | ||
| * the text editor that has focus. | ||
| * | ||
| * @param caret The source for the currently active caret. | ||
| */ | ||
| public void setCaret( final Supplier<Caret> caret ) { | ||
| + assert caret != null; | ||
| mCaret = caret; | ||
| } | ||
| public void setExportFormat( final ExportFormat exportFormat ) { | ||
| + assert exportFormat != null; | ||
| mExportFormat = exportFormat; | ||
| } | ||
| public void setWorkspace( final Workspace workspace ) { | ||
| + assert workspace != null; | ||
| mWorkspace = workspace; | ||
| + } | ||
| + | ||
| + public void setConcatenate( final boolean concatenate ) { | ||
| + mConcatenate = concatenate; | ||
| } | ||
| } | ||
| public InterpolatingMap getInterpolatedDefinitions() { | ||
| final var map = new InterpolatingMap( | ||
| - createDefinitionSigilOperator(), getDefinitions() | ||
| + getWorkspace().createDefinitionKeyOperator(), getDefinitions() | ||
| ); | ||
| } | ||
| - public SigilKeyOperator createSigilOperator() { | ||
| - return getWorkspace().createSigilOperator( getInputPath() ); | ||
| + /** | ||
| + * Answers whether to process a single text file or all text files in | ||
| + * the same directory as a single text file. See {@link #getInputPath()} | ||
| + * for the file to process (or all files in its directory). | ||
| + * | ||
| + * @return {@code true} means to process all text files, {@code false} | ||
| + * means to process a single file. | ||
| + */ | ||
| + public boolean getConcatenate() { | ||
| + return mMutator.mConcatenate; | ||
| } | ||
| - public SigilKeyOperator createDefinitionSigilOperator() { | ||
| - return getWorkspace().createDefinitionKeyOperator(); | ||
| + public SigilKeyOperator createSigilOperator() { | ||
| + return getWorkspace().createSigilOperator( getInputPath() ); | ||
| } | ||
| } | ||
| Delta | 143 lines added, 92 lines removed, 51-line increase |
|---|