├── .github ├── .agp └── workflows │ ├── ant-pull.yml │ └── ant.yml ├── .gitignore ├── CITATION.cff ├── Completions ├── Readme.md ├── textidote.bash └── textidote.zsh ├── Dockerfile ├── LICENSE ├── Readme.md ├── Source ├── Core │ ├── src │ │ └── ca │ │ │ └── uqac │ │ │ └── lif │ │ │ ├── textidote │ │ │ ├── Advice.java │ │ │ ├── AdviceRenderer.java │ │ │ ├── EmptyInputException.java │ │ │ ├── Linter.java │ │ │ ├── LinterException.java │ │ │ ├── Main.java │ │ │ ├── Rule.java │ │ │ ├── RuleException.java │ │ │ ├── as │ │ │ │ ├── AnnotatedString.java │ │ │ │ ├── Line.java │ │ │ │ ├── Match.java │ │ │ │ ├── PassthroughFunction.java │ │ │ │ ├── Position.java │ │ │ │ ├── PositionRange.java │ │ │ │ └── RangeFetcher.java │ │ │ ├── cleaning │ │ │ │ ├── CompositeCleaner.java │ │ │ │ ├── ReplacementCleaner.java │ │ │ │ ├── TextCleaner.java │ │ │ │ ├── TextCleanerException.java │ │ │ │ ├── latex │ │ │ │ │ └── LatexCleaner.java │ │ │ │ └── markdown │ │ │ │ │ ├── MarkdownCleaner.java │ │ │ │ │ └── package-info.java │ │ │ ├── package-info.java │ │ │ ├── render │ │ │ │ ├── AnsiAdviceRenderer.java │ │ │ │ ├── HtmlAdviceRenderer.java │ │ │ │ ├── JsonAdviceRenderer.java │ │ │ │ ├── SinglelineAdviceRenderer.java │ │ │ │ ├── postamble.html │ │ │ │ └── preamble.html │ │ │ └── rules │ │ │ │ ├── CheckCaptions.java │ │ │ │ ├── CheckCiteMix.java │ │ │ │ ├── CheckFigurePaths.java │ │ │ │ ├── CheckFigureReferences.java │ │ │ │ ├── CheckLanguage.java │ │ │ │ ├── CheckNoBreak.java │ │ │ │ ├── CheckStackedHeadings.java │ │ │ │ ├── CheckSubsectionSize.java │ │ │ │ ├── CheckSubsections.java │ │ │ │ ├── LanguageFactory.java │ │ │ │ ├── RegexRule.java │ │ │ │ ├── SectionInfo.java │ │ │ │ ├── regex-detex.csv │ │ │ │ └── regex.csv │ │ │ └── util │ │ │ ├── AnsiPrinter.java │ │ │ └── NullPrintStream.java │ ├── test2.tex │ └── tex │ │ ├── folder │ │ └── other.tex │ │ └── other2.tex └── CoreTest │ └── src │ └── ca │ └── uqac │ └── lif │ ├── textidote │ ├── LanguageCodeTest.java │ ├── LinterTest.java │ ├── MainTest.java │ ├── as │ │ ├── AnnotatedStringTest.java │ │ ├── CheckRegexTest.java │ │ ├── PositionTest.java │ │ └── data │ │ │ ├── replacements-1.txt │ │ │ ├── replacements-2.txt │ │ │ ├── replacements-3.txt │ │ │ └── test-subsec-3.tex │ ├── cleaning │ │ ├── LatexCleanerTest.java │ │ ├── MarkdownCleanerTest.java │ │ └── data │ │ │ ├── issue215.tex │ │ │ ├── markdown-test-1.md │ │ │ ├── markdown-test-2.md │ │ │ ├── replace-with-nothing.txt │ │ │ ├── replacements-1.txt │ │ │ ├── replacements-2.txt │ │ │ ├── replacements-3.txt │ │ │ ├── test1.tex │ │ │ ├── test2.tex │ │ │ ├── test3.tex │ │ │ └── test4.tex │ ├── render │ │ ├── HtmlRenderTest.java │ │ └── SinglelineAdviceRendererTest.java │ └── rules │ │ ├── CheckAdviceTest.java │ │ ├── CheckCaptionsTest.java │ │ ├── CheckCiteMixTest.java │ │ ├── CheckFigurePathsTest.java │ │ ├── CheckFigureReferencesTest.java │ │ ├── CheckLanguageTest.java │ │ ├── CheckLevelSkipTest.java │ │ ├── CheckNoBreakTest.java │ │ ├── CheckRegexTest.java │ │ ├── CheckRule.java │ │ ├── CheckStackedHeadingsTest.java │ │ ├── CheckSubsectionSizeTest.java │ │ ├── CheckSubsectionsTest.java │ │ └── data │ │ ├── beamer.tex │ │ ├── childs │ │ ├── child-section-no-root.tex │ │ ├── child-section.tex │ │ └── child-sibling.tex │ │ ├── dictionary.txt │ │ ├── empty.txt │ │ ├── include-twice.tex │ │ ├── input1.tex │ │ ├── input2.tex │ │ ├── replace.txt │ │ ├── root.tex │ │ ├── test-input1.tex │ │ ├── test-lt-1.tex │ │ ├── test-lt-2.tex │ │ ├── test-nobreak.tex │ │ ├── test-stacked-0.tex │ │ ├── test-stacked-1.tex │ │ ├── test-subsec-1.tex │ │ ├── test-subsec-2.tex │ │ ├── test-subsec-3.tex │ │ ├── test-subsec-4.tex │ │ ├── test-subsec-stress.tex │ │ ├── test-text.txt │ │ ├── test1.md │ │ ├── test1.tex │ │ ├── test2.tex │ │ ├── test3.tex │ │ ├── test4.tex │ │ ├── test5.tex │ │ └── test6.tex │ └── util │ └── AnsiPrinterTest.java ├── build.xml ├── config.xml ├── dictionary.txt ├── docs ├── assets │ ├── css │ │ ├── responsive.css │ │ └── style.css │ ├── images │ │ ├── Screenshot.png │ │ ├── Shape.svg │ │ ├── card_back.jpg │ │ ├── card_front.jpg │ │ ├── customizable.svg │ │ ├── design.svg │ │ ├── facebook.jpg │ │ ├── facebook.svg │ │ ├── favicon.png │ │ ├── flowchart.png │ │ ├── flowchart.svg │ │ ├── logo.svg │ │ ├── mouse.svg │ │ ├── nova.png │ │ ├── responsive.svg │ │ ├── strips.png │ │ ├── textidote.svg │ │ ├── twitter.svg │ │ └── webscope.svg │ └── js │ │ └── script.js └── index.html ├── example.tex ├── mymap.txt └── sonar-project.properties /.github/.agp: -------------------------------------------------------------------------------- 1 | 2 | Auto Github Push (AGP) 3 | https://github.com/ms-jpq/auto-github-push 4 | 5 | --- 6 | 2021-02-01 00:22 7 | -------------------------------------------------------------------------------- /.github/workflows/ant-pull.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Ant 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-ant 3 | 4 | # Same as ant.yml, except that no SonarCloud scan is done since pull requests 5 | # do not have access to repository secrets (and thus generate an error when 6 | # attempting to use Sonar). 7 | 8 | name: Build (pull request) 9 | 10 | on: 11 | pull_request: 12 | branches: [ master ] 13 | 14 | jobs: 15 | build: 16 | name: Compile 17 | runs-on: ubuntu-latest 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | java-version: [ '11', "17", "19" ] 22 | steps: 23 | - uses: actions/checkout@v3 24 | - name: Set up JDK ${{ matrix.java-version }} 25 | uses: actions/setup-java@v3 26 | with: 27 | java-version: ${{ matrix.java-version }} 28 | distribution: 'temurin' 29 | - name: Build with Ant 30 | run: ant -noinput -buildfile build.xml 31 | - name: Test with JUnit 32 | run: ant -noinput -buildfile build.xml test -------------------------------------------------------------------------------- /.github/workflows/ant.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Ant 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-ant 3 | 4 | # Compiles, tests and generates reports using SonarCloud. 5 | # This script is only called on push events (not pull requests). 6 | # There is a separate and slightly different script for pull requets. 7 | 8 | name: Build 9 | 10 | on: 11 | push: 12 | branches: [ master ] 13 | 14 | jobs: 15 | build: 16 | name: Compile 17 | runs-on: ubuntu-latest 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | java-version: [ '11', "17", "19" ] 22 | steps: 23 | - uses: actions/checkout@v3 24 | - name: Set up JDK ${{ matrix.java-version }} 25 | uses: actions/setup-java@v3 26 | with: 27 | java-version: ${{ matrix.java-version }} 28 | distribution: 'temurin' 29 | - name: Build with Ant 30 | run: ant -noinput -buildfile build.xml 31 | - name: Test with JUnit 32 | run: ant -noinput -buildfile build.xml test 33 | - name: Test with JUnit 34 | run: ant -noinput -buildfile build.xml report 35 | - name: SonarCloud Scan 36 | uses: SonarSource/sonarcloud-github-action@master 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any 39 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------- 2 | # Top-level .gitignore rules for an AntRun project 3 | # ---------------------------------------------------- 4 | 5 | # Backup and temp files 6 | *~ 7 | *.tmp 8 | *.swp 9 | *.bak 10 | *.old 11 | \#* 12 | 13 | # MacOS !#@$ files 14 | .DS_Store 15 | 16 | # Auto-generated folders 17 | /docs/javadoc/ 18 | /lib/ 19 | /deps/ 20 | /tests/ 21 | /out/ 22 | 23 | # Top-level jar, but not jars in subfolders (if any) 24 | *.jar 25 | *.zip 26 | !*/*.jar 27 | 28 | # File generated by the JaCoCo code coverage tool 29 | jacoco.exec 30 | 31 | # ---------------------------------------------------- 32 | # Source-level .gitignore rules 33 | # These rules are placed here so that only one .gitignore 34 | # file is required 35 | # ---------------------------------------------------- 36 | 37 | # Eclipse-specific 38 | **/.joblock 39 | **/.metadata/ 40 | **/*.pydevproject 41 | **/.project 42 | **/.metadata 43 | **/*.class 44 | **/bin 45 | **/tmp 46 | **/*~.nib 47 | **/local.properties 48 | **/.classpath 49 | **/.settings/ 50 | **/.recommenders/ 51 | **/.loadpath 52 | 53 | # vscode-specific 54 | /.vscode/ 55 | 56 | # IntelliJ specific 57 | /.idea 58 | *.iml 59 | 60 | # External tool builders 61 | **/.externalToolBuilders/ 62 | 63 | # Locally stored "Eclipse launch configurations" 64 | **/*.launch 65 | 66 | # CDT-specific 67 | **/.cproject 68 | 69 | # PDT-specific 70 | **/.buildpath 71 | /cache/ 72 | 73 | 74 | textidote.sh 75 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | title: "TeXtidote" 3 | message: "If you use this software, please cite it as below." 4 | authors: 5 | - family-names: Hallé 6 | given-names: Sylvain 7 | orcid: "https://orcid.org/0000-0002-4406-6154" 8 | version: 0.9 9 | date-released: "2021-12-18" 10 | license: GPL-3.0 11 | repository-code: "https://github.com/sylvainhalle/textidote" 12 | -------------------------------------------------------------------------------- /Completions/Readme.md: -------------------------------------------------------------------------------- 1 | Shell auto-complete scripts for TeXtidote 2 | ========================================= 3 | 4 | This folder contains shell scripts to allow auto-completion (with the TAB key) 5 | when calling TeXtidote from the command line. 6 | 7 | Bash 8 | ---- 9 | 10 | The Bash script is called `textidote.bash`. When installed using the Debian 11 | package, it is placed in the folder `/etc/bash.completion.d` and renamed 12 | `textidote`. Otherwise, you can download it and put it in the location of your 13 | choice. To enable Bash auto-completion, locate your `.bashrc` file and add the 14 | line: 15 | 16 | source /path/to/textidote.bash 17 | 18 | Replace `/path/to` by the actual path to the location of the file. 19 | 20 | Zsh 21 | --- 22 | 23 | The Zsh script is called `textidote.zsh`. When installed using the Debian 24 | package, it is placed in the folder `/opt/textidote`. Otherwise, you can 25 | download it and put it in the location of your choice. To enable Zsh 26 | auto-completion, locate your `.zshrc` file and add the line: 27 | 28 | source /path/to/textidote.zsh 29 | 30 | Replace `/path/to` by the actual path to the location of the file. 31 | 32 | -------------------------------------------------------------------------------- /Completions/textidote.bash: -------------------------------------------------------------------------------- 1 | # Bash completion script for TeXtidote 2 | # Based on this template: 3 | # https://debian-administration.org/article/317/An_introduction_to_bash_completion_part_2 4 | 5 | beginswith() 6 | { 7 | case $2 in 8 | "$1"*) true;; 9 | *) false;; 10 | esac; 11 | } 12 | 13 | # Found here: https://superuser.com/a/564776 14 | completeFiles() 15 | { 16 | local IFS=$'\n' 17 | local LASTCHAR=' ' 18 | COMPREPLY=($(compgen -o plusdirs -f -- "${COMP_WORDS[COMP_CWORD]}")) 19 | if [ ${#COMPREPLY[@]} = 1 ]; then 20 | [ -d "$COMPREPLY" ] && LASTCHAR=/ 21 | COMPREPLY=$(printf %q%s "$COMPREPLY" "$LASTCHAR") 22 | else 23 | for ((i=0; i < ${#COMPREPLY[@]}; i++)); do 24 | [ -d "${COMPREPLY[$i]}" ] && COMPREPLY[$i]=${COMPREPLY[$i]}/ 25 | done 26 | fi 27 | } 28 | 29 | _textidote() 30 | { 31 | local cur prev opts base 32 | COMPREPLY=() 33 | cur="${COMP_WORDS[COMP_CWORD]}" 34 | prev="${COMP_WORDS[COMP_CWORD-1]}" 35 | 36 | # 37 | # The basic options we'll complete. 38 | # 39 | opts="--check --ci --clean --dict --encoding --firstlang --help --ignore --languagemodel --map --name --no-color --no-config --output --quiet --read-all --remove --remove --replace --single-file --type --version" 40 | 41 | # 42 | # Complete the arguments to some of the basic commands. 43 | # 44 | case "${prev}" in 45 | --check) 46 | local langs="ar de de_AT de_CH de_DE en en_CA en_UK en_US es fr nl pt pl" 47 | COMPREPLY=( $(compgen -W "${langs}" -- ${cur}) ) 48 | return 0 49 | ;; 50 | --dict) 51 | completeFiles 52 | return 0 53 | ;; 54 | --encoding) 55 | local encodings="ASCII cp437 cp1252 UTF8" 56 | COMPREPLY=( $(compgen -W "${encodings}" -- ${cur}) ) 57 | return 0 58 | ;; 59 | --map) 60 | completeFiles 61 | return 0 62 | ;; 63 | --output) 64 | local methods="html json plain singleline" 65 | COMPREPLY=( $(compgen -W "${methods}" -- ${cur}) ) 66 | return 0 67 | ;; 68 | --replace) 69 | completeFiles 70 | return 0 71 | ;; 72 | --type) 73 | local types="tex md" 74 | COMPREPLY=( $(compgen -W "${types}" -- ${cur}) ) 75 | return 0 76 | ;; 77 | *) 78 | ;; 79 | esac 80 | 81 | # If we are writing an option, complete from option list 82 | if beginswith "-" $cur; then 83 | COMPREPLY=($(compgen -W "${opts}" -- ${cur})) 84 | return 0 85 | fi 86 | 87 | # Reply with list of TeX files 88 | completeFiles 89 | return 0 90 | } 91 | 92 | _textidote_zsh() 93 | { 94 | compadd --check --ci --clean --dict --encoding --firstlang --help --ignore --languagemodel --map --name --no-color --no-config --output --quiet --read-all --remove --replace --single-file --type --version 95 | } 96 | 97 | # Register the goto completions. 98 | if [ -n "${BASH_VERSION}" ]; then 99 | if ! [[ $(uname -s) =~ Darwin* ]]; then 100 | complete -o filenames -F _textidote textidote 101 | else 102 | complete -F _textidote textidote 103 | fi 104 | elif [ -n "${ZSH_VERSION}" ]; then 105 | compdef_textidote_zsh textidote 106 | else 107 | echo "Unsupported shell." 108 | exit 1 109 | fi 110 | 111 | # :mode=bash: -------------------------------------------------------------------------------- /Completions/textidote.zsh: -------------------------------------------------------------------------------- 1 | #compdef textidote 2 | # ---------------------------------------------------------------------- 3 | # Zsh completions. To enable in your Zsh shell, add the line 4 | # 5 | # source /opt/textidote/textidote.zsh 6 | # 7 | # in your .zshrc file. 8 | # ---------------------------------------------------------------------- 9 | _textidote_complete() 10 | { 11 | _arguments '--check[Check grammar]:lang:->lang' '--clean[Clear markup]' '--dict[Use dictionary]:filename:_files' '--encoding[Set input encoding]:encoding:->encoding' '--help[Show command line usage]' '--ignore[Ignore rules]' '--languagemodel [Use n-grams data from dir]' '--map[Output correspondence map to file]' '--no-config[Ignore config file if any]' '--output [Output method is]:method:->method' '--map[Produce map file]:filename:_files' '--no-color[No ANSI color]' '--quiet[No messages]' '--read-all[Read all file]' '--remove[Remove LaTeX environments envs]' '--remove-macros[Remove LaTeX macros macs]' '--replace[Apply replacements]:filename:_files' '--type[Input is of type]:type:->type' 12 | case "$state" in 13 | encoding) 14 | _values -s ' ' 'encoding' ASCII cp437 cp1252 UTF8 15 | ;; 16 | lang) 17 | _values -s ' ' 'lang' ar de de_AT de_CH de_DE en en_CA en_UK en_US es fr nl pt pl 18 | ;; 19 | 20 | texfiles) 21 | local -a tex_files 22 | tex_files=(*.tex) 23 | _multi_parts / tex_files 24 | ;; 25 | txtfiles) 26 | local -a dict_files 27 | dict_files=(*.txt) 28 | _multi_parts / dict_files 29 | ;; 30 | type) 31 | _values -s ' ' 'type' tex md 32 | ;; 33 | method) 34 | _values -s ' ' 'method' plain html json singleline 35 | ;; 36 | *) 37 | local -a tex_files 38 | tex_files=(*.tex) 39 | _multi_parts / tex_files 40 | ;; 41 | esac 42 | } 43 | 44 | compdef _textidote_complete textidote 45 | 46 | # :mode=shellscript:wrap=none: -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8 AS build 2 | 3 | # ADD https://archive.apache.org/dist/ant/binaries/apache-ant-1.10.11-bin.tar.gz /root/ant.tar.gz 4 | ADD https://downloads.apache.org/ant/binaries/apache-ant-1.10.15-bin.tar.gz /root/ant.tar.gz 5 | COPY . /build 6 | WORKDIR /build 7 | 8 | #RUN tar -xzvf /root/ant.tar.gz && \ 9 | # mv apache-ant* /ant && \ 10 | # /ant/bin/ant download-deps && \ 11 | # /ant/bin/ant 12 | 13 | 14 | RUN tar -xzvf /root/ant.tar.gz 15 | RUN mv apache-ant* /ant 16 | RUN /ant/bin/ant wipe 17 | RUN /ant/bin/ant download-deps 18 | RUN /ant/bin/ant 19 | 20 | FROM openjdk:8 21 | COPY --from=build /build/textidote-0.9.jar /textidote.jar 22 | ENTRYPOINT ["java", "-jar", "/textidote.jar"] 23 | 24 | -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/AdviceRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018-2019 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote; 19 | 20 | import java.util.HashMap; 21 | import java.util.List; 22 | import java.util.Map; 23 | 24 | import ca.uqac.lif.textidote.as.AnnotatedString; 25 | import ca.uqac.lif.util.AnsiPrinter; 26 | 27 | /** 28 | * Renders a list of advice in a special format 29 | * @author Sylvain Hallé 30 | */ 31 | public abstract class AdviceRenderer 32 | { 33 | /** 34 | * The printer where the renderer will print its results 35 | */ 36 | /*@ non_null @*/ protected AnsiPrinter m_printer; 37 | 38 | /** 39 | * A map between filenames and the list of advice computed for that 40 | * file 41 | */ 42 | /*@ non_null @*/ protected Map> m_advice; 43 | 44 | /** 45 | * A map associating each filename to its (annotated) contents 46 | */ 47 | /*@ non_null @*/ protected Map m_originalStrings; 48 | 49 | /** 50 | * A string containing the language code (if any) used when checking 51 | * the documents. May be the empty string. 52 | */ 53 | /*@ non_null @*/ protected String m_languageCode; 54 | 55 | /** 56 | * Creates a new advice renderer 57 | * @param printer The printer where the renderer will print its 58 | * results 59 | */ 60 | public AdviceRenderer(/*@ non_null @*/ AnsiPrinter printer) 61 | { 62 | this(printer, ""); 63 | } 64 | 65 | /** 66 | * Creates a new advice renderer 67 | * @param printer The printer where the renderer will print its 68 | * results 69 | * @param lang_code An optional language code 70 | */ 71 | public AdviceRenderer(/*@ non_null @*/ AnsiPrinter printer, String lang_code) 72 | { 73 | super(); 74 | m_printer = printer; 75 | m_advice = new HashMap>(); 76 | m_originalStrings = new HashMap(); 77 | m_languageCode = lang_code; 78 | } 79 | 80 | /** 81 | * Associate a list of advice to a filename 82 | * @param filename The filename 83 | * @param contents The contents of the corresponding file 84 | * @param advice The list of advice 85 | */ 86 | public void addAdvice(/*@ non_null @*/ String filename, /*@ non_null @*/ AnnotatedString contents, /*@ non_null @*/ List advice) 87 | { 88 | m_advice.put(filename, advice); 89 | m_originalStrings.put(filename, contents); 90 | } 91 | 92 | /** 93 | * Renders the list of advice for each of the files given to the 94 | * renderer, and prints them to the {@link AnsiPrinter} associated to 95 | * it when it was instantiated. 96 | */ 97 | public abstract void render(); 98 | } 99 | -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/EmptyInputException.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018-2023 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote; 19 | 20 | /** 21 | * Exception thrown by a linter when it is given an empty file to analyze. 22 | * @author Sylvain Hallé 23 | */ 24 | public class EmptyInputException extends LinterException 25 | { 26 | /** 27 | * Dummy UID. 28 | */ 29 | private static final long serialVersionUID = 1L; 30 | 31 | public EmptyInputException(String s) 32 | { 33 | super(s); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/LinterException.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018-2023 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote; 19 | 20 | /** 21 | * Exception thrown by a {@link Linter}. 22 | */ 23 | public class LinterException extends Exception 24 | { 25 | /** 26 | * Dummy UID 27 | */ 28 | private static final long serialVersionUID = 1L; 29 | 30 | public LinterException(Throwable t) 31 | { 32 | super(t); 33 | } 34 | 35 | public LinterException(String s) 36 | { 37 | super(s); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/Rule.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote; 19 | 20 | import java.util.List; 21 | 22 | import ca.uqac.lif.textidote.as.AnnotatedString; 23 | 24 | /** 25 | * Description of a condition that must apply on a piece of text. 26 | * That condition can take multiple forms: a regular expression pattern 27 | * that must/must not be found, etc. 28 | * 29 | * @author Sylvain Hallé 30 | */ 31 | public abstract class Rule 32 | { 33 | /** 34 | * A unique name given to the rule 35 | */ 36 | /*@ non_null @*/ protected String m_name; 37 | 38 | /** 39 | * Evaluates the rule on a string 40 | * @param s The string on which to evaluate the rule 41 | * @return A list of advice generated from the evaluation of the rule 42 | */ 43 | public abstract List evaluate(/*@ non_null @*/ AnnotatedString s); 44 | 45 | /** 46 | * Creates a new rule 47 | * @param name A unique name given to the rule 48 | */ 49 | public Rule(/*@ non_null @*/ String name) 50 | { 51 | super(); 52 | m_name = name; 53 | } 54 | 55 | /** 56 | * Gets the name given to the rule 57 | * @return The name 58 | */ 59 | /*@ pure non_null @*/ public String getName() 60 | { 61 | return m_name; 62 | } 63 | 64 | /** 65 | * Sets the name given to the rule 66 | * @param name The name 67 | */ 68 | public void setName(/*@ non_null @*/ String name) 69 | { 70 | m_name = name; 71 | } 72 | 73 | @Override 74 | /*@ pure non_null @*/ public String toString() 75 | { 76 | return m_name; 77 | } 78 | 79 | /** 80 | * Gets a short textual description of the rule 81 | * @return The description 82 | */ 83 | /*@ pure non_null @*/ public abstract String getDescription(); 84 | } 85 | -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/RuleException.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018-2019 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote; 19 | 20 | /** 21 | * Exception thrown by a {@link Rule}. 22 | */ 23 | public class RuleException extends Exception 24 | { 25 | /** 26 | * Dummy UID 27 | */ 28 | private static final long serialVersionUID = 1L; 29 | 30 | public RuleException(Throwable t) 31 | { 32 | super(t); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/as/Line.java: -------------------------------------------------------------------------------- 1 | package ca.uqac.lif.textidote.as; 2 | 3 | -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/as/Match.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.as; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | /** 24 | * Represents a match by a regular expression find. 25 | * @author Sylvain Hallé 26 | */ 27 | public class Match 28 | { 29 | /** 30 | * The position where the match occurred 31 | */ 32 | private final int m_position; 33 | 34 | /** 35 | * The string that matched the regular expression 36 | */ 37 | private final String m_match; 38 | 39 | /** 40 | * A list with the capture groups for the regex 41 | */ 42 | private List m_groups; 43 | 44 | /** 45 | * Creates a new match object 46 | * @param match The position where the match occurred 47 | * @param pos The string that matched the regular expression 48 | */ 49 | public Match(String match, int pos) 50 | { 51 | super(); 52 | m_match = match; 53 | m_position = pos; 54 | m_groups = new ArrayList(); 55 | } 56 | 57 | /** 58 | * Gets the linear position where the match occurred 59 | * @return The position 60 | */ 61 | public int getPosition() 62 | { 63 | return m_position; 64 | } 65 | 66 | /** 67 | * Gets the string that matched the regular expression 68 | * @return The string 69 | */ 70 | public String getMatch() 71 | { 72 | return m_match; 73 | } 74 | 75 | /** 76 | * Adds the string for a new capture group. Capture groups should 77 | * be added in the order they are named in the corresponding regex. 78 | * @param s The string 79 | */ 80 | public void addGroup(String s) 81 | { 82 | m_groups.add(s); 83 | } 84 | 85 | /** 86 | * Gets the capture group with given index 87 | * group(0) always gives the string matching the whole pattern 88 | * group(1) is the first capture group 89 | * @param index The index of the capture group 90 | * @return The string corresponding to the capture group 91 | */ 92 | public String group(int index) 93 | { 94 | return m_groups.get(index); 95 | } 96 | 97 | /** 98 | * Gets the number of capture groups mentioned in this match 99 | * @return The number of capture groups 100 | */ 101 | public int groupCount() 102 | { 103 | return m_groups.size()-1; 104 | } 105 | 106 | @Override 107 | public String toString() 108 | { 109 | return m_match + " (" + m_position + ")"; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/as/PassthroughFunction.java: -------------------------------------------------------------------------------- 1 | package ca.uqac.lif.textidote.as; 2 | 3 | import ca.uqac.lif.petitpoucet.function.strings.Range; 4 | import ca.uqac.lif.petitpoucet.function.strings.RangeMapping.RangePair; 5 | import ca.uqac.lif.petitpoucet.function.strings.StringMappingFunction; 6 | 7 | public class PassthroughFunction extends StringMappingFunction 8 | { 9 | public PassthroughFunction(StringMappingFunction f, int offset) 10 | { 11 | super(); 12 | m_mapping.add(new Range(0, offset - 1), new Range(0, offset - 1)); 13 | for (RangePair rp : f.getMapping().getPairs()) 14 | { 15 | m_mapping.add(rp.getFrom().shift(offset), rp.getTo().shift(offset)); 16 | } 17 | } 18 | 19 | @Override 20 | protected String transformString(String s) 21 | { 22 | return s; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/as/Position.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018-2021 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.as; 19 | 20 | /** 21 | * A position in the string, expressed in terms of lines and columns. 22 | * Contrary to a linear index, a Position displays lines and 23 | * column indices starting at 1 instead of 0. However they are internally 24 | * stored as 0-based indices, and {@link #getLine()} and 25 | * {@link #getColumn()} return 0-based locations. 26 | */ 27 | public class Position implements Comparable 28 | { 29 | /** 30 | * The position of the first character of the string. 31 | */ 32 | public static final Position ZERO = new Position(0, 0); 33 | 34 | /** 35 | * A fictive position representing no location. 36 | */ 37 | public static final Position NOWHERE = new Position(-1, -1); 38 | 39 | /** 40 | * The line corresponding to the position. 41 | */ 42 | private final int m_line; 43 | 44 | /** 45 | * The column corresponding to the position. 46 | */ 47 | private final int m_column; 48 | 49 | /** 50 | * Creates a new position. 51 | * @param line The line corresponding to the position 52 | * @param column The column corresponding to the position 53 | */ 54 | public Position(int line, int column) 55 | { 56 | super(); 57 | m_line = line; 58 | m_column = column; 59 | } 60 | 61 | /** 62 | * Gets the line coordinate of the position. 63 | * @return The coordinate 64 | */ 65 | /*@ pure @*/ public int getLine() 66 | { 67 | return m_line; 68 | } 69 | 70 | /** 71 | * Gets the column coordinate of the position. 72 | * @return The coordinate 73 | */ 74 | /*@ pure @*/ public int getColumn() 75 | { 76 | return m_column; 77 | } 78 | 79 | @Override 80 | public String toString() 81 | { 82 | return "L" + (m_line + 1) + "C" + (m_column + 1); 83 | } 84 | 85 | @Override 86 | public int hashCode() 87 | { 88 | return m_line * m_column; 89 | } 90 | 91 | @Override 92 | public boolean equals(Object o) 93 | { 94 | if (o == null || !(o instanceof Position)) 95 | { 96 | return false; 97 | } 98 | Position p = (Position) o; 99 | return p.m_column == m_column && p.m_line == m_line; 100 | } 101 | 102 | @Override 103 | public int compareTo(Position p) 104 | { 105 | if (p.m_line > m_line) 106 | { 107 | return -1; 108 | } 109 | if (p.m_line == m_line) 110 | { 111 | if (p.m_column > m_column) 112 | { 113 | return -1; 114 | } 115 | if (p.m_column == m_column) 116 | { 117 | return 0; 118 | } 119 | } 120 | return 1; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/as/PositionRange.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018-2021 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.as; 19 | 20 | /** 21 | * An interval of characters in a string represented by a start and end 22 | * position. 23 | */ 24 | public class PositionRange implements Comparable 25 | { 26 | /** 27 | * The start position of the range. 28 | */ 29 | /*@ non_null @*/ private final Position m_start; 30 | 31 | /** 32 | * The end position of the range. 33 | */ 34 | /*@ non_null @*/ private final Position m_end; 35 | 36 | /** 37 | * Creates a new position range. 38 | * @param start The start position of the range 39 | * @param end The end position of the range 40 | */ 41 | public PositionRange(/*@ non_null @*/ Position start, /*@ non_null @*/ Position end) 42 | { 43 | super(); 44 | m_start = start; 45 | m_end = end; 46 | } 47 | 48 | /** 49 | * Gets the start position of the range. 50 | * @return The position 51 | */ 52 | public Position getStart() 53 | { 54 | return m_start; 55 | } 56 | 57 | /** 58 | * Gets the end position of the range. 59 | * @return The position 60 | */ 61 | public Position getEnd() 62 | { 63 | return m_end; 64 | } 65 | 66 | @Override 67 | public String toString() 68 | { 69 | return m_start + "-" + m_end; 70 | } 71 | 72 | @Override 73 | public int compareTo(PositionRange o) 74 | { 75 | if (m_start.compareTo(o.m_start) < 0) 76 | { 77 | return -1; 78 | } 79 | if (m_start.compareTo(o.m_start) == 0) 80 | { 81 | if (m_end.compareTo(o.m_end) < 0) 82 | { 83 | return -1; 84 | } 85 | if (m_end.equals(o.m_end)) 86 | { 87 | return 0; 88 | } 89 | } 90 | return 1; 91 | } 92 | } 93 | 94 | -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/as/RangeFetcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018-2021 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.as; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | import ca.uqac.lif.dag.Crawler; 24 | import ca.uqac.lif.dag.Node; 25 | import ca.uqac.lif.petitpoucet.Part; 26 | import ca.uqac.lif.petitpoucet.PartNode; 27 | import ca.uqac.lif.petitpoucet.function.strings.Range; 28 | 29 | /** 30 | * A crawler that traverses an explanation graph and fetches all the ranges 31 | * mentioned in leaf nodes. 32 | */ 33 | class RangeFetcher extends Crawler 34 | { 35 | /** 36 | * The ranges found in leaf nodes. 37 | */ 38 | /*@ non_null @*/ protected final List m_ranges; 39 | 40 | /** 41 | * Creates a new range fetcher. 42 | * @param start The starting point of the crawl 43 | */ 44 | public RangeFetcher(/*@ non_null @*/ Node start) 45 | { 46 | super(start); 47 | m_ranges = new ArrayList(); 48 | } 49 | 50 | @Override 51 | public void visit(/*@ non_null @*/ Node n) 52 | { 53 | if (isLeaf(n)) 54 | { 55 | Part p = ((PartNode) n).getPart(); 56 | Range r = Range.mentionedRange(p); 57 | if (r != null) 58 | { 59 | m_ranges.add(r); 60 | } 61 | } 62 | } 63 | 64 | /** 65 | * Gets the list of ranges fetched during the crawl. 66 | * @return The list of ranges 67 | */ 68 | /*@ pure non_null @*/ public List getRanges() 69 | { 70 | return m_ranges; 71 | } 72 | 73 | /** 74 | * Determines if a node is a leaf PartNode. 75 | * @param n The node 76 | * @return true if n is a part node and a leaf, false 77 | * otherwise 78 | */ 79 | protected static boolean isLeaf(/*@ non_null @*/ Node n) 80 | { 81 | if (!(n instanceof PartNode)) 82 | { 83 | return false; 84 | } 85 | for (int i = 0; i < n.getOutputArity(); i++) 86 | { 87 | if (n.getOutputLinks(i).size() > 0) 88 | { 89 | return false; 90 | } 91 | } 92 | return true; 93 | } 94 | } -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/cleaning/CompositeCleaner.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018-2019 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.cleaning; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | import ca.uqac.lif.textidote.as.AnnotatedString; 24 | 25 | /** 26 | * Text cleaner that calls multiple other cleaners in succession. 27 | * @author Sylvain Hallé 28 | */ 29 | public class CompositeCleaner extends TextCleaner 30 | { 31 | /** 32 | * The list of cleaners to call 33 | */ 34 | protected List m_cleaners; 35 | 36 | /** 37 | * Creates a new composite text cleaner from a list of 38 | * other cleaners. 39 | * @param cleaners The list of cleaner to give to this composite cleaner 40 | */ 41 | public CompositeCleaner(TextCleaner ... cleaners) 42 | { 43 | super(); 44 | m_cleaners = new ArrayList(cleaners.length); 45 | for (TextCleaner tc : cleaners) 46 | { 47 | m_cleaners.add(tc); 48 | } 49 | } 50 | 51 | /** 52 | * Creates a new copy of a composite cleaner 53 | * @param c The cleaner to copy 54 | */ 55 | public CompositeCleaner(/*@ non_null @*/ CompositeCleaner c) 56 | { 57 | super(); 58 | m_cleaners = new ArrayList(c.m_cleaners.size()); 59 | m_cleaners.addAll(c.m_cleaners); 60 | } 61 | 62 | @Override 63 | /*@ pure non_null @*/ public AnnotatedString clean(/*@ non_null @*/ AnnotatedString s) throws TextCleanerException 64 | { 65 | for (TextCleaner tc : m_cleaners) 66 | { 67 | s = tc.clean(s); 68 | } 69 | return s; 70 | } 71 | 72 | @Override 73 | /*@ pure non_null @*/ public AnnotatedString cleanComments(/*@ non_null @*/ AnnotatedString s) throws TextCleanerException 74 | { 75 | for (TextCleaner tc : m_cleaners) 76 | { 77 | s = tc.cleanComments(s); 78 | } 79 | return s; 80 | } 81 | 82 | /** 83 | * Adds a new cleaner to the list of cleaners 84 | * @param tc The cleaner to add 85 | * @return This composite cleaner 86 | */ 87 | /*@ non_null @*/ public CompositeCleaner add(/*@ non_null @*/ TextCleaner tc) 88 | { 89 | m_cleaners.add(tc); 90 | return this; 91 | } 92 | 93 | @Override 94 | /*@ pure non_null @*/ public List getInnerFiles() 95 | { 96 | ArrayList files = new ArrayList(); 97 | for (TextCleaner tc : m_cleaners) 98 | { 99 | files.addAll(tc.getInnerFiles()); 100 | } 101 | return files; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/cleaning/ReplacementCleaner.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018-2021 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.cleaning; 19 | 20 | import java.util.ArrayList; 21 | import java.util.HashMap; 22 | import java.util.List; 23 | import java.util.Map; 24 | import java.util.Scanner; 25 | import java.util.regex.PatternSyntaxException; 26 | 27 | import ca.uqac.lif.textidote.as.AnnotatedString; 28 | 29 | /** 30 | * Text cleaner that applies a series of find/replace operations on a 31 | * string. 32 | * @author Sylvain Hallé 33 | */ 34 | public class ReplacementCleaner extends TextCleaner 35 | { 36 | /** 37 | * The map of find/replace patterns 38 | */ 39 | protected Map m_replacements; 40 | 41 | /** 42 | * Creates a new replacement cleaner with a map of replacements 43 | * @param replacements The map of replacements. Keys in the map 44 | * represent the patterns to find, and values represent what they should 45 | * be replaced with. 46 | */ 47 | public ReplacementCleaner(/*@ non_null @*/ Map replacements) 48 | { 49 | super(); 50 | m_replacements = replacements; 51 | } 52 | 53 | /** 54 | * Creates a new replacement cleaner. 55 | */ 56 | public ReplacementCleaner() 57 | { 58 | this(new HashMap()); 59 | } 60 | 61 | @Override 62 | public AnnotatedString clean(/*@ non_null @*/ AnnotatedString s) throws TextCleanerException 63 | { 64 | for (Map.Entry entry : m_replacements.entrySet()) 65 | { 66 | try 67 | { 68 | s = s.replaceAll(entry.getKey(), entry.getValue()); 69 | } 70 | catch (PatternSyntaxException pse) 71 | { 72 | throw new TextCleanerException(pse); 73 | } 74 | } 75 | return s; 76 | } 77 | 78 | @Override 79 | public AnnotatedString cleanComments(AnnotatedString s) 80 | { 81 | // We do nothing 82 | return s; 83 | } 84 | 85 | /** 86 | * Creates a new replacement cleaner from a list of find/replace patterns 87 | * taken from a text source. The format for the text source is as follows: 88 | *
    89 | *
  • Lines that begin with "#" or are made only of whitespace 90 | * are ignored
  • 91 | *
  • Other lines must contain a find pattern, one or more tabs, and a 92 | * replacement pattern
  • 93 | *
94 | * @param scanner A scanner open on a text source 95 | * @return A new replacement cleaner. 96 | */ 97 | public static ReplacementCleaner create(/*@ non_null @*/ Scanner scanner) 98 | { 99 | Map replacements = new HashMap(); 100 | while (scanner.hasNextLine()) 101 | { 102 | String line = scanner.nextLine(); 103 | if (line.trim().startsWith("#") || line.trim().isEmpty()) 104 | { 105 | // Ignore 106 | continue; 107 | } 108 | String[] parts = line.split("\\t+", 2); 109 | if (parts.length != 2) 110 | { 111 | // Malformed line: ignore 112 | continue; 113 | } 114 | replacements.put(parts[0], parts[1]); 115 | } 116 | return new ReplacementCleaner(replacements); 117 | } 118 | 119 | @Override 120 | /*@ pure non_null @*/ public List getInnerFiles() 121 | { 122 | return new ArrayList(0); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/cleaning/TextCleaner.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018-2023 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.cleaning; 19 | 20 | import ca.uqac.lif.textidote.as.AnnotatedString; 21 | 22 | import java.util.List; 23 | 24 | /** 25 | * Removes markup from a text file. A text cleaner can perform two kinds 26 | * of "cleanup" on a text file: 27 | *
    28 | *
  • A full cleanup that removes all markup. Portions of the file that do 29 | * not contain meaningful text (for example, LaTeX equations) may also be 30 | * removed. As a rule, do not consider the output string as a completely 31 | * faithful clear-text rendition of the original document.
  • 32 | *
  • A cleanup that only removes blocks of text identified as comments. 33 | * What a "comment" means depends on the markup language, and some languages 34 | * may not have comments at all.
  • 35 | *
36 | * @author Sylvain Hallé 37 | */ 38 | public abstract class TextCleaner 39 | { 40 | /** 41 | * The string to look for to tell TeXtidote to start ignoring lines 42 | */ 43 | public static final String IGNORE_BEGIN = "textidote: ignore begin"; 44 | 45 | /** 46 | * The string to look for to tell TeXtidote to stop ignoring lines 47 | */ 48 | public static final String IGNORE_END = "textidote: ignore end"; 49 | /** 50 | * Removes markup from a string. 51 | * @param s The original string. Note that this string can be modified 52 | * by the method. 53 | * @return The new string. Whether this object is a copy of {@code s} 54 | * or {@code s} itself is left undefined. 55 | * @throws TextCleanerException If a problem occurs when cleaning 56 | */ 57 | /*@ non_null @*/ public abstract AnnotatedString clean(/*@ non_null @*/ AnnotatedString s) throws TextCleanerException; 58 | 59 | /** 60 | * Removes portions of the string identified as comments, but keeps all 61 | * other markup. 62 | * @param s The original string. Note that this string can be modified 63 | * by the method. 64 | * @return The new string. Whether this object is a copy of {@code s} 65 | * or {@code s} itself is left undefined. 66 | * @throws TextCleanerException If a problem occurs when cleaning 67 | */ 68 | /*@ non_null @*/ public abstract AnnotatedString cleanComments(/*@ non_null @*/ AnnotatedString s) throws TextCleanerException; 69 | 70 | /** 71 | * Returns the list of inner files included in the file to be cleaned. 72 | * Currently, this only has a meaning for cleaners based on LaTeX, 73 | * which has include and input instructions. 74 | * This result will be non-empty only after 75 | * {@link #clean(AnnotatedString) clean()} has been called. 76 | * @return The list of filenames 77 | */ 78 | /*@ non_null @*/ public abstract List getInnerFiles(); 79 | } 80 | -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/cleaning/TextCleanerException.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018-2023 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.cleaning; 19 | 20 | /** 21 | * Exception thrown when cleaning text. 22 | * @author Sylvain Hallé 23 | */ 24 | public class TextCleanerException extends Exception 25 | { 26 | /** 27 | * Dummy UID 28 | */ 29 | private static final long serialVersionUID = 1L; 30 | 31 | public TextCleanerException(Throwable t) 32 | { 33 | super(t); 34 | } 35 | 36 | public TextCleanerException(String s) 37 | { 38 | super(s); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/cleaning/markdown/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | /** 5 | * @author sylvain 6 | * 7 | */ 8 | package ca.uqac.lif.textidote.cleaning.markdown; -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | /** 5 | * @author sylvain 6 | * 7 | */ 8 | package ca.uqac.lif.textidote; -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/render/AnsiAdviceRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018-2019 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.render; 19 | 20 | import java.util.List; 21 | import java.util.Map; 22 | 23 | import ca.uqac.lif.petitpoucet.function.strings.Range; 24 | import ca.uqac.lif.textidote.Advice; 25 | import ca.uqac.lif.textidote.AdviceRenderer; 26 | import ca.uqac.lif.textidote.as.AnnotatedString.Line; 27 | import ca.uqac.lif.textidote.as.PositionRange; 28 | import ca.uqac.lif.util.AnsiPrinter; 29 | import ca.uqac.lif.util.AnsiPrinter.Color; 30 | 31 | /** 32 | * Renders a list of advice to a terminal (such as {@code stdin}), using 33 | * colored output. 34 | * @author Sylvain Hallé 35 | */ 36 | public class AnsiAdviceRenderer extends AdviceRenderer 37 | { 38 | /** 39 | * The number of characters to display when showing an excerpt 40 | * from the file 41 | */ 42 | protected int m_lineWidth = 50; 43 | 44 | /** 45 | * The width of the line in the terminal (in number of characters) 46 | */ 47 | protected int m_terminalLineWidth = 78; 48 | 49 | /** 50 | * Creates a new advice renderer 51 | * @param printer The printer to which the advice will be printed 52 | */ 53 | public AnsiAdviceRenderer(AnsiPrinter printer) 54 | { 55 | super(printer); 56 | } 57 | 58 | @Override 59 | public void render() 60 | { 61 | boolean map_single = m_advice.size() <= 1; 62 | for (Map.Entry> entry : m_advice.entrySet()) 63 | { 64 | String filename = entry.getKey(); 65 | List list = entry.getValue(); 66 | if (!map_single) 67 | { 68 | m_printer.println(filename); 69 | m_printer.println(); 70 | } 71 | if (list.isEmpty()) 72 | { 73 | if (!map_single) 74 | { 75 | m_printer.print("* "); 76 | } 77 | m_printer.println("Everything is OK!"); 78 | } 79 | else 80 | { 81 | for (Advice ad : list) 82 | { 83 | PositionRange pr = ad.getPositionRange(); 84 | m_printer.setForegroundColor(Color.YELLOW); 85 | m_printer.print("* " + pr); 86 | m_printer.resetColors(); 87 | m_printer.print(" "); 88 | wrap(ad.getMessage() + " [" + ad.getRule().getName() + "]", " ", pr.toString().length() + 2); 89 | m_printer.println(); 90 | m_printer.setForegroundColor(Color.WHITE); 91 | renderExcerpt(ad, ad.getLine(), ad.getRange()); 92 | } 93 | } 94 | } 95 | } 96 | 97 | /** 98 | * Renders a line of text and "highlights" a portion of it. The highlight 99 | * here is simulated with a series of "^" characters, like this: 100 | *
101 | 	 * the quick brown fox jumps over the lazy dog
102 | 	 *     ^^^^^^^^^^^^^^^
103 | 	 * 
104 | * @param ad Advice to render 105 | * @param l The line of text 106 | * @param range The range to highlight 107 | */ 108 | protected void renderExcerpt(/*@ non_null @*/ Advice ad, /*@ non_null @*/ Line l, /*@ non_null @*/ Range range) 109 | { 110 | String line = l.toString(); 111 | int indent = 2; 112 | int left = ad.getReferenceString().getOriginalPosition(range.getStart()).getColumn(); 113 | int right = ad.getReferenceString().getOriginalPosition(range.getEnd()).getColumn(); 114 | int range_width = right - left; 115 | int mid_point = left + range_width / 2; 116 | int offset = 0; 117 | if (range_width < line.length()) 118 | { 119 | if (mid_point + m_lineWidth / 2 >= line.length()) 120 | { 121 | int char_dif = (mid_point + m_lineWidth / 2) - line.length(); 122 | offset = Math.max(0, (mid_point - m_lineWidth / 2) - char_dif); 123 | } 124 | else 125 | { 126 | offset = Math.max(0, mid_point - m_lineWidth / 2); 127 | } 128 | } 129 | String line_to_display = line.substring(offset, Math.min(line.length(), offset + m_lineWidth)); 130 | printSpaces(indent); 131 | m_printer.println(line_to_display); 132 | // Show squiggly line 133 | printSpaces(indent + Math.max(0, left - offset)); 134 | m_printer.setForegroundColor(Color.LIGHT_RED); 135 | for (int i = 0; i < range_width + 1; i++) 136 | { 137 | m_printer.append("^"); 138 | } 139 | m_printer.resetColors(); 140 | m_printer.println(); 141 | } 142 | 143 | /** 144 | * Prints some spaces 145 | * @param n The number of spaces to print 146 | */ 147 | protected void printSpaces(int n) 148 | { 149 | for (int i = 0; i < n; i++) 150 | { 151 | m_printer.print(" "); 152 | } 153 | } 154 | 155 | /** 156 | * Prints a sequence of words, using word wrapping and indenting each 157 | * new line 158 | * @param message The sequence of words to print 159 | * @param indent The indent (a sequence of spaces) to append at the 160 | * beginning of each new line 161 | * @param start_pos The start position on the first line (set to greater 162 | * than 0 to append text to an existing line) 163 | */ 164 | protected void wrap(/*@ non_null @*/ String message, /*@ non_null @*/ String indent, int start_pos) 165 | { 166 | int cur_width = start_pos; 167 | String[] words = message.split(" "); 168 | for (String word : words) 169 | { 170 | cur_width += word.length() + 1; 171 | if (cur_width > m_terminalLineWidth) 172 | { 173 | m_printer.println(); 174 | m_printer.print(indent); 175 | cur_width = word.length() + 1; 176 | } 177 | if (word.startsWith("[") && word.endsWith("]")) 178 | { 179 | m_printer.setForegroundColor(Color.BROWN); 180 | m_printer.print(word + " "); 181 | m_printer.resetColors(); 182 | } 183 | else 184 | { 185 | // Language Tool advice has this strange markup 186 | word = word.replaceAll("", "'"); 187 | word = word.replaceAll("", "'"); 188 | m_printer.print(word + " "); 189 | } 190 | } 191 | } 192 | } -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/render/SinglelineAdviceRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018-2019 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.render; 19 | 20 | import java.util.List; 21 | import java.util.Map; 22 | 23 | import ca.uqac.lif.petitpoucet.function.strings.Range; 24 | import ca.uqac.lif.textidote.Advice; 25 | import ca.uqac.lif.textidote.AdviceRenderer; 26 | import ca.uqac.lif.textidote.as.AnnotatedString; 27 | import ca.uqac.lif.textidote.as.AnnotatedString.Line; 28 | import ca.uqac.lif.textidote.as.Position; 29 | import ca.uqac.lif.util.AnsiPrinter; 30 | import ca.uqac.lif.util.AnsiPrinter.Color; 31 | 32 | /** 33 | * Renders advice to a terminal (such as {@code stdin}), printing a single line 34 | * per advice, using colored output. 35 | * 36 | * @author toolcreator 37 | */ 38 | public class SinglelineAdviceRenderer extends AdviceRenderer 39 | { 40 | /** 41 | * Creates a new advice renderer 42 | * 43 | * @param printer 44 | * The printer to which the advice will be printed 45 | */ 46 | public SinglelineAdviceRenderer(AnsiPrinter printer) 47 | { 48 | super(printer); 49 | } 50 | 51 | @Override 52 | public void render() 53 | { 54 | for (Map.Entry> entry : m_advice.entrySet()) 55 | { 56 | String filename = entry.getKey(); 57 | List list = entry.getValue(); 58 | if (!list.isEmpty()) 59 | { 60 | for (Advice ad : list) 61 | { 62 | m_printer.setForegroundColor(Color.YELLOW); 63 | m_printer.print(filename + "(" + ad.getPositionRange() + ")"); 64 | m_printer.resetColors(); 65 | m_printer.print(": "); 66 | m_printer.print( 67 | ad.getMessage().replaceAll("", "").replaceAll("= line.length()) 98 | { 99 | m_printer.print(line.substring(start.getColumn(), line.length())); 100 | } 101 | else 102 | { 103 | m_printer.print(line.substring(start.getColumn(), end.getColumn() + 1)); 104 | m_printer.setForegroundColor(Color.WHITE); 105 | m_printer.print(line.substring(end.getColumn() + 1, line.length())); 106 | } 107 | } 108 | else 109 | { 110 | m_printer.print(line); 111 | } 112 | m_printer.resetColors(); 113 | m_printer.print("\""); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/render/postamble.html: -------------------------------------------------------------------------------- 1 |
2 | Output produced by TeXtidote v0.9 beta, © 2018-2023 Sylvain Hallé - All rights reserved.
3 | See the TeXtidote website for more information. 4 | 5 | 6 | -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/rules/CheckCaptions.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018-2019 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.rules; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | import ca.uqac.lif.petitpoucet.function.strings.Range; 24 | import ca.uqac.lif.textidote.Advice; 25 | import ca.uqac.lif.textidote.Rule; 26 | import ca.uqac.lif.textidote.as.AnnotatedString; 27 | import ca.uqac.lif.textidote.as.AnnotatedString.Line; 28 | import ca.uqac.lif.textidote.as.Position; 29 | 30 | /** 31 | * Checks that captions end with a period. This rule does not evaluate a 32 | * regular expression, as the nesting of commands within the caption creates 33 | * lots of false positives. Rather, it finds an occurrence of 34 | * \caption, and then keeps track of the nesting level of opening 35 | * and closing braces. 36 | * 37 | * @author Sylvain Hallé 38 | * 39 | */ 40 | public class CheckCaptions extends Rule 41 | { 42 | public CheckCaptions() 43 | { 44 | super("sh:capperiod"); 45 | } 46 | 47 | @Override 48 | public List evaluate(AnnotatedString s) 49 | { 50 | List out_list = new ArrayList(); 51 | List lines = s.getLines(); 52 | for (int line_cnt = 0; line_cnt < lines.size(); line_cnt++) 53 | { 54 | Line l = lines.get(line_cnt); 55 | String line = l.toString(); 56 | int start_pos = line.indexOf("\\caption"); 57 | if (start_pos < 0) 58 | { 59 | continue; 60 | } 61 | if (line.substring(start_pos).matches("\\\\captionsetup.*")) 62 | { 63 | // We consider caption, but we ignore captionsetup 64 | continue; 65 | } 66 | boolean period_seen = false; 67 | int level = 0; 68 | for (int i = start_pos + 1; i < line.length(); i++) 69 | { 70 | String c = line.substring(i, i + 1); 71 | if (c.compareTo("{") == 0) 72 | { 73 | level++; 74 | period_seen = false; 75 | } 76 | else if (c.compareTo("}") == 0) 77 | { 78 | level--; 79 | if (level == 0 && !period_seen) 80 | { 81 | int start_p = s.findOriginalIndex(new Position(line_cnt, start_pos)); 82 | int end_p = s.findOriginalIndex(new Position(line_cnt, i)); 83 | Range r = new Range(start_p, end_p); 84 | out_list.add(new Advice(this, r, "A caption should end with a period", s, s.findOriginalLine(line_cnt))); 85 | break; 86 | } 87 | period_seen = false; 88 | } 89 | else if (c.compareTo(".") == 0) 90 | { 91 | period_seen = true; 92 | } 93 | else if (c.compareTo(" ") != 0) 94 | { 95 | period_seen = false; 96 | } 97 | } 98 | } 99 | return out_list; 100 | } 101 | 102 | @Override 103 | public String getDescription() 104 | { 105 | return "Period at the end of table and figure captions"; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/rules/CheckCiteMix.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.rules; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | import ca.uqac.lif.petitpoucet.function.strings.Range; 24 | import ca.uqac.lif.textidote.Advice; 25 | import ca.uqac.lif.textidote.Rule; 26 | import ca.uqac.lif.textidote.as.AnnotatedString; 27 | import ca.uqac.lif.textidote.as.Match; 28 | 29 | /** 30 | * Checks that a document does not mix occurrences of \cite 31 | * and \citep/\citet. * 32 | * @author Sylvain Hallé 33 | * 34 | */ 35 | public class CheckCiteMix extends Rule 36 | { 37 | public CheckCiteMix() 38 | { 39 | super("sh:c:itemix"); 40 | } 41 | 42 | @Override 43 | public List evaluate(AnnotatedString s) 44 | { 45 | List out_list = new ArrayList(); 46 | Match m1 = s.find("\\\\cite(p|t)"); 47 | Match m2 = s.find("\\\\cite[^pt]"); 48 | if (m1 != null && m2 != null) 49 | { 50 | Range r = s.findOriginalRange(new Range(m1.getPosition(), m1.getPosition() + m1.getMatch().length())); 51 | out_list.add(new Advice(this, r, "Do not mix \\cite with \\citep or \\citet in the same document.", s, s.findOriginalLineOf(m1.getPosition()))); 52 | } 53 | return out_list; 54 | } 55 | 56 | @Override 57 | public String getDescription() 58 | { 59 | return "No mix of citation styles"; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/rules/CheckFigurePaths.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.rules; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | import java.util.regex.Matcher; 23 | import java.util.regex.Pattern; 24 | 25 | import ca.uqac.lif.petitpoucet.function.strings.Range; 26 | import ca.uqac.lif.textidote.Advice; 27 | import ca.uqac.lif.textidote.Rule; 28 | import ca.uqac.lif.textidote.as.AnnotatedString; 29 | import ca.uqac.lif.textidote.as.Position; 30 | import ca.uqac.lif.textidote.as.AnnotatedString.Line; 31 | 32 | /** 33 | * Checks that included figures are not referenced by an absolute local path. 34 | *

35 | * For the means of this rule, an absolute path is anything of the form 36 | *

    37 | *
  • X:... (Windows drive letter)
  • 38 | *
  • /... (Unix absolute path)
  • 39 | *
  • anything that contains .. (moving up in the folder 40 | * structure is not recommended)
  • 41 | *
42 | * Formally, this rule checks that no such path appears in an 43 | * \includegraphics command. 44 | * 45 | * @author Sylvain Hallé 46 | * 47 | */ 48 | public class CheckFigurePaths extends Rule 49 | { 50 | /** 51 | * The pattern for finding figure labels 52 | */ 53 | Pattern m_figurePattern = Pattern.compile("\\\\includegraphics\\s*(\\[.*?\\])*?\\{(.*?)\\}"); 54 | 55 | public CheckFigurePaths() 56 | { 57 | super("sh:relpath"); 58 | } 59 | 60 | @Override 61 | public List evaluate(AnnotatedString s) 62 | { 63 | List out_list = new ArrayList(); 64 | List lines = s.getLines(); 65 | for (int line_cnt = 0; line_cnt < lines.size(); line_cnt++) 66 | { 67 | Line l = lines.get(line_cnt); 68 | String line = l.toString(); 69 | Matcher mat = m_figurePattern.matcher(line); 70 | if (mat.find()) 71 | { 72 | String path = mat.group(2).trim(); 73 | if (isAbsolute(path)) 74 | { 75 | // Absolute path 76 | int start_pos = mat.start(2); 77 | int end_pos = mat.start(2) + mat.group(2).length() - 1; 78 | int start_p = s.findOriginalIndex(new Position(line_cnt, start_pos)); 79 | int end_p = s.findOriginalIndex(new Position(line_cnt, end_pos)); 80 | Range r = new Range(start_p, end_p); 81 | out_list.add(new Advice(this, r, "Do not use an absolute path for a figure", s, l)); 82 | } 83 | } 84 | } 85 | return out_list; 86 | } 87 | 88 | /** 89 | * Checks if string represents an absolute path (or at least a path 90 | * outside the current folder) 91 | * @param path The path 92 | * @return {@code true} if the path is absolute 93 | */ 94 | protected static boolean isAbsolute(/*@ non_null @*/ String path) 95 | { 96 | return path.matches("[A-Za-z]\\:.*") || path.matches("/.*") || path.contains(".."); 97 | } 98 | 99 | @Override 100 | public String getDescription() 101 | { 102 | return "Absolute paths in figures"; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/rules/CheckFigureReferences.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.rules; 19 | 20 | import java.util.ArrayList; 21 | import java.util.HashMap; 22 | import java.util.List; 23 | import java.util.Map; 24 | import java.util.regex.Matcher; 25 | import java.util.regex.Pattern; 26 | 27 | import ca.uqac.lif.petitpoucet.function.strings.Range; 28 | import ca.uqac.lif.textidote.Advice; 29 | import ca.uqac.lif.textidote.Rule; 30 | import ca.uqac.lif.textidote.as.AnnotatedString; 31 | import ca.uqac.lif.textidote.as.AnnotatedString.Line; 32 | 33 | /** 34 | * Checks that every figure with a label is mentioned in the text 35 | * at least once. 36 | *

37 | * Formally, this rule checks that for every occurrence of \label{X} 38 | * within a figure environment (that is not commented out), there 39 | * exists a \ref{X} somewhere in the text (that is not commented 40 | * out). 41 | * 42 | * @author Sylvain Hallé 43 | * 44 | */ 45 | public class CheckFigureReferences extends Rule 46 | { 47 | /** 48 | * The pattern for finding figure labels 49 | */ 50 | Pattern m_figurePattern = Pattern.compile("\\\\label\\s*\\{(.*?)\\}"); 51 | 52 | public CheckFigureReferences() 53 | { 54 | super("sh:figref"); 55 | } 56 | 57 | @Override 58 | public List evaluate(AnnotatedString s) 59 | { 60 | List out_list = new ArrayList(); 61 | boolean in_figure = false; 62 | Map figure_defs = new HashMap(); 63 | List lines = s.getLines(); 64 | boolean found_label = false; 65 | // Step 1: find all figure labels 66 | for (int line_cnt = 0; line_cnt < lines.size(); line_cnt++) 67 | { 68 | Line l = lines.get(line_cnt); 69 | String line = l.toString(); 70 | if (line.matches(".*\\\\begin\\s*\\{\\s*(figure|wrapfigure).*")) 71 | { 72 | in_figure = true; 73 | found_label = false; 74 | continue; 75 | } 76 | if (line.matches(".*\\\\end\\s*\\{\\s*(figure|wrapfigure).*")) 77 | { 78 | in_figure = false; 79 | if (!found_label) 80 | { 81 | // This figure is missing a label 82 | int start_pos = l.getOffset(); 83 | int end_pos = start_pos + line.length(); 84 | Range r = new Range(start_pos, end_pos); 85 | out_list.add(new Advice(this, r, "This figure is missing a label", s, l)); 86 | } 87 | continue; 88 | } 89 | if (in_figure) 90 | { 91 | Matcher mat = m_figurePattern.matcher(line); 92 | if (mat.find()) 93 | { 94 | String fig_name = mat.group(1).trim(); 95 | int fig_pos = l.getOffset() + mat.start(1); 96 | figure_defs.put(fig_name, fig_pos); 97 | found_label = true; 98 | } 99 | } 100 | } 101 | // Step 2: find references to these figures 102 | for (String fig_name : figure_defs.keySet()) 103 | { 104 | if (s.find("\\\\(C|c|auto){0,1}ref\\s*\\{.*" + fig_name + ".*?\\}") == null) 105 | { 106 | // This figure is not referenced 107 | int start_pos = figure_defs.get(fig_name); 108 | int end_pos = start_pos + fig_name.length() - 1; 109 | Range r = s.findOriginalRange(start_pos, end_pos); 110 | Line original_line = s.getOriginalLineOf(start_pos); 111 | out_list.add(new Advice(this, r, "Figure " + fig_name + " is never referenced in the text", s, original_line)); 112 | } 113 | } 114 | return out_list; 115 | } 116 | 117 | @Override 118 | public String getDescription() 119 | { 120 | return "Every figure must be referenced"; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/rules/CheckNoBreak.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.rules; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | import java.util.regex.Matcher; 23 | import java.util.regex.Pattern; 24 | 25 | import ca.uqac.lif.petitpoucet.function.strings.Range; 26 | import ca.uqac.lif.textidote.Advice; 27 | import ca.uqac.lif.textidote.Rule; 28 | import ca.uqac.lif.textidote.as.AnnotatedString; 29 | import ca.uqac.lif.textidote.as.AnnotatedString.Line; 30 | 31 | /** 32 | * Checks that text paragraphs do not contain forced line breaks. 33 | * 34 | * @author Sylvain Hallé 35 | * 36 | */ 37 | public class CheckNoBreak extends Rule 38 | { 39 | /** 40 | * The pattern for finding figure labels 41 | */ 42 | Pattern m_breakPattern = Pattern.compile("\\\\\\\\"); 43 | 44 | public CheckNoBreak() 45 | { 46 | super("sh:nobreak"); 47 | } 48 | 49 | @Override 50 | public List evaluate(AnnotatedString s) 51 | { 52 | List out_list = new ArrayList(); 53 | List lines = s.getLines(); 54 | int env_level = 0; 55 | for (int line_cnt = 0; line_cnt < lines.size(); line_cnt++) 56 | { 57 | Line l = lines.get(line_cnt); 58 | String line = l.toString(); 59 | if (line.matches(".*\\\\begin\\s*\\{\\s*(equation|equation\\*|align|align\\*|table|tabular|verbatim|lstlisting|IEEEkeywords|figure|matrix|bmatrix|Bmatrix|pmatrix|vmatrix|Vmatrix|smallmatrix).*") || line.matches(".*\\\\\\[.*")) 60 | { 61 | env_level++; 62 | } 63 | if (env_level == 0) 64 | { 65 | Matcher mat = m_breakPattern.matcher(line); 66 | if (mat.find()) 67 | { 68 | // Forced break 69 | int start_pos = l.getOffset() + mat.start(); 70 | int end_pos = l.getOffset() + mat.start() + mat.group(0).length(); 71 | Range r = s.findOriginalRange(new Range(start_pos, end_pos - 1)); 72 | out_list.add(new Advice(this, r, "You should not break lines manually in a paragraph. Either start a new paragraph or stay in the current one.", s, l)); 73 | } 74 | } 75 | if (line.matches(".*\\\\end\\s*\\{\\s*(equation|equation\\*|align|align\\*|table|tabular|verbatim|lstlisting|IEEEkeywords|figure|matrix|bmatrix|Bmatrix|pmatrix|vmatrix|Vmatrix|smallmatrix).*") || line.matches(".*\\\\\\].*")) 76 | { 77 | env_level--; 78 | } 79 | } 80 | return out_list; 81 | } 82 | 83 | @Override 84 | public String getDescription() 85 | { 86 | return "Don't use manual line breaks in text"; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/rules/CheckStackedHeadings.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.rules; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | import java.util.regex.Matcher; 23 | import java.util.regex.Pattern; 24 | 25 | import ca.uqac.lif.petitpoucet.function.strings.Range; 26 | import ca.uqac.lif.textidote.Advice; 27 | import ca.uqac.lif.textidote.Rule; 28 | import ca.uqac.lif.textidote.as.AnnotatedString; 29 | import ca.uqac.lif.textidote.as.AnnotatedString.Line; 30 | 31 | /** 32 | * Checks that stacked headings are not present. 33 | * @author Sylvain Hallé 34 | */ 35 | public class CheckStackedHeadings extends Rule 36 | { 37 | /** 38 | * The pattern to detect a new section heading. 39 | */ 40 | protected Pattern m_headingPattern = Pattern.compile("\\\\(part|chapter|section|subsection|subsubsection|paragraph)\\s*\\{"); 41 | 42 | /** 43 | * Creates a new instance of the rule 44 | */ 45 | public CheckStackedHeadings() 46 | { 47 | super("sh:nsubdiv"); 48 | } 49 | 50 | @Override 51 | public List evaluate(AnnotatedString s) 52 | { 53 | List out_list = new ArrayList(); 54 | List lines = s.getLines(); 55 | boolean found_text = true; 56 | for (int line_cnt = 0; line_cnt < lines.size(); line_cnt++) 57 | { 58 | Line l = lines.get(line_cnt); 59 | String line = l.toString(); 60 | Matcher mat = m_headingPattern.matcher(line); 61 | if (mat.find()) 62 | { 63 | if (!found_text) 64 | { 65 | int start_pos = l.getOffset() + mat.start(1); 66 | int end_pos = l.getOffset() + mat.start(1) + mat.group(1).length(); 67 | Range r = s.findOriginalRange(start_pos, end_pos); 68 | out_list.add(new Advice(CheckStackedHeadingsAdvice.instance, r, "Avoid stacked headings, i.e. consecutive headings without text in between.", s, l)); 69 | } 70 | found_text = false; 71 | } 72 | else if (!line.trim().isEmpty()) 73 | { 74 | found_text = true; 75 | } 76 | } 77 | return out_list; 78 | } 79 | 80 | /** 81 | * A placeholder class for a sub-rule checked by {@link CheckStackedHeadings}. 82 | * This class exists only to have a different rule ID. 83 | */ 84 | public static class CheckStackedHeadingsAdvice extends Rule 85 | { 86 | /** 87 | * Reference to a single instance of this class 88 | */ 89 | protected static final CheckStackedHeadingsAdvice instance = new CheckStackedHeadingsAdvice(); 90 | 91 | private CheckStackedHeadingsAdvice() 92 | { 93 | super("sh:stacked"); 94 | } 95 | 96 | @Override 97 | public List evaluate(AnnotatedString s) 98 | { 99 | // Do nothing; this is a placeholder 100 | return new ArrayList(0); 101 | } 102 | 103 | @Override 104 | public String getDescription() 105 | { 106 | return "Stacked headings"; 107 | } 108 | } 109 | 110 | @Override 111 | public String getDescription() 112 | { 113 | return "Stacked headings"; 114 | } 115 | } -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/rules/CheckSubsectionSize.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.rules; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | import java.util.Stack; 23 | import java.util.regex.Matcher; 24 | import java.util.regex.Pattern; 25 | 26 | import ca.uqac.lif.petitpoucet.function.strings.Range; 27 | import ca.uqac.lif.textidote.Advice; 28 | import ca.uqac.lif.textidote.Rule; 29 | import ca.uqac.lif.textidote.as.AnnotatedString; 30 | import ca.uqac.lif.textidote.as.AnnotatedString.Line; 31 | 32 | /** 33 | * Checks that each sub-division in the text (section, sub-section, etc.) has 34 | * a minimum number of words. 35 | * @author Sylvain Hallé 36 | */ 37 | public class CheckSubsectionSize extends Rule 38 | { 39 | /** 40 | * The pattern to detect a new section heading. 41 | */ 42 | protected Pattern m_headingPattern = Pattern.compile("\\\\(chapter|section|subsection|subsubsection)\\s*\\{"); 43 | 44 | /** 45 | * The minimum number of words in a subdivision 46 | */ 47 | protected int m_minNumWords = 150; 48 | 49 | /** 50 | * Ignore this rule for sections named "Conclusion" 51 | */ 52 | protected boolean m_ignoreConclusion = true; 53 | 54 | /** 55 | * Creates a new instance of the rule 56 | */ 57 | public CheckSubsectionSize() 58 | { 59 | super("sh:seclen"); 60 | } 61 | 62 | /** 63 | * Sets the minimum number of words a section requires to avoid a 64 | * warning. 65 | * @param n The minimum number of words 66 | */ 67 | /*@ requires n > 0 @*/ 68 | public void setMinNumWords(int n) 69 | { 70 | m_minNumWords = n; 71 | } 72 | 73 | @Override 74 | public List evaluate(AnnotatedString s) 75 | { 76 | Stack sections = new Stack(); 77 | SectionInfo doc_head = new SectionInfo("", new Range(0, 0)); 78 | List out_list = new ArrayList(); 79 | List lines = s.getLines(); 80 | sections.push(doc_head); 81 | for (int line_cnt = 0; line_cnt < lines.size(); line_cnt++) 82 | { 83 | Line l = lines.get(line_cnt); 84 | String line = l.toString(); 85 | Matcher mat = m_headingPattern.matcher(line); 86 | if (mat.find()) 87 | { 88 | int start_pos = l.getOffset() + mat.start(1); 89 | int end_pos = l.getOffset() + mat.start(1) + mat.group(1).length(); 90 | String heading = mat.group(1).trim(); 91 | SectionInfo si = new SectionInfo(heading, new Range(start_pos, end_pos - 1)); 92 | SectionInfo si_last = sections.peek(); 93 | if (SectionInfo.isMoveDown(si_last, si)) 94 | { 95 | si_last.m_size++; 96 | sections.push(si); 97 | } 98 | else 99 | { 100 | // Move up or same level 101 | if (si_last.m_sectionName.compareTo(heading) == 0) 102 | { 103 | // Same heading 104 | SectionInfo si_sibling = sections.pop(); 105 | SectionInfo si_parent = sections.peek(); 106 | si_parent.m_size += si_sibling.m_size; 107 | sections.push(si); 108 | } 109 | else 110 | { 111 | // Move up 112 | while (!sections.isEmpty() && si_last.m_sectionName.compareTo(si.m_sectionName) != 0) 113 | { 114 | si_last = sections.pop(); 115 | if (si_last.m_size < m_minNumWords && !si_last.m_sectionName.isEmpty()) 116 | { 117 | Range r2 = s.findOriginalRange(si_last.m_range); 118 | out_list.add(new Advice(this, r2, "This " + si_last.m_sectionName + " is very short (about " + si_last.m_size + " words). You should consider merging it with another section or make it longer.", s, s.findOriginalLine(s.getPosition(si_last.m_range.getStart()).getLine()))); 119 | } 120 | } 121 | if (sections.isEmpty()) 122 | { 123 | sections.push(doc_head); 124 | } 125 | else 126 | { 127 | SectionInfo si_parent = sections.peek(); 128 | si_parent.m_size++; 129 | sections.push(si); 130 | } 131 | sections.push(si); 132 | } 133 | } 134 | } 135 | else 136 | { 137 | // Just increment number of words 138 | int num_words = countWords(line); 139 | SectionInfo si_last = sections.peek(); 140 | si_last.m_size += num_words; 141 | } 142 | } 143 | // End 144 | while (!sections.isEmpty()) 145 | { 146 | SectionInfo si_last = sections.pop(); 147 | if (!si_last.m_sectionName.isEmpty() && si_last.m_size < m_minNumWords) 148 | { 149 | Range r2 = s.findOriginalRange(si_last.m_range); 150 | out_list.add(new Advice(this, r2, "This section is very short (about " + si_last.m_size + " words). You should consider merging it with another section or make it longer.", s, s.findOriginalLine(s.getPosition(si_last.m_range.getStart()).getLine()))); 151 | } 152 | } 153 | return out_list; 154 | } 155 | 156 | /** 157 | * Counts the words in a line. This performs a very crude estimation of 158 | * the number of words, by simply counting the number of character 159 | * blobs that are separated by spaces. That's good enough for the "size" 160 | * rule we are evaluating here. 161 | * @param line The line to count words in 162 | * @return The number of words 163 | */ 164 | protected static int countWords(/*@ non_null @*/ String line) 165 | { 166 | return line.split("\\s+").length; 167 | } 168 | 169 | @Override 170 | public String getDescription() 171 | { 172 | return "Short sub-sections"; 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/rules/LanguageFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018-2021 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.rules; 19 | 20 | import org.languagetool.Language; 21 | import org.languagetool.language.AmericanEnglish; 22 | import org.languagetool.language.Arabic; 23 | import org.languagetool.language.AustrianGerman; 24 | import org.languagetool.language.BritishEnglish; 25 | import org.languagetool.language.CanadianEnglish; 26 | import org.languagetool.language.Dutch; 27 | import org.languagetool.language.French; 28 | import org.languagetool.language.GermanyGerman; 29 | import org.languagetool.language.Portuguese; 30 | import org.languagetool.language.BrazilianPortuguese; 31 | import org.languagetool.language.Spanish; 32 | import org.languagetool.language.Polish; 33 | import org.languagetool.language.SwissGerman; 34 | 35 | /** 36 | * Factory class whose sole purpose is to provide instances of {@code Language} 37 | * objects. 38 | * @author Sylvain Hallé 39 | */ 40 | public class LanguageFactory 41 | { 42 | /** 43 | * Instantiates a Language object based on a string 44 | * @param s The string 45 | * @return A Language object, or {@code null} if no language could be 46 | * instantiated from the string 47 | */ 48 | /*@ nullable @*/ public static Language getLanguageFromString(String s) 49 | { 50 | if (s.compareToIgnoreCase("en") == 0 || s.compareToIgnoreCase("en_US") == 0) 51 | { 52 | return new AmericanEnglish(); 53 | } 54 | if (s.compareToIgnoreCase("ar") == 0) 55 | { 56 | return new Arabic(); 57 | } 58 | if (s.compareToIgnoreCase("en_CA") == 0) 59 | { 60 | return new CanadianEnglish(); 61 | } 62 | if (s.compareToIgnoreCase("en_UK") == 0) 63 | { 64 | return new BritishEnglish(); 65 | } 66 | if (s.compareToIgnoreCase("pl") == 0) 67 | { 68 | return new Polish(); 69 | } 70 | if (s.compareToIgnoreCase("fr") == 0 || s.compareToIgnoreCase("fr_CA") == 0) 71 | { 72 | return new French(); 73 | } 74 | if (s.compareToIgnoreCase("es") == 0) 75 | { 76 | return new Spanish(); 77 | } 78 | if (s.compareToIgnoreCase("de") == 0 || s.compareToIgnoreCase("de_DE") == 0) 79 | { 80 | return new GermanyGerman(); 81 | } 82 | if (s.compareToIgnoreCase("de_CH") == 0) 83 | { 84 | return new SwissGerman(); 85 | } 86 | if (s.compareToIgnoreCase("de_AT") == 0) 87 | { 88 | return new AustrianGerman(); 89 | } 90 | if (s.compareToIgnoreCase("nl") == 0) 91 | { 92 | return new Dutch(); 93 | } 94 | if (s.compareToIgnoreCase("pt") == 0) 95 | { 96 | return new Portuguese(); 97 | } 98 | if (s.compareToIgnoreCase("pt_BR") == 0) 99 | { 100 | return new BrazilianPortuguese(); 101 | } 102 | return null; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/rules/RegexRule.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.rules; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | import ca.uqac.lif.petitpoucet.function.strings.Range; 24 | import ca.uqac.lif.textidote.Advice; 25 | import ca.uqac.lif.textidote.Rule; 26 | import ca.uqac.lif.textidote.as.AnnotatedString; 27 | import ca.uqac.lif.textidote.as.AnnotatedString.Line; 28 | import ca.uqac.lif.textidote.as.Match; 29 | 30 | /** 31 | * Rule based on a regular expression pattern to be found in the text. 32 | * @author Sylvain Hallé 33 | */ 34 | public class RegexRule extends Rule 35 | { 36 | /** 37 | * The pattern to find in the text 38 | */ 39 | protected String m_pattern; 40 | 41 | /** 42 | * If this pattern is found, the rule does not apply 43 | */ 44 | protected String m_exceptionPattern; 45 | 46 | /** 47 | * The message template to generate when the pattern is found 48 | */ 49 | protected String m_message; 50 | 51 | /** 52 | * The maximum number of times the rule can look for the pattern in 53 | * the text 54 | */ 55 | protected static final transient int MAX_ITERATIONS = 100; 56 | 57 | /** 58 | * Creates a new regex rule 59 | * @param name The name given to the rule 60 | * @param pattern The pattern to find in the text 61 | * @param message The message template to generate when the pattern 62 | * is found. If the pattern contains capture groups, the message can 63 | * refer to these capture groups in the usual way (i.e. "$1" refers to 64 | * the first group, etc.). 65 | */ 66 | public RegexRule(String name, String pattern, String message) 67 | { 68 | this(name, pattern, null, message); 69 | } 70 | 71 | /** 72 | * Creates a new regex rule 73 | * @param name The name given to the rule 74 | * @param pattern The pattern to find in the text 75 | * @param exception If this pattern is found, the rule does not apply 76 | * @param message The message template to generate when the pattern 77 | * is found. If the pattern contains capture groups, the message can 78 | * refer to these capture groups in the usual way (i.e. "$1" refers to 79 | * the first group, etc.). 80 | */ 81 | public RegexRule(String name, String pattern, String exception, String message) 82 | { 83 | super(name); 84 | m_pattern = pattern; 85 | m_message = message; 86 | m_exceptionPattern = exception; 87 | } 88 | 89 | @Override 90 | /*@ non_null @*/ public List evaluate(/*@ non_null @*/ AnnotatedString s) 91 | { 92 | List out_list = new ArrayList(); 93 | int pos = 0; 94 | for (int num_iterations = 0; num_iterations < MAX_ITERATIONS; num_iterations++) 95 | { 96 | Match match = s.find(m_pattern, pos); 97 | if (match == null) 98 | { 99 | // No cigarettes, no matches 100 | break; 101 | } 102 | if (m_exceptionPattern != null && match.getMatch().matches(m_exceptionPattern)) 103 | { 104 | // Rule does not apply 105 | continue; 106 | } 107 | String message = createMessage(match); 108 | int start_pos = match.getPosition(); 109 | int end_pos = start_pos + match.getMatch().length(); 110 | Range r = s.findOriginalRange(new Range(start_pos, end_pos - 1)); 111 | Line original_line = null; 112 | boolean original_range = true; 113 | if (r != null) 114 | { 115 | // Found a range in the original string 116 | original_line = s.getOriginalLineOf(r.getStart()); 117 | } 118 | else 119 | { 120 | // Did not find a range in the original string, use the clean string instead 121 | r = new Range(match.getPosition(), match.getPosition() + match.getMatch().length() - 1); 122 | original_line = s.getLineOf(match.getPosition()); 123 | original_range = false; 124 | } 125 | assert r != null; 126 | Advice a = new Advice(this, r, message, s, original_line); 127 | a.setOriginal(original_range); 128 | out_list.add(a); 129 | pos = start_pos + match.getMatch().length(); 130 | } 131 | return out_list; 132 | } 133 | 134 | /** 135 | * Creates the message for a specific advice, by replacing references 136 | * to capture groups in the message template by the actual strings that 137 | * matched these capture groups 138 | * @param match A match object containing data about the regex match 139 | * @return The formatted message 140 | */ 141 | protected String createMessage(Match match) 142 | { 143 | String out = m_message; 144 | for (int i = 0; i <= match.groupCount(); i++) 145 | { 146 | String s = match.group(i); 147 | if (s != null) 148 | { 149 | out = out.replace("$" + i, match.group(i)); 150 | } 151 | else 152 | { 153 | out = out.replace("$" + i, ""); 154 | } 155 | } 156 | return out; 157 | } 158 | 159 | @Override 160 | public String getDescription() 161 | { 162 | return "Regex check on the text"; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/rules/SectionInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.rules; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | import ca.uqac.lif.petitpoucet.function.strings.Range; 24 | 25 | /** 26 | * Information on a section heading inside a document. 27 | * @author Sylvain Hallé 28 | */ 29 | class SectionInfo 30 | { 31 | /** 32 | * The name of the section heading 33 | */ 34 | protected String m_sectionName; 35 | 36 | /** 37 | * The range of characters corresponding to this heading in the source 38 | * document 39 | */ 40 | protected Range m_range; 41 | 42 | /** 43 | * The approximate size (in number of words) of this section. 44 | * This includes the contents of all subsections contained within 45 | * the section. 46 | */ 47 | protected int m_size = 0; 48 | 49 | protected static final List s_headings = createHeadings(); 50 | 51 | /** 52 | * Creates a new section info structure 53 | * @param section_name The name of the section heading 54 | * @param range The range of characters corresponding to this heading 55 | * in the source document 56 | */ 57 | public SectionInfo(String section_name, Range range) 58 | { 59 | super(); 60 | m_sectionName = section_name; 61 | m_range = range; 62 | m_size = 0; 63 | } 64 | 65 | /** 66 | * Returns the distance between two successive headings. For example, 67 | * the distance between a section and a subsection is -1 (moving down 68 | * one level); the distance between a subsection and a chapter is +2 69 | * (moving up two levels). 70 | * @param last_heading The last heading seen 71 | * @param heading The heading currently seen 72 | * @return The distance 73 | */ 74 | public static int distance(String last_heading, String heading) 75 | { 76 | return s_headings.indexOf(last_heading) - s_headings.indexOf(heading); 77 | } 78 | 79 | /** 80 | * Determines if the new heading is a move down in the heading hierarchy, 81 | * with respect to the last heading. For example, seeing a "section" 82 | * followed by a "subsection" is a move down. 83 | * @param last_heading The last heading seen 84 | * @param heading The heading currently seen 85 | * @return {@code true} if this is a move down, {@code false} otherwise 86 | */ 87 | // TODO: eventually, this method could be replaced with a call to distance() 88 | public static boolean isMoveDown(String last_heading, String heading) 89 | { 90 | if (heading.compareTo("chapter") == 0) 91 | { 92 | return last_heading.isEmpty() || last_heading.compareTo("part") == 0; 93 | } 94 | if (heading.compareTo("section") == 0) 95 | { 96 | return last_heading.isEmpty() || last_heading.compareTo("part") == 0 || last_heading.compareTo("chapter") == 0; 97 | } 98 | if (heading.compareTo("subsection") == 0) 99 | { 100 | return last_heading.isEmpty() || last_heading.compareTo("part") == 0 || last_heading.compareTo("chapter") == 0 || last_heading.compareTo("section") == 0; 101 | } 102 | if (heading.compareTo("subsubsection") == 0) 103 | { 104 | return last_heading.isEmpty() || last_heading.compareTo("part") == 0 || last_heading.compareTo("chapter") == 0 || last_heading.compareTo("section") == 0 || last_heading.compareTo("subsection") == 0; 105 | } 106 | if (heading.compareTo("paragraph") == 0) 107 | { 108 | return last_heading.isEmpty() || last_heading.compareTo("part") == 0 || last_heading.compareTo("chapter") == 0 || last_heading.compareTo("section") == 0 || last_heading.compareTo("subsection") == 0 || last_heading.compareTo("subsubsection") == 0; 109 | } 110 | return last_heading.compareTo("") == 0; 111 | } 112 | 113 | public static boolean isMoveDown(SectionInfo last, SectionInfo current) 114 | { 115 | return isMoveDown(last.m_sectionName, current.m_sectionName); 116 | } 117 | 118 | /** 119 | * Creates an ordered list of headings in LaTeX documents 120 | * @return The list of headings 121 | */ 122 | protected static List createHeadings() 123 | { 124 | List list = new ArrayList(6); 125 | list.add("part"); 126 | list.add("chapter"); 127 | list.add("section"); 128 | list.add("subsection"); 129 | list.add("subsubsection"); 130 | list.add("paragraph"); 131 | return list; 132 | } 133 | 134 | @Override 135 | public String toString() 136 | { 137 | return "\\" + m_sectionName + "{} " + m_range + " (" + m_size + ")"; 138 | } 139 | } -------------------------------------------------------------------------------- /Source/Core/src/ca/uqac/lif/textidote/rules/regex-detex.csv: -------------------------------------------------------------------------------- 1 | # Spaces and punctuation 2 | 3 | sh:d:001 ,[A-Za-z] There should be a space after a comma. 4 | sh:d:002 \s[^@\s]*?\.[A-Za-z][^\.] \.e There should be a space after a period. If you are writing a URL or a filename, use the \url{} or \verb markup. 5 | sh:d:003 \s\.[^\s\d] There should not be a space before the period at the end of a sentence. 6 | sh:d:004 \s,[^\s] There should not be a space before a comma. 7 | sh:d:005 \s[;:\?!] There should not be a space before a punctuation mark. If in your language, typographic rules require a space here, LaTeX takes care of inserting it without your intervention. 8 | sh:d:007 <<|>> Use \og{} and \fg{} instead of << and >>. 9 | sh:d:008 (?. 17 | */ 18 | package ca.uqac.lif.util; 19 | 20 | import java.io.ByteArrayOutputStream; 21 | import java.io.IOException; 22 | import java.io.OutputStream; 23 | import java.io.PrintStream; 24 | 25 | /** 26 | * Print stream that prints nothing 27 | * @author Sylvain Hallé 28 | */ 29 | public class NullPrintStream extends PrintStream 30 | { 31 | /** 32 | * Creates a new null print stream 33 | */ 34 | public NullPrintStream() 35 | { 36 | super(new NullByteArrayOutputStream()); 37 | } 38 | 39 | /** 40 | * Output stream that prints nothing 41 | */ 42 | private static class NullByteArrayOutputStream extends ByteArrayOutputStream 43 | { 44 | @Override 45 | public void write(int b) 46 | { 47 | // do nothing 48 | } 49 | 50 | @Override 51 | public void write(byte[] b, int off, int len) 52 | { 53 | // do nothing 54 | } 55 | 56 | @Override 57 | public void writeTo(OutputStream out) throws IOException 58 | { 59 | // do nothing 60 | } 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /Source/Core/test2.tex: -------------------------------------------------------------------------------- 1 | \documentclass[]{scrbook} 2 | \begin{document} 3 | 4 | Errors generated by compilers and some other programs are listed for easy one-click access in the 'Plugins->Error List->Error List' window \partial. 5 | 6 | \input{tex/other2} 7 | 8 | 9 | \end{document} -------------------------------------------------------------------------------- /Source/Core/tex/folder/other.tex: -------------------------------------------------------------------------------- 1 | Helllo worlds. Dare is no ways to try thise i.e. others. -------------------------------------------------------------------------------- /Source/Core/tex/other2.tex: -------------------------------------------------------------------------------- 1 | Helllo worlds. Dare is no ways to try thise i.e. others. 2 | 3 | \input{folder/other.tex} -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/LanguageCodeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018-2023 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote; 19 | 20 | import java.io.ByteArrayOutputStream; 21 | import java.io.IOException; 22 | import java.io.InputStream; 23 | import java.io.PrintStream; 24 | import java.util.Arrays; 25 | import java.util.Collection; 26 | 27 | import static org.junit.Assert.*; 28 | import org.junit.Test; 29 | import org.junit.runner.RunWith; 30 | import org.junit.runners.Parameterized; 31 | 32 | import ca.uqac.lif.util.NullPrintStream; 33 | 34 | /** 35 | * Attempts to run the main loop using each language code in succession. 36 | * This is to make sure that all advertised languages are indeed usable and 37 | * don't throw an exception. 38 | * 39 | * @author Sylvain Hallé 40 | * 41 | */ 42 | @RunWith(Parameterized.class) 43 | public class LanguageCodeTest 44 | { 45 | /** 46 | * The langauge code of the current test 47 | */ 48 | protected String m_languageCode; 49 | 50 | /** 51 | * A nonexistent language code 52 | */ 53 | protected static final String FAKE_LANG = "zzz"; 54 | 55 | @Parameterized.Parameters(name = "{0}") 56 | public static Collection languageCodes() { 57 | return Arrays.asList(new Object[] { 58 | "de", "de_AT", "de_CH", "de_DE", 59 | "en", "en_CA", "en_UK", "en_US", 60 | "es", 61 | "fr", 62 | "nl", 63 | "pt", 64 | FAKE_LANG // We add a non existent language, to make sure the CLI 65 | // does report an error in such a case 66 | }); 67 | } 68 | 69 | /** 70 | * Creates a new instance of the language code test 71 | * @param language_code The language code to check 72 | */ 73 | public LanguageCodeTest(String language_code) 74 | { 75 | super(); 76 | m_languageCode = language_code; 77 | } 78 | 79 | @Test(timeout = 25000) 80 | public void testEachLanguage() throws IOException 81 | { 82 | InputStream in = LanguageCodeTest.class.getResourceAsStream("rules/data/test1.tex"); 83 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 84 | PrintStream out = new PrintStream(baos); 85 | int ret_code = Main.mainLoop(new String[] {"--no-color", "--read-all", "--check", m_languageCode}, in, out, new NullPrintStream()); 86 | String output = new String(baos.toByteArray()); 87 | assertNotNull(output); 88 | if (m_languageCode.compareTo(FAKE_LANG) == 0) 89 | { 90 | // For an unsupported language, the return code is negative 91 | assertTrue(ret_code == Main.ERR_UNKNOWN_LANGUAGE); 92 | } 93 | else 94 | { 95 | // Otherwise the code is null or positive 96 | assertTrue(ret_code >= 0); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/LinterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018-2023 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote; 19 | 20 | import static ca.uqac.lif.textidote.as.AnnotatedString.CRLF; 21 | 22 | import java.util.Scanner; 23 | 24 | import org.junit.Test; 25 | 26 | import ca.uqac.lif.textidote.as.AnnotatedString; 27 | import ca.uqac.lif.textidote.cleaning.TextCleanerException; 28 | import ca.uqac.lif.textidote.cleaning.latex.LatexCleaner; 29 | 30 | public class LinterTest 31 | { 32 | @Test(expected=LinterException.class) 33 | public void testNoBeginDocument1() throws TextCleanerException, LinterException 34 | { 35 | LatexCleaner detexer = new LatexCleaner().setIgnoreBeforeDocument(true); 36 | Linter l = new Linter(detexer); 37 | l.evaluateAll(AnnotatedString.read(new Scanner("abc" + CRLF + "def"))); 38 | } 39 | 40 | @Test 41 | public void testNoBeginDocument2() throws TextCleanerException, LinterException 42 | { 43 | LatexCleaner detexer = new LatexCleaner().setIgnoreBeforeDocument(false); 44 | Linter l = new Linter(detexer); 45 | l.evaluateAll(AnnotatedString.read(new Scanner("abc" + CRLF + "def"))); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/as/CheckRegexTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.as; 19 | 20 | import static org.junit.Assert.*; 21 | 22 | import java.util.List; 23 | import java.util.Map; 24 | import java.util.Scanner; 25 | 26 | import org.junit.Test; 27 | 28 | import ca.uqac.lif.textidote.Advice; 29 | import ca.uqac.lif.textidote.Main; 30 | import ca.uqac.lif.textidote.Rule; 31 | import ca.uqac.lif.textidote.rules.RegexRule; 32 | 33 | /** 34 | * Unit tests on regex rules 35 | */ 36 | public class CheckRegexTest 37 | { 38 | /** 39 | * A map of all regex rule names to rule instances 40 | */ 41 | protected final Map m_rules = Main.readRules(Main.REGEX_FILENAME); 42 | 43 | @Test 44 | public void testGeneric1() 45 | { 46 | AnnotatedString in_string = AnnotatedString.read(new Scanner("Lorem ipsum dolor sit amet")); 47 | Rule r = new RegexRule("name:foo", "foo", "Message foo"); 48 | List ad_list = r.evaluate(in_string); 49 | assertTrue(ad_list.isEmpty()); 50 | } 51 | 52 | @Test 53 | public void testGeneric2() 54 | { 55 | AnnotatedString in_string = AnnotatedString.read(new Scanner("Lorem foo ipsum dolor foo sit amet")); 56 | Rule r = new RegexRule("name:foo", "foo", "Message foo"); 57 | List ad_list = r.evaluate(in_string); 58 | assertEquals(2, ad_list.size()); 59 | } 60 | 61 | @Test 62 | public void testCmul1() 63 | { 64 | AnnotatedString in_string = AnnotatedString.read(new Scanner("\\cite{foo} Lorem ipsum \\cite{foo}")); 65 | Rule r = m_rules.get("sh:c:mul"); 66 | List ad_list = r.evaluate(in_string); 67 | assertTrue(ad_list.isEmpty()); 68 | } 69 | 70 | @Test 71 | public void testCmul2() 72 | { 73 | AnnotatedString in_string = AnnotatedString.read(new Scanner("\\citep{foo} Lorem ipsum \\citet{foo}")); 74 | Rule r = m_rules.get("sh:c:mul"); 75 | List ad_list = r.evaluate(in_string); 76 | assertTrue(ad_list.isEmpty()); 77 | } 78 | 79 | @Test 80 | public void testCmul3() 81 | { 82 | AnnotatedString in_string = AnnotatedString.read(new Scanner("Lorem ipsum \\cite{foo} \\cite{foo}")); 83 | Rule r = m_rules.get("sh:c:mul"); 84 | List ad_list = r.evaluate(in_string); 85 | assertEquals(1, ad_list.size()); 86 | } 87 | 88 | @Test 89 | public void testCmul4() 90 | { 91 | AnnotatedString in_string = AnnotatedString.read(new Scanner("Lorem ipsum \\cite{foo} \\citep{foo}")); 92 | Rule r = m_rules.get("sh:c:mul"); 93 | List ad_list = r.evaluate(in_string); 94 | assertEquals(1, ad_list.size()); 95 | } 96 | 97 | @Test 98 | public void testCmul5() 99 | { 100 | AnnotatedString in_string = AnnotatedString.read(new Scanner("Lorem ipsum \\cite{foo}, \\cite{foo}")); 101 | Rule r = m_rules.get("sh:c:mul"); 102 | List ad_list = r.evaluate(in_string); 103 | assertEquals(1, ad_list.size()); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/as/PositionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.as; 19 | 20 | import static org.junit.Assert.*; 21 | 22 | import org.junit.Test; 23 | 24 | public class PositionTest 25 | { 26 | @Test 27 | public void test1() 28 | { 29 | Position p1 = new Position(0, 0); 30 | Position p2 = new Position(0, 0); 31 | assertTrue(p1.compareTo(p2) == 0); 32 | } 33 | 34 | @Test 35 | public void test2() 36 | { 37 | Position p1 = new Position(0, 0); 38 | Position p2 = new Position(0, 1); 39 | assertTrue(p1.compareTo(p2) < 0); 40 | } 41 | 42 | @Test 43 | public void test3() 44 | { 45 | Position p1 = new Position(0, 1); 46 | Position p2 = new Position(0, 0); 47 | assertTrue(p1.compareTo(p2) > 0); 48 | } 49 | 50 | @Test 51 | public void test4() 52 | { 53 | Position p1 = new Position(0, 0); 54 | Position p2 = new Position(1, 0); 55 | assertTrue(p1.compareTo(p2) < 0); 56 | } 57 | 58 | @Test 59 | public void test5() 60 | { 61 | Position p1 = new Position(1, 0); 62 | Position p2 = new Position(0, 0); 63 | assertTrue(p1.compareTo(p2) > 0); 64 | } 65 | 66 | @Test 67 | public void testToString() 68 | { 69 | Position p1 = new Position(1, 0); 70 | String s = p1.toString(); 71 | assertNotNull(s); 72 | assertTrue(s.length() > 0); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/as/data/replacements-1.txt: -------------------------------------------------------------------------------- 1 | # Test with comments 2 | 3 | foo bar 4 | 5 | #hello -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/as/data/replacements-2.txt: -------------------------------------------------------------------------------- 1 | bar baz -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/as/data/replacements-3.txt: -------------------------------------------------------------------------------- 1 | bar( baz -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/as/data/test-subsec-3.tex: -------------------------------------------------------------------------------- 1 | \begin{document} 2 | \paragraph{foo} 3 | \part{bar} 4 | \chapter{baz} -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/cleaning/MarkdownCleanerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018-2021 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.cleaning; 19 | 20 | import ca.uqac.lif.textidote.as.AnnotatedString; 21 | import ca.uqac.lif.textidote.cleaning.markdown.MarkdownCleaner; 22 | import org.junit.Test; 23 | 24 | import java.util.Objects; 25 | import java.util.Scanner; 26 | 27 | import static ca.uqac.lif.textidote.as.AnnotatedString.CRLF; 28 | import static org.junit.Assert.assertEquals; 29 | 30 | public class MarkdownCleanerTest 31 | { 32 | @Test 33 | public void testRemoveBold1() throws TextCleanerException 34 | { 35 | MarkdownCleaner detexer = new MarkdownCleaner(); 36 | AnnotatedString as = detexer.clean(AnnotatedString.read(new Scanner("This is **bold**."))); 37 | assertEquals("This is bold.", as.toString()); 38 | } 39 | 40 | @Test 41 | public void testRemoveBackticks1() throws TextCleanerException 42 | { 43 | MarkdownCleaner detexer = new MarkdownCleaner(); 44 | AnnotatedString as = detexer.clean(AnnotatedString.read(new Scanner("This is `foo`."))); 45 | assertEquals("This is X.", as.toString()); 46 | } 47 | 48 | @Test 49 | public void testRemoveIndentedBlocks1() throws TextCleanerException 50 | { 51 | MarkdownCleaner detexer = new MarkdownCleaner(); 52 | AnnotatedString as = detexer.clean(AnnotatedString.read(new Scanner("Here are a few words." + CRLF + CRLF + " Some code block" + CRLF + CRLF + "Here are some more."))); 53 | assertEquals("Here are a few words." + CRLF + CRLF + CRLF + CRLF + "Here are some more.", as.toString()); 54 | } 55 | 56 | @Test 57 | public void testMarkdownCommentsAndFrontMatter() throws TextCleanerException { 58 | MarkdownCleaner markdownCleaner = new MarkdownCleaner(); 59 | AnnotatedString as = 60 | markdownCleaner.clean(AnnotatedString.read(new Scanner(Objects.requireNonNull(MarkdownCleanerTest.class.getResourceAsStream("data" + 61 | "/markdown-test-1.md"))))); 62 | assertEquals("Test with comments" + CRLF + CRLF + "foo bar" + CRLF + CRLF + "Begin of multiline " + CRLF + " end " + 63 | "comment" + CRLF + CRLF + 64 | "Second " + 65 | "comment" + CRLF + CRLF + 66 | "Some " + 67 | "other text", 68 | as.toString()); 69 | } 70 | 71 | @Test 72 | public void testMarkdownIgnoreComments() throws TextCleanerException { 73 | MarkdownCleaner markdownCleaner = new MarkdownCleaner(); 74 | AnnotatedString as = 75 | markdownCleaner.clean(AnnotatedString.read(new Scanner(Objects.requireNonNull(MarkdownCleanerTest.class.getResourceAsStream("data" + 76 | "/markdown-test-2.md"))))); 77 | assertEquals("TexTidote ignore test file" + CRLF + CRLF + "some words" + CRLF + CRLF + "Ignore everything " + 78 | "below" + CRLF, 79 | as.toString()); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/cleaning/data/issue215.tex: -------------------------------------------------------------------------------- 1 | \begin{document} 2 | One line math: 3 | \[\int_{1}^{2}x \mathrm{d}x}\] 4 | 5 | Tikzpicture: 6 | \begin{tikzpicture} 7 | \draw (0,0); 8 | \end{tikzpicture} 9 | 10 | Multi line math: 11 | \[ 12 | \int_{1}^{2}x \mathrm{d}x} 13 | \] 14 | 15 | Second one line math: 16 | \[\int_{1}^{2}x \mathrm{d}x}\] 17 | \end{document} -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/cleaning/data/markdown-test-1.md: -------------------------------------------------------------------------------- 1 | --- 2 | eleventyNavigation: 3 | key: ExamplePage 4 | title: Example Page 5 | layout: default_layout 6 | --- 7 | # Test with comments 8 | 9 | 10 | foo bar 11 | 12 | Begin of multiline end comment 15 | 16 | ## Second comment 17 | 18 | Some other text 19 | -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/cleaning/data/markdown-test-2.md: -------------------------------------------------------------------------------- 1 | # TexTidote ignore test file 2 | 3 | 4 | This should be ignored 5 | 6 | 7 | 8 | some words 9 | 10 | ## Ignore everything below 11 | 12 | 13 | All of this should now be ignored. 14 | Also this. 15 | And This 16 | -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/cleaning/data/replace-with-nothing.txt: -------------------------------------------------------------------------------- 1 | # commment 2 | text -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/cleaning/data/replacements-1.txt: -------------------------------------------------------------------------------- 1 | # Test with comments 2 | 3 | foo bar 4 | 5 | #hello -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/cleaning/data/replacements-2.txt: -------------------------------------------------------------------------------- 1 | bar baz -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/cleaning/data/replacements-3.txt: -------------------------------------------------------------------------------- 1 | bar( baz -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/cleaning/data/test1.tex: -------------------------------------------------------------------------------- 1 | \begin{itemize} 2 | \item Hello % This is a comment 3 | \item World 4 | \end{itemize} -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/cleaning/data/test2.tex: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | %% Something that looks like a regular LaTeX file 3 | %% 4 | %% By me 5 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 6 | 7 | \documentclass{article} 8 | \usepackage[english]{babel} 9 | 10 | \begin{document} 11 | 12 | \title{My Paper} 13 | \maketitle 14 | 15 | A first paragraph with some stuff, some \textbf{bold text} and other things, like a \cite{my:paper} citation.The text is not evenly spaced. 16 | 17 | \section{A first section} 18 | 19 | Hello world! 20 | 21 | \begin{itemize} 22 | \item Hello % This is a comment 23 | \item World 24 | \end{itemize} 25 | 26 | \end{document} -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/cleaning/data/test3.tex: -------------------------------------------------------------------------------- 1 | \begin{comment} 2 | My Paper 3 | A first paragraph with some stuff, some bold text and other things, like a [0] citation.The text is not evenly spaced. 4 | A first section 5 | Hello world! 6 | \end{comment} -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/cleaning/data/test4.tex: -------------------------------------------------------------------------------- 1 | \begin{IEEEeqnarray*} 2 | foo 3 | \end{IEEEeqnarray*} -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/render/HtmlRenderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018-2019 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.render; 19 | 20 | import static org.junit.Assert.*; 21 | 22 | import java.io.ByteArrayOutputStream; 23 | import java.io.PrintStream; 24 | import java.util.List; 25 | import java.util.Scanner; 26 | 27 | import org.junit.Test; 28 | 29 | import ca.uqac.lif.textidote.Advice; 30 | import ca.uqac.lif.textidote.Rule; 31 | import ca.uqac.lif.textidote.as.AnnotatedString; 32 | import ca.uqac.lif.textidote.rules.CheckFigureReferences; 33 | import ca.uqac.lif.textidote.rules.CheckFigureReferencesTest; 34 | import ca.uqac.lif.util.AnsiPrinter; 35 | 36 | public class HtmlRenderTest 37 | { 38 | @Test 39 | public void test1() 40 | { 41 | AnnotatedString in_string = AnnotatedString.read(new Scanner(CheckFigureReferencesTest.class.getResourceAsStream("data/test4.tex"))); 42 | Rule r = new CheckFigureReferences(); 43 | List ad_list = r.evaluate(in_string); 44 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 45 | PrintStream out = new PrintStream(baos); 46 | AnsiPrinter ansi_p = new AnsiPrinter(out); 47 | HtmlAdviceRenderer har = new HtmlAdviceRenderer(ansi_p); 48 | har.addAdvice("foo", in_string, ad_list); 49 | har.render(); 50 | String html_render = new String(baos.toByteArray()); 51 | assertNotNull(html_render); 52 | assertFalse(html_render.isEmpty()); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/render/SinglelineAdviceRendererTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018-2019 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.render; 19 | 20 | import static org.junit.Assert.*; 21 | 22 | import java.io.ByteArrayOutputStream; 23 | import java.util.List; 24 | import java.util.ArrayList; 25 | 26 | import org.junit.Test; 27 | 28 | import ca.uqac.lif.petitpoucet.function.strings.Range; 29 | import ca.uqac.lif.textidote.Advice; 30 | import ca.uqac.lif.textidote.Rule; 31 | import ca.uqac.lif.textidote.as.AnnotatedString; 32 | import ca.uqac.lif.textidote.as.Position; 33 | import ca.uqac.lif.util.AnsiPrinter; 34 | import static ca.uqac.lif.textidote.as.AnnotatedString.CRLF; 35 | 36 | public class SinglelineAdviceRendererTest { 37 | @Test 38 | public void test1() { 39 | String filename = "file"; 40 | String rulename = "rule"; 41 | String line1 = "foo"; 42 | String line2 = "bar"; 43 | String message = "warning message"; 44 | AnnotatedString as = new AnnotatedString(line1 + CRLF + line2 + CRLF); 45 | int startLine = 1; 46 | int startCol = 0; 47 | int endLine = 1; 48 | int endCol = 2; 49 | Position start = new Position(startLine, startCol); 50 | Position end = new Position(endLine, endCol); 51 | Range range = new Range(as.getIndex(start), as.getIndex(end)); 52 | Rule rule = new Rule(rulename) 53 | { 54 | @Override 55 | public List evaluate(/* @ non_null @ */ AnnotatedString s) 56 | { 57 | return new ArrayList(); 58 | } 59 | @Override 60 | public String getDescription() 61 | { 62 | return ""; 63 | } 64 | }; 65 | ArrayList adList = new ArrayList(); 66 | Advice ad = new Advice(rule, range, message, as, as.getLine(1)); 67 | adList.add(ad); 68 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 69 | AnsiPrinter printer = new AnsiPrinter(baos); 70 | printer.disableColors(); 71 | SinglelineAdviceRenderer renderer = new SinglelineAdviceRenderer(printer); 72 | renderer.addAdvice(filename, as, adList); 73 | renderer.render(); 74 | String output = new String(baos.toByteArray()); 75 | assertNotNull(output); 76 | String expected = String.format(filename + "(L" + (startLine + 1) + "C" + (startCol + 1) + "-L" + (endLine + 1) 77 | + "C" + (endCol + 1) + "): " + message + " \"" + line2 + "\"%n"); 78 | assertEquals(expected, output); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/CheckAdviceTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018-2019 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.rules; 19 | 20 | import static org.junit.Assert.*; 21 | 22 | import java.util.List; 23 | import java.util.Scanner; 24 | 25 | import org.junit.Test; 26 | 27 | import ca.uqac.lif.petitpoucet.function.strings.Range; 28 | import ca.uqac.lif.textidote.Advice; 29 | import ca.uqac.lif.textidote.as.AnnotatedString; 30 | 31 | public class CheckAdviceTest 32 | { 33 | @Test 34 | public void testToString() 35 | { 36 | // Simple test to check that the toString method of Advice 37 | // produces a non-empty string 38 | AnnotatedString in_string = AnnotatedString.read(new Scanner(CheckAdviceTest.class.getResourceAsStream("data/test-subsec-1.tex"))); 39 | CheckSubsectionSize r = new CheckSubsectionSize(); 40 | r.setMinNumWords(40); 41 | List ad_list = r.evaluate(in_string); 42 | Advice ad = ad_list.get(0); 43 | String s = ad.toString(); 44 | assertNotNull(s); 45 | assertFalse(s.isEmpty()); 46 | } 47 | 48 | @Test 49 | public void testEquals() 50 | { 51 | Advice ad1 = new Advice(new CheckNoBreak(), new Range(0, 10), "message", new AnnotatedString("resource"), new AnnotatedString("line").getLine(0)); 52 | Advice ad2 = new Advice(new CheckNoBreak(), new Range(1, 10), "message", new AnnotatedString("resource"), new AnnotatedString("line").getLine(0)); 53 | Advice ad3 = new Advice(new CheckStackedHeadings(), new Range(0, 10), "message", new AnnotatedString("resource"), new AnnotatedString("line").getLine(0)); 54 | assertTrue(ad1.equals(ad1)); 55 | assertFalse(ad1.equals(ad2)); 56 | assertFalse(ad1.equals(ad3)); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/CheckCaptionsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.rules; 19 | 20 | import static org.junit.Assert.*; 21 | 22 | import java.util.List; 23 | import java.util.Scanner; 24 | 25 | import org.junit.Test; 26 | 27 | import ca.uqac.lif.textidote.Advice; 28 | import ca.uqac.lif.textidote.as.AnnotatedString; 29 | 30 | public class CheckCaptionsTest 31 | { 32 | @Test 33 | public void test1() 34 | { 35 | CheckCaptions rule = new CheckCaptions(); 36 | AnnotatedString as = AnnotatedString.read(new Scanner("\\caption{Hello world }")); 37 | List ad_list = rule.evaluate(as); 38 | assertEquals(1, ad_list.size()); 39 | } 40 | 41 | @Test 42 | public void test2() 43 | { 44 | CheckCaptions rule = new CheckCaptions(); 45 | AnnotatedString as = AnnotatedString.read(new Scanner("\\caption{Hello world. }")); 46 | List ad_list = rule.evaluate(as); 47 | assertEquals(0, ad_list.size()); 48 | } 49 | 50 | @Test 51 | public void test3() 52 | { 53 | CheckCaptions rule = new CheckCaptions(); 54 | AnnotatedString as = AnnotatedString.read(new Scanner("\\caption{Hello \textbf{world }}")); 55 | List ad_list = rule.evaluate(as); 56 | assertEquals(1, ad_list.size()); 57 | } 58 | 59 | @Test 60 | public void test4() 61 | { 62 | CheckCaptions rule = new CheckCaptions(); 63 | AnnotatedString as = AnnotatedString.read(new Scanner("\\caption{Hello \textbf{world.}}")); 64 | List ad_list = rule.evaluate(as); 65 | assertEquals(1, ad_list.size()); 66 | } 67 | 68 | @Test 69 | public void test5() 70 | { 71 | CheckCaptions rule = new CheckCaptions(); 72 | AnnotatedString as = AnnotatedString.read(new Scanner("\\caption{Hello \textbf{world }.}")); 73 | List ad_list = rule.evaluate(as); 74 | assertEquals(0, ad_list.size()); 75 | } 76 | 77 | @Test 78 | public void test6() 79 | { 80 | CheckCaptions rule = new CheckCaptions(); 81 | AnnotatedString as = AnnotatedString.read(new Scanner("\\captionsetup{whatever}")); 82 | List ad_list = rule.evaluate(as); 83 | assertEquals(0, ad_list.size()); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/CheckCiteMixTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.rules; 19 | 20 | import static org.junit.Assert.*; 21 | 22 | import java.util.List; 23 | import java.util.Scanner; 24 | 25 | import org.junit.Test; 26 | 27 | import ca.uqac.lif.textidote.Advice; 28 | import ca.uqac.lif.textidote.Rule; 29 | import ca.uqac.lif.textidote.as.AnnotatedString; 30 | 31 | public class CheckCiteMixTest 32 | { 33 | @Test 34 | public void test1() 35 | { 36 | AnnotatedString in_string = AnnotatedString.read(new Scanner("\\cite{foo}\n" + 37 | "\\cite{bar}\n" + 38 | "\\cite{baz}")); 39 | Rule r = new CheckCiteMix(); 40 | List ad_list = r.evaluate(in_string); 41 | assertTrue(ad_list.isEmpty()); 42 | } 43 | 44 | @Test 45 | public void test2() 46 | { 47 | AnnotatedString in_string = AnnotatedString.read(new Scanner("\\cite{foo}\n" + 48 | "\\citep{bar}\n" + 49 | "\\citet{baz}")); 50 | Rule r = new CheckCiteMix(); 51 | List ad_list = r.evaluate(in_string); 52 | assertEquals(1, ad_list.size()); 53 | } 54 | 55 | @Test 56 | public void test3() 57 | { 58 | AnnotatedString in_string = AnnotatedString.read(new Scanner("\\citep{foo}\n" + 59 | "\\citep{bar}\n" + 60 | "\\cite{baz}")); 61 | Rule r = new CheckCiteMix(); 62 | List ad_list = r.evaluate(in_string); 63 | assertEquals(1, ad_list.size()); 64 | } 65 | 66 | @Test 67 | public void test4() 68 | { 69 | AnnotatedString in_string = AnnotatedString.read(new Scanner("\\citep{foo}\n" + 70 | "\\citet{bar}\n" + 71 | "\\citep{baz}")); 72 | Rule r = new CheckCiteMix(); 73 | List ad_list = r.evaluate(in_string); 74 | assertTrue(ad_list.isEmpty()); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/CheckFigurePathsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.rules; 19 | 20 | import static org.junit.Assert.*; 21 | 22 | import java.util.List; 23 | import java.util.Scanner; 24 | 25 | import org.junit.Test; 26 | 27 | import ca.uqac.lif.textidote.Advice; 28 | import ca.uqac.lif.textidote.Rule; 29 | import ca.uqac.lif.textidote.as.AnnotatedString; 30 | 31 | public class CheckFigurePathsTest 32 | { 33 | @Test 34 | public void test1() 35 | { 36 | AnnotatedString in_string = AnnotatedString.read(new Scanner("\\begin{figure}\n" + 37 | "\\includegraphics{path/to/file.pdf}\n" + 38 | "\\end{figure}")); 39 | Rule r = new CheckFigurePaths(); 40 | List ad_list = r.evaluate(in_string); 41 | assertTrue(ad_list.isEmpty()); 42 | } 43 | 44 | @Test 45 | public void test2() 46 | { 47 | AnnotatedString in_string = AnnotatedString.read(new Scanner("\\begin{figure}\n" + 48 | "\\includegraphics{D:\\Sylvain\\file.pdf}\n" + 49 | "\\end{figure}")); 50 | Rule r = new CheckFigurePaths(); 51 | List ad_list = r.evaluate(in_string); 52 | assertEquals(1, ad_list.size()); 53 | } 54 | 55 | @Test 56 | public void test3() 57 | { 58 | AnnotatedString in_string = AnnotatedString.read(new Scanner("\\begin{figure}\n" + 59 | "\\includegraphics{/home/Sylvain/file.pdf}\n" + 60 | "\\end{figure}")); 61 | Rule r = new CheckFigurePaths(); 62 | List ad_list = r.evaluate(in_string); 63 | assertEquals(1, ad_list.size()); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/CheckFigureReferencesTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.rules; 19 | 20 | import static org.junit.Assert.*; 21 | 22 | import java.util.List; 23 | import java.util.Scanner; 24 | 25 | import org.junit.Test; 26 | 27 | import ca.uqac.lif.textidote.Advice; 28 | import ca.uqac.lif.textidote.Rule; 29 | import ca.uqac.lif.textidote.as.AnnotatedString; 30 | import ca.uqac.lif.textidote.as.Position; 31 | import ca.uqac.lif.textidote.as.PositionRange; 32 | 33 | public class CheckFigureReferencesTest 34 | { 35 | @Test 36 | public void test1() 37 | { 38 | AnnotatedString in_string = AnnotatedString.read(new Scanner(CheckFigureReferencesTest.class.getResourceAsStream("data/test4.tex"))); 39 | Rule r = new CheckFigureReferences(); 40 | List ad_list = r.evaluate(in_string); 41 | assertTrue(ad_list.isEmpty()); 42 | } 43 | 44 | @Test 45 | public void test1Location() 46 | { 47 | AnnotatedString in_string = AnnotatedString.read(new Scanner(CheckFigureReferencesTest.class.getResourceAsStream("data/test6.tex"))); 48 | Rule r = new CheckFigureReferences(); 49 | List ad_list = r.evaluate(in_string); 50 | Advice ad = ad_list.get(0); 51 | PositionRange range = ad.getPositionRange(); 52 | assertEquals(new Position(6, 0), range.getStart()); 53 | assertEquals(new Position(6, 12), range.getEnd()); 54 | } 55 | 56 | @Test 57 | public void test2() 58 | { 59 | AnnotatedString in_string = AnnotatedString.read(new Scanner(CheckFigureReferencesTest.class.getResourceAsStream("data/test5.tex"))); 60 | Rule r = new CheckFigureReferences(); 61 | List ad_list = r.evaluate(in_string); 62 | assertEquals(1, ad_list.size()); 63 | } 64 | 65 | @Test 66 | public void test3() 67 | { 68 | AnnotatedString in_string = AnnotatedString.read(new Scanner(CheckSubsectionsTest.class.getResourceAsStream("data/test-subsec-3.tex"))); 69 | Rule r = new CheckSubsections(); 70 | List ad_list = r.evaluate(in_string); 71 | assertTrue(containsAdviceWithLabel(ad_list, "sh:secorder")); 72 | } 73 | 74 | /** 75 | * Checks if the list of advice contains one with a given label 76 | * @param list The list of advice 77 | * @param The label to look for 78 | * @return {@code true} if the list contains an advice with given label, 79 | * {@code false} otherwise 80 | */ 81 | protected static boolean containsAdviceWithLabel(List list, String label) 82 | { 83 | for (Advice a : list) 84 | { 85 | if (a.getRule().getName().compareToIgnoreCase(label) == 0) 86 | { 87 | return true; 88 | } 89 | } 90 | return false; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/CheckLanguageTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.rules; 19 | 20 | import static org.junit.Assert.*; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | import java.util.Scanner; 25 | 26 | import org.junit.Test; 27 | 28 | import ca.uqac.lif.textidote.Advice; 29 | import ca.uqac.lif.textidote.Rule; 30 | import ca.uqac.lif.textidote.as.AnnotatedString; 31 | 32 | public class CheckLanguageTest 33 | { 34 | @Test 35 | public void test1() throws CheckLanguage.UnsupportedLanguageException 36 | { 37 | AnnotatedString in_string = AnnotatedString.read(new Scanner(CheckLanguageTest.class.getResourceAsStream("data/test-lt-1.tex"))); 38 | Rule r = new CheckLanguage(LanguageFactory.getLanguageFromString("en")); 39 | List ad_list = r.evaluate(in_string); 40 | assertEquals(0, ad_list.size()); 41 | } 42 | 43 | @Test 44 | public void test2() throws CheckLanguage.UnsupportedLanguageException 45 | { 46 | AnnotatedString in_string = AnnotatedString.read(new Scanner(CheckLanguageTest.class.getResourceAsStream("data/test-lt-2.tex"))); 47 | Rule r = new CheckLanguage(LanguageFactory.getLanguageFromString("en")); 48 | List ad_list = r.evaluate(in_string); 49 | assertEquals(1, ad_list.size()); 50 | } 51 | 52 | @Test 53 | public void test3() throws CheckLanguage.UnsupportedLanguageException 54 | { 55 | AnnotatedString in_string = AnnotatedString.read(new Scanner(CheckLanguageTest.class.getResourceAsStream("data/test-lt-2.tex"))); 56 | List dict = new ArrayList(2); 57 | dict.add("foo"); 58 | dict.add("foagzx"); 59 | Rule r = new CheckLanguage(LanguageFactory.getLanguageFromString("en"), dict); 60 | List ad_list = r.evaluate(in_string); 61 | assertEquals(0, ad_list.size()); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/CheckLevelSkipTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.rules; 19 | 20 | import static org.junit.Assert.*; 21 | 22 | import java.util.List; 23 | import java.util.Scanner; 24 | 25 | import org.junit.Test; 26 | 27 | import ca.uqac.lif.textidote.Advice; 28 | import ca.uqac.lif.textidote.Rule; 29 | import ca.uqac.lif.textidote.as.AnnotatedString; 30 | 31 | public class CheckLevelSkipTest 32 | { 33 | @Test 34 | public void test1() 35 | { 36 | AnnotatedString in_string = AnnotatedString.read(new Scanner("\\section{foo}\n" + 37 | "\\subsection{bar}\n" + 38 | "\\chapter{baz}")); 39 | Rule r = new CheckSubsections(); 40 | List ad_list = r.evaluate(in_string); 41 | assertFalse(containsSkipAdvice(ad_list)); 42 | } 43 | 44 | @Test 45 | public void test2() 46 | { 47 | AnnotatedString in_string = AnnotatedString.read(new Scanner("\\section{foo}\n" + 48 | "\\subsubsection{bar}\n" + 49 | "\\section{baz}")); 50 | Rule r = new CheckSubsections(); 51 | List ad_list = r.evaluate(in_string); 52 | assertTrue(containsSkipAdvice(ad_list)); 53 | } 54 | 55 | /** 56 | * Checks if the list of advice contains one of type "section skip" 57 | * @param list The list of advice 58 | * @return {@code true} if the list contains a "section skip" advice, 59 | * {@code false} otherwise 60 | */ 61 | protected static boolean containsSkipAdvice(List list) 62 | { 63 | for (Advice a : list) 64 | { 65 | if (a.getRule().getName().compareToIgnoreCase("sh:secskip") == 0) 66 | { 67 | return true; 68 | } 69 | } 70 | return false; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/CheckNoBreakTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.rules; 19 | 20 | import static org.junit.Assert.*; 21 | 22 | import java.util.List; 23 | import java.util.Scanner; 24 | 25 | import org.junit.Test; 26 | 27 | import ca.uqac.lif.textidote.Advice; 28 | import ca.uqac.lif.textidote.Rule; 29 | import ca.uqac.lif.textidote.as.AnnotatedString; 30 | 31 | public class CheckNoBreakTest 32 | { 33 | @Test 34 | public void test1() 35 | { 36 | AnnotatedString in_string = AnnotatedString.read(new Scanner("Lorem ipsum dolor sit amet.\n\n" + 37 | "Lorem ipsum dolor sit amet.")); 38 | Rule r = new CheckNoBreak(); 39 | List ad_list = r.evaluate(in_string); 40 | assertTrue(ad_list.isEmpty()); 41 | } 42 | 43 | @Test 44 | public void test2() 45 | { 46 | AnnotatedString in_string = AnnotatedString.read(new Scanner("Lorem ipsum dolor sit amet.\\\\\n" + 47 | "Lorem ipsum dolor sit amet.")); 48 | Rule r = new CheckNoBreak(); 49 | List ad_list = r.evaluate(in_string); 50 | assertEquals(1, ad_list.size()); 51 | } 52 | 53 | @Test 54 | public void test3() 55 | { 56 | AnnotatedString in_string = AnnotatedString.read(new Scanner("\\begin{align*}\n" + 57 | "Lorem ipsum dolor sit amet.\\\\\n" + 58 | "Lorem ipsum dolor sit amet.\n"+ 59 | "\\end{align*}")); 60 | Rule r = new CheckNoBreak(); 61 | List ad_list = r.evaluate(in_string); 62 | assertTrue(ad_list.isEmpty()); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/CheckRule.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018-2019 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.rules; 19 | 20 | import static org.junit.Assert.*; 21 | 22 | import java.util.List; 23 | 24 | import org.junit.Test; 25 | 26 | import ca.uqac.lif.textidote.Advice; 27 | import ca.uqac.lif.textidote.Rule; 28 | import ca.uqac.lif.textidote.as.AnnotatedString; 29 | 30 | public class CheckRule 31 | { 32 | @Test 33 | public void testToString() 34 | { 35 | // Simple test to check that the toString method 36 | // produces a non-empty string 37 | Rule r = new DummyRule("foo"); 38 | assertEquals("foo", r.toString()); 39 | } 40 | 41 | public static class DummyRule extends Rule 42 | { 43 | public DummyRule(String name) 44 | { 45 | super(name); 46 | } 47 | 48 | @Override 49 | public List evaluate(AnnotatedString s) 50 | { 51 | // Don't care 52 | return null; 53 | } 54 | 55 | @Override 56 | public String getDescription() 57 | { 58 | // Don't care 59 | return ""; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/CheckStackedHeadingsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.rules; 19 | 20 | import static org.junit.Assert.*; 21 | 22 | import java.util.List; 23 | import java.util.Scanner; 24 | 25 | import org.junit.Test; 26 | 27 | import ca.uqac.lif.textidote.Advice; 28 | import ca.uqac.lif.textidote.as.AnnotatedString; 29 | 30 | public class CheckStackedHeadingsTest 31 | { 32 | @Test 33 | public void test1() 34 | { 35 | AnnotatedString in_string = AnnotatedString.read(new Scanner(CheckStackedHeadingsTest.class.getResourceAsStream("data/test-stacked-1.tex"))); 36 | CheckStackedHeadings r = new CheckStackedHeadings(); 37 | List ad_list = r.evaluate(in_string); 38 | assertEquals(1, ad_list.size()); 39 | Advice ad = ad_list.get(0); 40 | assertEquals(4, in_string.getPosition(ad.getRange().getStart()).getLine()); 41 | } 42 | 43 | @Test 44 | public void test2() 45 | { 46 | AnnotatedString in_string = AnnotatedString.read(new Scanner(CheckStackedHeadingsTest.class.getResourceAsStream("data/test-stacked-0.tex"))); 47 | CheckStackedHeadings r = new CheckStackedHeadings(); 48 | List ad_list = r.evaluate(in_string); 49 | assertEquals(0, ad_list.size()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/CheckSubsectionSizeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.rules; 19 | 20 | import static org.junit.Assert.*; 21 | 22 | import java.util.List; 23 | import java.util.Scanner; 24 | 25 | import org.junit.Test; 26 | 27 | import ca.uqac.lif.textidote.Advice; 28 | import ca.uqac.lif.textidote.as.AnnotatedString; 29 | 30 | public class CheckSubsectionSizeTest 31 | { 32 | @Test 33 | public void test1() 34 | { 35 | AnnotatedString in_string = AnnotatedString.read(new Scanner(CheckSubsectionSizeTest.class.getResourceAsStream("data/test-subsec-1.tex"))); 36 | CheckSubsectionSize r = new CheckSubsectionSize(); 37 | r.setMinNumWords(40); 38 | List ad_list = r.evaluate(in_string); 39 | assertEquals(1, ad_list.size()); 40 | Advice ad = ad_list.get(0); 41 | assertEquals(17, in_string.getPosition(ad.getRange().getStart()).getLine()); 42 | } 43 | 44 | @Test 45 | public void test2() 46 | { 47 | AnnotatedString in_string = AnnotatedString.read(new Scanner(CheckSubsectionSizeTest.class.getResourceAsStream("data/test-subsec-2.tex"))); 48 | CheckSubsectionSize r = new CheckSubsectionSize(); 49 | r.setMinNumWords(40); 50 | List ad_list = r.evaluate(in_string); 51 | assertTrue(ad_list.isEmpty()); 52 | } 53 | 54 | @Test 55 | public void test3() 56 | { 57 | AnnotatedString in_string = AnnotatedString.read(new Scanner(CheckSubsectionSizeTest.class.getResourceAsStream("data/test-subsec-4.tex"))); 58 | CheckSubsectionSize r = new CheckSubsectionSize(); 59 | r.setMinNumWords(40); 60 | List ad_list = r.evaluate(in_string); 61 | assertFalse(ad_list.isEmpty()); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/CheckSubsectionsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.textidote.rules; 19 | 20 | import static org.junit.Assert.*; 21 | 22 | import java.util.List; 23 | import java.util.Scanner; 24 | 25 | import org.junit.Test; 26 | 27 | import ca.uqac.lif.textidote.Advice; 28 | import ca.uqac.lif.petitpoucet.function.strings.Range; 29 | import ca.uqac.lif.textidote.Rule; 30 | import ca.uqac.lif.textidote.as.AnnotatedString; 31 | 32 | public class CheckSubsectionsTest 33 | { 34 | @Test 35 | public void test1() 36 | { 37 | AnnotatedString in_string = AnnotatedString.read(new Scanner(CheckSubsectionsTest.class.getResourceAsStream("data/test-subsec-1.tex"))); 38 | Rule r = new CheckSubsections(); 39 | List ad_list = r.evaluate(in_string); 40 | assertEquals(1, ad_list.size()); 41 | Advice ad = ad_list.get(0); 42 | assertEquals(2, in_string.getPosition(ad.getRange().getStart()).getLine()); 43 | } 44 | 45 | @Test 46 | public void test2() 47 | { 48 | AnnotatedString in_string = AnnotatedString.read(new Scanner(CheckSubsectionsTest.class.getResourceAsStream("data/test-subsec-2.tex"))); 49 | Rule r = new CheckSubsections(); 50 | List ad_list = r.evaluate(in_string); 51 | assertTrue(ad_list.isEmpty()); 52 | } 53 | 54 | @Test 55 | public void test3() 56 | { 57 | AnnotatedString in_string = AnnotatedString.read(new Scanner(CheckSubsectionsTest.class.getResourceAsStream("data/test-subsec-3.tex"))); 58 | Rule r = new CheckSubsections(); 59 | List ad_list = r.evaluate(in_string); 60 | assertEquals(1, countAdviceWithLabel(ad_list, "sh:secorder")); 61 | } 62 | 63 | @Test 64 | public void testSingleSectionUsages() 65 | { 66 | assertTrue(new CheckSubsections().evaluate(new AnnotatedString("\\chapter{Foo}")).isEmpty()); 67 | assertTrue(new CheckSubsections().evaluate(new AnnotatedString("\\section{Foo}")).isEmpty()); 68 | assertTrue(new CheckSubsections().evaluate(new AnnotatedString("\\subsection{Foo}")).isEmpty()); 69 | assertTrue(new CheckSubsections().evaluate(new AnnotatedString("\\subsubsection{Foo}")).isEmpty()); 70 | assertTrue(new CheckSubsections().evaluate(new AnnotatedString("\\paragraph{Foo}")).isEmpty()); 71 | assertTrue(new CheckSubsections().evaluate(new AnnotatedString("\\part{Foo}")).isEmpty()); 72 | } 73 | 74 | @Test 75 | public void testExtensiveStressTest() 76 | { 77 | AnnotatedString in_string = AnnotatedString.read(new Scanner(CheckSubsectionsTest.class.getResourceAsStream("data/test-subsec-stress.tex"))); 78 | Rule r = new CheckSubsections(); 79 | List ad_list = r.evaluate(in_string); 80 | assertEquals(10, countAdviceWithLabel(ad_list, "sh:secskip")); 81 | assertEquals(0, countAdviceWithLabel(ad_list, "sh:nsubdiv")); 82 | assertEquals(0, countAdviceWithLabel(ad_list, "sh:secorder")); 83 | } 84 | 85 | @Test 86 | public void testSectionInfoToString() 87 | { 88 | assertEquals("\\chapter{} I1-8 (0)", new SectionInfo("chapter", new Range(1, 8)).toString()); 89 | } 90 | 91 | /** 92 | * Checks if the list of advice contains one with a given label 93 | * @param list The list of advice 94 | * @param The label to look for 95 | * @return the number of occurences of the label in the list 96 | */ 97 | protected static int countAdviceWithLabel(List list, String label) 98 | { 99 | int count = 0; 100 | for (Advice a : list) 101 | { 102 | if (a.getRule().getName().compareToIgnoreCase(label) == 0) 103 | { 104 | count++; 105 | } 106 | } 107 | return count; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/beamer.tex: -------------------------------------------------------------------------------- 1 | \documentclass{beamer} 2 | \begin{document} 3 | \begin{frame}[c] 4 | \frametitle{Background} 5 | \framesubtitle{Benefitial skew example} 6 | Clock and data are travelllling in the same direction 7 | \end{frame} 8 | \end{document} -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/childs/child-section-no-root.tex: -------------------------------------------------------------------------------- 1 | This is the child section. 2 | \include{childs/child-sibling} -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/childs/child-section.tex: -------------------------------------------------------------------------------- 1 | %!TEX root = ../root.tex 2 | 3 | This is the child section. 4 | \include{childs/child-sibling} -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/childs/child-sibling.tex: -------------------------------------------------------------------------------- 1 | %!TEX root = ../root.tex 2 | 3 | This is the child sibling section. -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/dictionary.txt: -------------------------------------------------------------------------------- 1 | 2 | FooBar -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sylvainhalle/textidote/becfe02d12c890a9f5d1b9316bddab19ac578548/Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/empty.txt -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/include-twice.tex: -------------------------------------------------------------------------------- 1 | \begin{document} 2 | \section{Introduction} 3 | 4 | \include{childs/child_section} 5 | \include{childs/child_section} 6 | \end{document} -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/input1.tex: -------------------------------------------------------------------------------- 1 | \section{Something} 2 | 3 | \subsection{Something else} 4 | 5 | Hello world. 6 | -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/input2.tex: -------------------------------------------------------------------------------- 1 | Foo bar 2 | -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/replace.txt: -------------------------------------------------------------------------------- 1 | text replacement -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/root.tex: -------------------------------------------------------------------------------- 1 | \begin{document} 2 | \section{Introduction} 3 | 4 | \include{childs/child_section} 5 | \end{document} -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/test-input1.tex: -------------------------------------------------------------------------------- 1 | \input{input1.tex} 2 | \input{input2.tex} -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/test-lt-1.tex: -------------------------------------------------------------------------------- 1 | The quick brown fox jumps over the lazy dog. -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/test-lt-2.tex: -------------------------------------------------------------------------------- 1 | The quick brown foagzx jumps over the lazy dog. 2 | -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/test-nobreak.tex: -------------------------------------------------------------------------------- 1 | First line \\ Second line -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/test-stacked-0.tex: -------------------------------------------------------------------------------- 1 | \begin{document} 2 | 3 | \chapter{Foo} 4 | 5 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum viverra 6 | lacinia dolor, et consectetur orci varius et. Etiam et massa sed turpis 7 | tristique laoreet. Suspendisse gravida consequat dolor, eget gravida turpis 8 | laoreet eget. Nunc commodo lectus nec dui iaculis, eget imperdiet augue dapibus. 9 | Fusce malesuada eros ante, sed finibus leo suscipit finibus. Etiam consequat 10 | cursus rhoncus. Vestibulum felis mi, malesuada sit amet lectus at, malesuada 11 | iaculis dui. Donec eget dui sit amet nisl auctor ornare. Pellentesque semper 12 | nibh ut lorem sollicitudin scelerisque. Fusce sem ligula, aliquet eu sapien id, 13 | cursus convallis ex. Praesent vulputate, ipsum ut lobortis ullamcorper, enim 14 | enim scelerisque nibh, sit amet tempus risus lacus in nulla. Nulla efficitur, 15 | neque sit amet luctus sagittis, nunc ex tempor neque, non vestibulum neque dui 16 | ut magna. In sit amet varius eros. Curabitur molestie varius dictum. 17 | 18 | \section{Bar} 19 | 20 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum viverra 21 | lacinia dolor, et consectetur orci varius et. 22 | 23 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum viverra 24 | lacinia dolor, et consectetur orci varius et. Etiam et massa sed turpis 25 | tristique laoreet. Suspendisse gravida consequat dolor, eget gravida turpis 26 | laoreet eget. Nunc commodo lectus nec dui iaculis, eget imperdiet augue dapibus. 27 | Fusce malesuada eros ante, sed finibus leo suscipit finibus. Etiam consequat 28 | cursus rhoncus. Vestibulum felis mi, malesuada sit amet lectus at, malesuada 29 | iaculis dui. Donec eget dui sit amet nisl auctor ornare. Pellentesque semper 30 | nibh ut lorem sollicitudin scelerisque. Fusce sem ligula, aliquet eu sapien id, 31 | cursus convallis ex. Praesent vulputate, ipsum ut lobortis ullamcorper, enim 32 | enim scelerisque nibh, sit amet tempus risus lacus in nulla. Nulla efficitur, 33 | neque sit amet luctus sagittis, nunc ex tempor neque, non vestibulum neque dui 34 | ut magna. In sit amet varius eros. Curabitur molestie varius dictum. 35 | 36 | 37 | \chapter{Baz} 38 | 39 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum viverra 40 | lacinia dolor, et consectetur orci varius et. Etiam et massa sed turpis 41 | tristique laoreet. Suspendisse gravida consequat dolor, eget gravida turpis 42 | laoreet eget. Nunc commodo lectus nec dui iaculis, eget imperdiet augue dapibus. 43 | Fusce malesuada eros ante, sed finibus leo suscipit finibus. Etiam consequat 44 | cursus rhoncus. Vestibulum felis mi, malesuada sit amet lectus at, malesuada 45 | iaculis dui. Donec eget dui sit amet nisl auctor ornare. Pellentesque semper 46 | nibh ut lorem sollicitudin scelerisque. Fusce sem ligula, aliquet eu sapien id, 47 | cursus convallis ex. Praesent vulputate, ipsum ut lobortis ullamcorper, enim 48 | enim scelerisque nibh, sit amet tempus risus lacus in nulla. Nulla efficitur, 49 | neque sit amet luctus sagittis, nunc ex tempor neque, non vestibulum neque dui 50 | ut magna. In sit amet varius eros. Curabitur molestie varius dictum. 51 | 52 | \end{document} -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/test-stacked-1.tex: -------------------------------------------------------------------------------- 1 | \begin{document} 2 | 3 | \chapter{Foo} 4 | 5 | \section{Bar} 6 | 7 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum viverra 8 | lacinia dolor, et consectetur orci varius et. 9 | 10 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum viverra 11 | lacinia dolor, et consectetur orci varius et. Etiam et massa sed turpis 12 | tristique laoreet. Suspendisse gravida consequat dolor, eget gravida turpis 13 | laoreet eget. Nunc commodo lectus nec dui iaculis, eget imperdiet augue dapibus. 14 | Fusce malesuada eros ante, sed finibus leo suscipit finibus. Etiam consequat 15 | cursus rhoncus. Vestibulum felis mi, malesuada sit amet lectus at, malesuada 16 | iaculis dui. Donec eget dui sit amet nisl auctor ornare. Pellentesque semper 17 | nibh ut lorem sollicitudin scelerisque. Fusce sem ligula, aliquet eu sapien id, 18 | cursus convallis ex. Praesent vulputate, ipsum ut lobortis ullamcorper, enim 19 | enim scelerisque nibh, sit amet tempus risus lacus in nulla. Nulla efficitur, 20 | neque sit amet luctus sagittis, nunc ex tempor neque, non vestibulum neque dui 21 | ut magna. In sit amet varius eros. Curabitur molestie varius dictum. 22 | 23 | 24 | \chapter{Baz} 25 | 26 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum viverra 27 | lacinia dolor, et consectetur orci varius et. Etiam et massa sed turpis 28 | tristique laoreet. Suspendisse gravida consequat dolor, eget gravida turpis 29 | laoreet eget. Nunc commodo lectus nec dui iaculis, eget imperdiet augue dapibus. 30 | Fusce malesuada eros ante, sed finibus leo suscipit finibus. Etiam consequat 31 | cursus rhoncus. Vestibulum felis mi, malesuada sit amet lectus at, malesuada 32 | iaculis dui. Donec eget dui sit amet nisl auctor ornare. Pellentesque semper 33 | nibh ut lorem sollicitudin scelerisque. Fusce sem ligula, aliquet eu sapien id, 34 | cursus convallis ex. Praesent vulputate, ipsum ut lobortis ullamcorper, enim 35 | enim scelerisque nibh, sit amet tempus risus lacus in nulla. Nulla efficitur, 36 | neque sit amet luctus sagittis, nunc ex tempor neque, non vestibulum neque dui 37 | ut magna. In sit amet varius eros. Curabitur molestie varius dictum. 38 | 39 | \end{document} -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/test-subsec-1.tex: -------------------------------------------------------------------------------- 1 | \begin{document} 2 | 3 | \chapter{Foo} 4 | 5 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum viverra 6 | lacinia dolor, et consectetur orci varius et. Etiam et massa sed turpis 7 | tristique laoreet. Suspendisse gravida consequat dolor, eget gravida turpis 8 | laoreet eget. Nunc commodo lectus nec dui iaculis, eget imperdiet augue dapibus. 9 | Fusce malesuada eros ante, sed finibus leo suscipit finibus. Etiam consequat 10 | cursus rhoncus. Vestibulum felis mi, malesuada sit amet lectus at, malesuada 11 | iaculis dui. Donec eget dui sit amet nisl auctor ornare. Pellentesque semper 12 | nibh ut lorem sollicitudin scelerisque. Fusce sem ligula, aliquet eu sapien id, 13 | cursus convallis ex. Praesent vulputate, ipsum ut lobortis ullamcorper, enim 14 | enim scelerisque nibh, sit amet tempus risus lacus in nulla. Nulla efficitur, 15 | neque sit amet luctus sagittis, nunc ex tempor neque, non vestibulum neque dui 16 | ut magna. In sit amet varius eros. Curabitur molestie varius dictum. 17 | 18 | \section{Bar} 19 | 20 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum viverra 21 | lacinia dolor, et consectetur orci varius et. 22 | 23 | \chapter{Baz} 24 | 25 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum viverra 26 | lacinia dolor, et consectetur orci varius et. Etiam et massa sed turpis 27 | tristique laoreet. Suspendisse gravida consequat dolor, eget gravida turpis 28 | laoreet eget. Nunc commodo lectus nec dui iaculis, eget imperdiet augue dapibus. 29 | Fusce malesuada eros ante, sed finibus leo suscipit finibus. Etiam consequat 30 | cursus rhoncus. Vestibulum felis mi, malesuada sit amet lectus at, malesuada 31 | iaculis dui. Donec eget dui sit amet nisl auctor ornare. Pellentesque semper 32 | nibh ut lorem sollicitudin scelerisque. Fusce sem ligula, aliquet eu sapien id, 33 | cursus convallis ex. Praesent vulputate, ipsum ut lobortis ullamcorper, enim 34 | enim scelerisque nibh, sit amet tempus risus lacus in nulla. Nulla efficitur, 35 | neque sit amet luctus sagittis, nunc ex tempor neque, non vestibulum neque dui 36 | ut magna. In sit amet varius eros. Curabitur molestie varius dictum. 37 | 38 | \end{document} -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/test-subsec-2.tex: -------------------------------------------------------------------------------- 1 | \begin{document} 2 | 3 | \chapter{Foo} 4 | 5 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum viverra 6 | lacinia dolor, et consectetur orci varius et. Etiam et massa sed turpis 7 | tristique laoreet. Suspendisse gravida consequat dolor, eget gravida turpis 8 | laoreet eget. Nunc commodo lectus nec dui iaculis, eget imperdiet augue dapibus. 9 | Fusce malesuada eros ante, sed finibus leo suscipit finibus. Etiam consequat 10 | cursus rhoncus. Vestibulum felis mi, malesuada sit amet lectus at, malesuada 11 | iaculis dui. Donec eget dui sit amet nisl auctor ornare. Pellentesque semper 12 | nibh ut lorem sollicitudin scelerisque. Fusce sem ligula, aliquet eu sapien id, 13 | cursus convallis ex. Praesent vulputate, ipsum ut lobortis ullamcorper, enim 14 | enim scelerisque nibh, sit amet tempus risus lacus in nulla. Nulla efficitur, 15 | neque sit amet luctus sagittis, nunc ex tempor neque, non vestibulum neque dui 16 | ut magna. In sit amet varius eros. Curabitur molestie varius dictum. 17 | 18 | \section{Bar} 19 | 20 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum viverra 21 | lacinia dolor, et consectetur orci varius et. Etiam et massa sed turpis 22 | tristique laoreet. Suspendisse gravida consequat dolor, eget gravida turpis 23 | laoreet eget. Nunc commodo lectus nec dui iaculis, eget imperdiet augue dapibus. 24 | Fusce malesuada eros ante, sed finibus leo suscipit finibus. Etiam consequat 25 | cursus rhoncus. Vestibulum felis mi, malesuada sit amet lectus at, malesuada 26 | iaculis dui. Donec eget dui sit amet nisl auctor ornare. Pellentesque semper 27 | nibh ut lorem sollicitudin scelerisque. Fusce sem ligula, aliquet eu sapien id, 28 | cursus convallis ex. Praesent vulputate, ipsum ut lobortis ullamcorper, enim 29 | enim scelerisque nibh, sit amet tempus risus lacus in nulla. Nulla efficitur, 30 | neque sit amet luctus sagittis, nunc ex tempor neque, non vestibulum neque dui 31 | ut magna. In sit amet varius eros. Curabitur molestie varius dictum. 32 | 33 | \section{Bar} 34 | 35 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum viverra 36 | lacinia dolor, et consectetur orci varius et. Etiam et massa sed turpis 37 | tristique laoreet. Suspendisse gravida consequat dolor, eget gravida turpis 38 | laoreet eget. Nunc commodo lectus nec dui iaculis, eget imperdiet augue dapibus. 39 | Fusce malesuada eros ante, sed finibus leo suscipit finibus. Etiam consequat 40 | cursus rhoncus. Vestibulum felis mi, malesuada sit amet lectus at, malesuada 41 | iaculis dui. Donec eget dui sit amet nisl auctor ornare. Pellentesque semper 42 | nibh ut lorem sollicitudin scelerisque. Fusce sem ligula, aliquet eu sapien id, 43 | cursus convallis ex. Praesent vulputate, ipsum ut lobortis ullamcorper, enim 44 | enim scelerisque nibh, sit amet tempus risus lacus in nulla. Nulla efficitur, 45 | neque sit amet luctus sagittis, nunc ex tempor neque, non vestibulum neque dui 46 | ut magna. In sit amet varius eros. Curabitur molestie varius dictum. 47 | 48 | \chapter{Baz} 49 | 50 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum viverra 51 | lacinia dolor, et consectetur orci varius et. Etiam et massa sed turpis 52 | tristique laoreet. Suspendisse gravida consequat dolor, eget gravida turpis 53 | laoreet eget. Nunc commodo lectus nec dui iaculis, eget imperdiet augue dapibus. 54 | Fusce malesuada eros ante, sed finibus leo suscipit finibus. Etiam consequat 55 | cursus rhoncus. Vestibulum felis mi, malesuada sit amet lectus at, malesuada 56 | iaculis dui. Donec eget dui sit amet nisl auctor ornare. Pellentesque semper 57 | nibh ut lorem sollicitudin scelerisque. Fusce sem ligula, aliquet eu sapien id, 58 | cursus convallis ex. Praesent vulputate, ipsum ut lobortis ullamcorper, enim 59 | enim scelerisque nibh, sit amet tempus risus lacus in nulla. Nulla efficitur, 60 | neque sit amet luctus sagittis, nunc ex tempor neque, non vestibulum neque dui 61 | ut magna. In sit amet varius eros. Curabitur molestie varius dictum. 62 | 63 | \end{document} -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/test-subsec-3.tex: -------------------------------------------------------------------------------- 1 | \begin{document} 2 | \paragraph{foo} 3 | \part{bar} 4 | \chapter{baz} -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/test-subsec-4.tex: -------------------------------------------------------------------------------- 1 | \documentclass[]{scrbook} 2 | \begin{document} 3 | \section{A} 4 | \chapter{B} 5 | \chapter{C} 6 | \end{document} -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/test-subsec-stress.tex: -------------------------------------------------------------------------------- 1 | % Write each section twice to prevent sh:nsubdiv 2 | \part{Foo} 3 | \section{Foo} 4 | \section{Foo} 5 | 6 | \part{Foo} 7 | \subsection{Foo} 8 | \subsection{Foo} 9 | 10 | \part{Foo} 11 | \subsubsection{Foo} 12 | \subsubsection{Foo} 13 | 14 | \part{Foo} 15 | \paragraph{Foo} 16 | \paragraph{Foo} 17 | 18 | \part{Foo} 19 | \chapter{Foo} 20 | \chapter{Foo} 21 | \subsection{Foo} 22 | \subsection{Foo} 23 | 24 | \part{Foo} 25 | \chapter{Foo} 26 | \chapter{Foo} 27 | \subsubsection{Foo} 28 | \subsubsection{Foo} 29 | 30 | \part{Foo} 31 | \chapter{Foo} 32 | \chapter{Foo} 33 | \paragraph{Foo} 34 | \paragraph{Foo} 35 | 36 | \part{Foo} 37 | \chapter{Foo} 38 | \chapter{Foo} 39 | \section{Foo} 40 | \section{Foo} 41 | \subsubsection{Foo} 42 | \subsubsection{Foo} 43 | 44 | \part{Foo} 45 | \chapter{Foo} 46 | \chapter{Foo} 47 | \section{Foo} 48 | \section{Foo} 49 | \paragraph{Foo} 50 | \paragraph{Foo} 51 | 52 | \part{Foo} 53 | \chapter{Foo} 54 | \chapter{Foo} 55 | \section{Foo} 56 | \section{Foo} 57 | \subsection{Foo} 58 | \subsection{Foo} 59 | \paragraph{Foo} 60 | \paragraph{Foo} 61 | 62 | \part{Foo} 63 | \chapter{Foo} 64 | \chapter{Foo} 65 | \section{Foo} 66 | \section{Foo} 67 | \subsection{Foo} 68 | \subsection{Foo} 69 | \subsubsection{Foo} 70 | \subsubsection{Foo} 71 | \paragraph{Foo} 72 | \paragraph{Foo} -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/test-text.txt: -------------------------------------------------------------------------------- 1 | This is a simple plain file. -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/test1.md: -------------------------------------------------------------------------------- 1 | Hello world! 2 | This is a simple Markdown file. -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/test1.tex: -------------------------------------------------------------------------------- 1 | \begin{itemize} 2 | \item Hello % This is a comment 3 | \item World 4 | \end{itemize} -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/test2.tex: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | %% Something that looks like a regular LaTeX file 3 | %% 4 | %% By me 5 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 6 | 7 | \documentclass{article} 8 | \usepackage[english]{babel} 9 | 10 | \begin{document} 11 | 12 | \title{My Paper} 13 | \maketitle 14 | 15 | A first paragraph with some stuff, some \textbf{bold text} and other things, like a \cite{my:paper} citation.The text is not evenly spaced. 16 | 17 | \section{A first section} 18 | 19 | Hello world! 20 | 21 | \begin{itemize} 22 | \item Hello % This is a comment 23 | \item World 24 | \end{itemize} 25 | 26 | \end{document} -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/test3.tex: -------------------------------------------------------------------------------- 1 | \begin{comment} 2 | My Paper 3 | A first paragraph with some stuff, some bold text and other things, like a [0] citation.The text is not evenly spaced. 4 | A first section 5 | Hello world! 6 | \end{comment} -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/test4.tex: -------------------------------------------------------------------------------- 1 | \begin{figure} 2 | \includegraphics{D:\Sylvain\file.pdf} 3 | \label{A} 4 | \end{figure} 5 | 6 | \begin{figure} 7 | \label{B} 8 | \end{figure} 9 | 10 | \begin{figure} 11 | \label{C} 12 | \end{figure} 13 | 14 | There is \ref{A} and \cref{B} and \Cref{C}. -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/test5.tex: -------------------------------------------------------------------------------- 1 | \begin{figure} 2 | \label{A} 3 | \end{figure} 4 | 5 | \begin{figure} 6 | \label{B} 7 | \end{figure} 8 | 9 | \begin{figure} 10 | \label{C} 11 | \end{figure} 12 | 13 | There is \ref{A} and \cref{B}. -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/textidote/rules/data/test6.tex: -------------------------------------------------------------------------------- 1 | \begin{figure} 2 | \label{A} 3 | \end{figure} 4 | 5 | \begin{figure} 6 | Foo 7 | \end{figure} 8 | 9 | \begin{figure} 10 | \label{C} 11 | \end{figure} 12 | 13 | There is \ref{A} and \cref{B}. -------------------------------------------------------------------------------- /Source/CoreTest/src/ca/uqac/lif/util/AnsiPrinterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | TeXtidote, a linter for LaTeX documents 3 | Copyright (C) 2018-2019 Sylvain Hallé 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | package ca.uqac.lif.util; 19 | 20 | import static org.junit.Assert.*; 21 | 22 | import java.io.ByteArrayOutputStream; 23 | import java.util.List; 24 | import java.util.ArrayList; 25 | 26 | import org.junit.Test; 27 | 28 | import ca.uqac.lif.util.AnsiPrinter; 29 | import static ca.uqac.lif.textidote.as.AnnotatedString.CRLF; 30 | 31 | public class AnsiPrinterTest { 32 | 33 | @Test 34 | public void testColoredHelloWorld() { 35 | String in = "Colored World"; 36 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 37 | AnsiPrinter printer = new AnsiPrinter(baos); 38 | for (AnsiPrinter.Color color : AnsiPrinter.Color.values()) { 39 | printer.fg(color); 40 | printer.bg(color); 41 | } 42 | printer.setBackgroundColor(255, 255, 255); 43 | printer.setForegroundColor(222, 0, 0); 44 | printer.disableColors(); 45 | printer.fg(AnsiPrinter.Color.LIGHT_RED); 46 | printer.bg(AnsiPrinter.Color.WHITE); 47 | printer.println(in); 48 | String output = new String(baos.toByteArray()); 49 | assertContains(in, output); 50 | } 51 | 52 | @Test 53 | public void testWhiteOnBlackHelloWorld() { 54 | String in = "White World"; 55 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 56 | AnsiPrinter printer = new AnsiPrinter(baos); 57 | printer.setWhiteOnBlack(); 58 | assertTrue(printer.colorsEnabled()); 59 | printer.disableColors(); 60 | assertFalse(printer.colorsEnabled()); 61 | printer.println(in); 62 | String output = new String(baos.toByteArray()); 63 | assertContains(in, output); 64 | } 65 | 66 | @Test 67 | public void testPadToLength() { 68 | String in = "Padded World"; 69 | int length = 15; 70 | String output; 71 | output = AnsiPrinter.padToLength(in, length); 72 | assertEquals(in, output.trim()); 73 | assertEquals(length, output.length()); 74 | assertEquals(" ", AnsiPrinter.padToLength(null, 3)); 75 | assertEquals(in, AnsiPrinter.padToLength(in, in.length())); 76 | assertEquals("Pad", AnsiPrinter.padToLength(in, 3)); 77 | } 78 | 79 | protected static void assertContains(String subs, String s) 80 | { 81 | assertTrue("Couldn't locate \""+subs+"\" inside \""+s+"\"", s.indexOf(subs)!=-1); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /config.xml: -------------------------------------------------------------------------------- 1 | 2 | 21 | 22 | 24 | 25 | 26 | TeXtidote 27 | 28 | 30 | Sylvain Hallé 31 | 32 | 33 | 0.9 34 | 35 | 38 | 8 39 | 40 | 41 | ca.uqac.lif.textidote.Main 42 | 43 | 44 | Source/Core/src 45 | Source/Core/bin 46 | Source/Core/dep 47 | docs/javadoc 48 | 49 | Source/CoreTest/src 50 | Source/CoreTest/bin 51 | 52 | 53 | 54 | 55 | textidote 56 | false 57 | true 58 | 59 | 60 | 63 | 64 | 65 | 68 | Bullwinkle 69 | ca.uqac.lif.bullwinkle.BnfParser 70 | 71 | https://github.com/sylvainhalle/Bullwinkle/releases/download/v1.4.6/bullwinkle-1.4.6.zip 72 | 73 | true 74 | 75 | 76 | 79 | Language Tool 80 | org.languagetool.JLanguageTool 81 | 82 | https://github.com/sylvainhalle/languagetool-bundle/releases/download/v5.6-patched/languagetool-bundle-5.6-patched.jar 83 | 84 | true 85 | 86 | 87 | 90 | json-lif 91 | ca.uqac.lif.json.JsonElement 92 | 93 | https://github.com/liflab/json-lif/releases/download/v1.6.4/json-lif-1.6.4.zip 94 | 95 | true 96 | 97 | 98 | 101 | Petit Poucet (Core) 102 | ca.uqac.lif.petitpoucet.Part 103 | 104 | https://github.com/liflab/petitpoucet/releases/download/v2.3.2/petitpoucet-core-2.3.2.jar 105 | 106 | true 107 | 108 | 109 | 112 | Petit Poucet (Functions) 113 | ca.uqac.lif.petitpoucet.function.Function 114 | 115 | https://github.com/liflab/petitpoucet/releases/download/v2.3.2/petitpoucet-functions-2.3.2.jar 116 | 117 | true 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /dictionary.txt: -------------------------------------------------------------------------------- 1 | textbox 2 | Cornipickle 3 | Cornipickle’s 4 | Cornipickle's 5 | runtime 6 | Runtime 7 | CSS 8 | HTML -------------------------------------------------------------------------------- /docs/assets/css/responsive.css: -------------------------------------------------------------------------------- 1 | /* Small Devices, Tablets */ 2 | 3 | @media only screen and (min-width: 768px) { 4 | 5 | .header-right { 6 | display: block; 7 | float: right; 8 | padding: 6px 20px; 9 | } 10 | 11 | .header-headline { 12 | text-align: left; 13 | max-width: 380px; 14 | padding-top: 75px; 15 | } 16 | 17 | .header-running-text { 18 | line-height: 30px; 19 | margin: 0; 20 | text-align: left; 21 | } 22 | 23 | .hero { 24 | padding-bottom: 230px; 25 | } 26 | 27 | .hero-button { 28 | margin-top: 30px; 29 | margin-bottom: 30px; 30 | } 31 | 32 | .hero-right { 33 | text-align: left; 34 | } 35 | 36 | .ipad { 37 | display: block; 38 | position: absolute; 39 | right: 100px; 40 | z-index: 1; 41 | height: 427px; 42 | margin-top: 45px; 43 | transform: rotate(-6deg); 44 | max-width: 45%; 45 | border-radius: 25px; 46 | background: #fff; 47 | box-shadow: inset 0 4px 7px 1px #fff, inset 0 -5px 20px rgba(173, 186, 204, .25), 0 0px 33px rgba(0, 21, 64, .14), 0 10px 20px rgba(0, 21, 64, .05); 48 | } 49 | 50 | .ipad-screen { 51 | display: block; 52 | padding-bottom: 40px; 53 | margin-top: 19px; 54 | right: 1px; 55 | } 56 | 57 | .feature-section { 58 | padding-top: 60px; 59 | } 60 | 61 | .features-headline { 62 | color: white; 63 | padding-bottom: 70px; 64 | } 65 | 66 | .logos { 67 | padding: 30px; 68 | max-width: 100%; 69 | } 70 | 71 | .white-section-text { 72 | text-align: justify; 73 | padding-top: 80px; 74 | padding-left: 100px; 75 | } 76 | 77 | .imac-section-desc { 78 | font-weight: 300; 79 | font-size: 15px; 80 | line-height: 30px; 81 | margin: inherit; 82 | text-align: left; 83 | } 84 | 85 | .pricing-table { 86 | padding: 70px 0 100px; 87 | max-width: 1050px; 88 | margin: auto; 89 | } 90 | 91 | } 92 | 93 | .team { 94 | padding-bottom: 130px; 95 | } 96 | 97 | .team-member { 98 | text-align: center; 99 | } 100 | 101 | .email-form-div { 102 | max-width: inherit; 103 | margin: auto; 104 | } 105 | 106 | .webscope { 107 | margin-top: 0; 108 | } 109 | 110 | .social-links { 111 | float: right; 112 | } 113 | 114 | /* Medium Devices, Desktops */ 115 | 116 | @media only screen and (min-width: 992px) { 117 | } 118 | 119 | /* Large Devices, Wide Screens */ 120 | 121 | @media only screen and (min-width: 1200px) { 122 | } 123 | -------------------------------------------------------------------------------- /docs/assets/images/Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sylvainhalle/textidote/becfe02d12c890a9f5d1b9316bddab19ac578548/docs/assets/images/Screenshot.png -------------------------------------------------------------------------------- /docs/assets/images/Shape.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Shape 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /docs/assets/images/card_back.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sylvainhalle/textidote/becfe02d12c890a9f5d1b9316bddab19ac578548/docs/assets/images/card_back.jpg -------------------------------------------------------------------------------- /docs/assets/images/card_front.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sylvainhalle/textidote/becfe02d12c890a9f5d1b9316bddab19ac578548/docs/assets/images/card_front.jpg -------------------------------------------------------------------------------- /docs/assets/images/customizable.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | customizable 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/assets/images/facebook.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sylvainhalle/textidote/becfe02d12c890a9f5d1b9316bddab19ac578548/docs/assets/images/facebook.jpg -------------------------------------------------------------------------------- /docs/assets/images/facebook.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | facebook 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /docs/assets/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sylvainhalle/textidote/becfe02d12c890a9f5d1b9316bddab19ac578548/docs/assets/images/favicon.png -------------------------------------------------------------------------------- /docs/assets/images/flowchart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sylvainhalle/textidote/becfe02d12c890a9f5d1b9316bddab19ac578548/docs/assets/images/flowchart.png -------------------------------------------------------------------------------- /docs/assets/images/mouse.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | mouse 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /docs/assets/images/nova.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sylvainhalle/textidote/becfe02d12c890a9f5d1b9316bddab19ac578548/docs/assets/images/nova.png -------------------------------------------------------------------------------- /docs/assets/images/responsive.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | responsive 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /docs/assets/images/strips.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sylvainhalle/textidote/becfe02d12c890a9f5d1b9316bddab19ac578548/docs/assets/images/strips.png -------------------------------------------------------------------------------- /docs/assets/images/textidote.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 60 | 63 | 67 | 71 | 75 | 79 | 83 | 87 | 91 | 95 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /docs/assets/images/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | twitter 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /docs/assets/js/script.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function () { 2 | 3 | var myVideo = document.getElementById("video1"); 4 | 5 | function playPause() { 6 | if (myVideo.paused) 7 | myVideo.play(); 8 | else 9 | myVideo.pause(); 10 | } 11 | 12 | function makeBig() { 13 | myVideo.width = 560; 14 | } 15 | 16 | function makeSmall() { 17 | myVideo.width = 320; 18 | } 19 | 20 | function makeNormal() { 21 | myVideo.width = 420; 22 | } 23 | 24 | //Mouse click scroll 25 | $(document).ready(function () { 26 | $(".mouse").click(function () { 27 | $('html, body').animate({scrollTop: '+=750px'}, 1200); 28 | }); 29 | }); 30 | 31 | //Features appearance 32 | $(window).scroll(function () { 33 | var scroll = $(window).scrollTop(); 34 | 35 | //>=, not <= 36 | if (scroll >= 500) { 37 | $(".feature-icon").addClass("feature-display"); 38 | $(".feature-head-text").addClass("feature-display"); 39 | $(".feature-subtext").addClass("feature-display"); 40 | } 41 | }); 42 | 43 | //Subscribe to newsletter 44 | $('#email-form').on('submit', function (e) { 45 | e.preventDefault(); 46 | 47 | 48 | $('#newsletter-spinner').css("display", "inline-block"); 49 | 50 | var data = { 51 | email: $('#newsletter-email-input').val() 52 | } 53 | 54 | $.ajax({ 55 | url: "/mailchimp.php", 56 | type: 'POST', 57 | data: data, 58 | success: function (data) { 59 | 60 | console.log(data); 61 | $('#newsletter-spinner').css("display", "none"); 62 | $('#newsletter-loading-div').html("Success! Cool things are on their way") 63 | $('#newsletter-email-input').val("") 64 | 65 | window.location.href = '/nova.zip'; 66 | }, 67 | error: function (error) { 68 | console.log(error); 69 | $('#newsletter-spinner').fadeOut() 70 | } 71 | }); 72 | }) 73 | 74 | 75 | //smooth scrolling 76 | 77 | $('a[href*="#"]:not([href="#"])').click(function () { 78 | if (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') && location.hostname == this.hostname) { 79 | var target = $(this.hash); 80 | target = target.length ? target : $('[name=' + this.hash.slice(1) + ']'); 81 | if (target.length) { 82 | $('html, body').animate({ 83 | scrollTop: target.offset().top 84 | }, 1000); 85 | return false; 86 | } 87 | } 88 | }); 89 | 90 | 91 | }) 92 | -------------------------------------------------------------------------------- /example.tex: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | %% Something that looks like a regular LaTeX file 3 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 4 | 5 | \documentclass{article} 6 | \usepackage[english]{babel} 7 | 8 | \begin{document} 9 | 10 | \title{My Paper} 11 | \maketitle 12 | 13 | A first paragraph with some stuff, some \textbf{bold text} and other things, like a citation\cite{my:paper} .The text is not evenly spaced. 14 | 15 | Hello world! The tex also, contains error of \emph{gramar and and spelling}. or use this text too see an few of of the problems that LanguageTool can detecd. What do you thinks of grammar checkers? Please not that they are not perfect. Style issues get a blue marker: It's 5 P.M. in the afternoon. LanguageTool 3.8 was released on Thursday, 27 June 2017. 16 | 17 | \begin{figure} 18 | \includegraphics{C:/Sylvain/my_fig} 19 | \caption{The figure's caption} 20 | \label{fig:thisfig1} 21 | \end{figure} 22 | 23 | \section{a first section} 24 | 25 | Here,I forget to put a space after a comma. I refer to \ref{fig:thisfig1} in the text, but I do not refer to the second one. \\ 26 | I can mention a filename such as \verb+myfile.txt+; some rules don't apply to this special markup; however, if I write myfile.txt, I'm likely to get a warning about a period not followed by a space. 27 | 28 | \begin{equation*} 29 | 4.3 \times 8.4 = foo a.b 30 | \end{equation*} 31 | 32 | \begin{itemize} 33 | \item Hello % This is a comment 34 | \item World 35 | \end{itemize} 36 | 37 | \subsection{ My subsection. } 38 | 39 | Some other text, part of which is <>, very "special", like $4 \times 5 = 21$. 40 | 41 | % \begin{figure} 42 | % \includegraphics{my_fig} 43 | % \caption{The figure's caption} 44 | % \label{fig:thisfig2} 45 | % \end{figure} 46 | 47 | \begin{lstlisting} 48 | import Cowtest from 'cowtest'; 49 | 50 | const seedUrl = "http://127.0.0.1:8080/"; 51 | const tests = `${__dirname}/test.js`; 52 | const connector = "ava"; 53 | const report = "html"; 54 | const dataSaveMethod = { method: 'jsonl', coStr:`${__dirname}/data.jsonl` } 55 | 56 | Cowtest({ 57 | seedUrl, 58 | tests, 59 | connector, 60 | dataSaveMethod, 61 | report 62 | }); 63 | \end{lstlisting} 64 | 65 | \section{ THIS TITLE IS IN CAPS AND SHOULD NOT} 66 | 67 | %%% I noticed these lines caused an infinite loop to occur in textidote 68 | %%% leaving here for testing purposes: 69 | 70 | %% 71 | %% The "author" command and its associated commands are used to define 72 | %% the authors and their affiliations. 73 | %% Of note is the shared affiliation of the first two authors, and the 74 | %% "authornote" and "authornotemark" commands 75 | %% used to denote shared contribution to the research. 76 | \author{Some Author} 77 | % Specifically, this line: 78 | \email{asdfgh.zxcvbnm@123.12345678.ca} 79 | 80 | \end{document} 81 | %% :wrap=soft: -------------------------------------------------------------------------------- /mymap.txt: -------------------------------------------------------------------------------- 1 | L1C1-L1C8=L12C8-L12C15 2 | L1C9-L1C8=L12C17-L12C16 3 | L2C1-L2C40=L15C1-L15C40 4 | L2C41-L2C49=L15C49-L15C57 5 | L2C50-L2C39=L15C51-L15C40 6 | L2C50-L2C85=L15C59-L15C94 7 | L2C89-L2C121=L15C110-L15C142 8 | L2C90-L2C49=L15C110-L15C69 9 | L2C97-L2C39=L15C110-L15C52 10 | L2C98-L2C40=L15C110-L15C52 11 | L3C1-L3C55=L17C1-L17C55 12 | L3C57-L3C141=L17C74-L17C158 13 | L3C145-L3C292=L17C176-L17C323 14 | L3C162-L3C55=L17C176-L17C69 15 | L4C1-L4C15=L25C10-L25C24 16 | L4C16-L4C15=L25C26-L25C25 17 | L5C1-L5C45=L27C1-L27C45 18 | L5C46-L5C68=L27C52-L27C74 19 | L5C69-L5C44=L27C70-L27C45 20 | L5C69-L5C69=L27C76-L27C76 21 | L6C1-L6C7=L34C7-L34C13 22 | L7C1-L7C5=L35C9-L35C13 23 | L8C1-L8C16=L38C13-L38C28 24 | L8C17-L8C16=L38C30-L38C29 25 | L9C1-L9C67=L40C1-L40C67 26 | L9C70-L9C70=L40C86-L40C86 27 | L10C1-L10C37=L66C10-L66C46 28 | L10C38-L10C37=L66C48-L66C47 29 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | # must be unique in a given SonarQube instance 2 | sonar.projectKey=sylvainhalle_textidote 3 | # this is the name and version displayed in the SonarQube UI. Was mandatory prior to SonarQube 6.1. 4 | sonar.projectName=TeXtidote 5 | sonar.projectVersion=0.8.2 6 | sonar.organization=sylvainhalle-github 7 | 8 | # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. 9 | # Since SonarQube 4.2, this property is optional if sonar.modules is set. 10 | # If not set, SonarQube starts looking for source code from the directory containing 11 | # the sonar-project.properties file. 12 | sonar.sources=Source/Core 13 | 14 | # Encoding of the source code. Default is default system encoding 15 | #sonar.sourceEncoding=UTF-8 16 | 17 | # We write Java 6 code 18 | sonar.java.source=1.6 19 | 20 | # Configure Sonar to use JaCoCo 21 | sonar.coverage.jacoco.xmlReportPaths=tests/junit/report.xml 22 | # http://stackoverflow.com/a/27782177 23 | sonar.java.binaries=Source/Core/bin 24 | 25 | # Ignore a few rules 26 | sonar.issue.ignore.multicriteria=e1,e2,e3,e4 27 | 28 | # Field names should comply with a naming convention 29 | sonar.issue.ignore.multicriteria.e1.ruleKey=squid:S00116 30 | sonar.issue.ignore.multicriteria.e1.resourceKey=**/*.java 31 | 32 | # Variable names should comply with a naming convention 33 | sonar.issue.ignore.multicriteria.e2.ruleKey=squid:S00117 34 | sonar.issue.ignore.multicriteria.e2.resourceKey=**/*.java 35 | 36 | # Constants should comply with a naming convention 37 | sonar.issue.ignore.multicriteria.e3.ruleKey=squid:S00115 38 | sonar.issue.ignore.multicriteria.e3.resourceKey=**/*.java 39 | 40 | # Static fields should comply with a naming convention 41 | sonar.issue.ignore.multicriteria.e4.ruleKey=squid:S3008 42 | sonar.issue.ignore.multicriteria.e4.resourceKey=**/*.java 43 | 44 | # Do not analyze test and example files 45 | sonar.exclusions=**/examples/*.java,**/*Test.java 46 | 47 | # This hack makes the analysis work in Travis 48 | # https://travis-ci.community/t/sonar-scanner-all-java-files-excluded-in-java-project/11539/3 49 | sonar.javascript.exclusions= 50 | sonar.typescript.exclusions= --------------------------------------------------------------------------------