diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bf76132..a360a55 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,8 +7,6 @@ jobs: ruby: [2.4, 2.5, 2.6, 2.7] gemfile: - gemfiles/jekyll_4.0.gemfile - - gemfiles/jekyll_3.8.gemfile - - gemfiles/jekyll_3.7.gemfile exclude: - ruby: 2.4 gemfile: gemfiles/jekyll_4.0.gemfile diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..f4f9b9d --- /dev/null +++ b/.travis.yml @@ -0,0 +1,22 @@ +language: ruby +cache: bundler +env: + global: + - CC_TEST_REPORTER_ID=6b81e393ea6ad38560386f650ea2fb0e57a7beb5e20f8c8364fabee30d5bff07 +rvm: + - 2.4.6 + - 2.5.5 + - 2.6.3 +# gemfile is generated by appraisal +gemfile: + - gemfiles/jekyll_3.8.gemfile + - gemfiles/jekyll_3.7.gemfile + - gemfiles/jekyll_3.6.gemfile +before_script: + # Download cc-test-reporter + - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter + - chmod +x ./cc-test-reporter + - ./cc-test-reporter before-build +after_success: + - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT + - bundle exec codeclimate-test-reporter diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..b672130 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,18 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "name": "Rake fd:test", + "type": "Ruby", + "request": "launch", + "cwd": "${workspaceRoot}", + "program": "${workspaceRoot}/bin/rake", + "useBundler": true, + "args": ["fd:test"] + } + ] +} \ No newline at end of file diff --git a/.whitesource b/.whitesource new file mode 100644 index 0000000..e0aaa3e --- /dev/null +++ b/.whitesource @@ -0,0 +1,8 @@ +{ + "checkRunSettings": { + "vulnerableCheckRunConclusionLevel": "failure" + }, + "issueSettings": { + "minSeverityLevel": "LOW" + } +} \ No newline at end of file diff --git a/Appraisals b/Appraisals index b84be41..049a0a8 100644 --- a/Appraisals +++ b/Appraisals @@ -1,13 +1,5 @@ -# frozen_string_literal: true - -appraise 'jekyll-4.0' do - gem 'jekyll', '4.0' -end - -appraise 'jekyll-3.8' do - gem 'jekyll', '3.8' -end - -appraise 'jekyll-3.7' do - gem 'jekyll', '3.7' -end +# frozen_string_literal: true + +appraise 'jekyll-4.0' do + gem 'jekyll', '4.0' +end diff --git a/LICENSE.md b/LICENSE.md index 5763175..d27c0bd 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2020 Toshimaru +Copyright (c) 2020 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index e85fc30..e8cdf60 100644 --- a/README.md +++ b/README.md @@ -1,247 +1,258 @@ -# jekyll-toc - -![CI](https://github.com/toshimaru/jekyll-toc/workflows/CI/badge.svg) -[![Gem Version](https://badge.fury.io/rb/jekyll-toc.svg)](http://badge.fury.io/rb/jekyll-toc) -[![Code Climate](https://codeclimate.com/github/toshimaru/jekyll-toc/badges/gpa.svg)](https://codeclimate.com/github/toshimaru/jekyll-toc) -[![Test Coverage](https://api.codeclimate.com/v1/badges/cd56b207f327603662a1/test_coverage)](https://codeclimate.com/github/toshimaru/jekyll-toc/test_coverage) - -## Table of Contents - -- [Installation](#installation) -- [Usage](#usage) - - [1. Basic Usage](#1-basic-usage) - - [2. Advanced Usage](#2-advanced-usage) -- [Generated HTML](#generated-html) -- [Default Configuration](#default-configuration) -- [Customization](#customization) - - [TOC levels](#toc-levels) - - [Skip TOC](#skip-toc) - - [Skip TOC Section](#skip-toc-section) - - [CSS Styling](#css-styling) - - [Custom CSS Class](#custom-css-class) - -## Installation - -Add jekyll-toc plugin in your site's `Gemfile`, and run `bundle install`. - -```ruby -gem 'jekyll-toc' -``` - -Add jekyll-toc to the `gems:` section in your site's `_config.yml`. - -```yml -plugins: - - jekyll-toc -``` - -Set `toc: true` in posts for which you want the TOC to appear. - -```yml ---- -layout: post -title: "Welcome to Jekyll!" -toc: true ---- -``` - -## Usage - -There are three Liquid filters, which can be applied to HTML content, -e.g. the Liquid variable `content` available in Jekyll's templates. - -### 1. Basic Usage - -#### `toc` filter - -Add the `toc` filter to your site's `{{ content }}` (e.g. `_layouts/post.html`). - -```liquid -{{ content | toc }} -``` - -This filter places the TOC directly above the content. - -### 2. Advanced Usage - -If you'd like separated TOC and content, you can use `{% toc %}` tag (or `toc_only` filter) and `inject_anchors` filter. - -#### `{% toc %}` tag - -Generates the TOC itself as described [below](#generated-html). -Mostly useful in cases where the TOC should _not_ be placed immediately -above the content but at some other place of the page, i.e. an aside. - -```html -
-
- {% toc %} -
-
- {{ content }} -
-
-``` - -:warning: **`{% toc %}` Tag Limitation** - -`{% toc %}` can be available only in [Jekyll Posts](https://jekyllrb.com/docs/step-by-step/08-blogging/) and [Jekyll Collections](https://jekyllrb.com/docs/collections/). If you'd like to use `{% toc %}` except posts or collections, please use `toc_only` filter as described below. - -```html -
-
- {{ content | toc_only }} -
-
- {{ content | inject_anchors }} -
-
-``` - -#### `inject_anchors` filter - -Injects HTML anchors into the content without actually outputting the TOC itself. -They are of the form: - -```html - -``` - -This is only useful when the TOC itself should be placed at some other -location with the `toc_only` filter. - -## Generated HTML - -jekyll-toc generates an unordered list. The HTML output is as follows. - -```html - -``` - -![screenshot](https://user-images.githubusercontent.com/803398/28401295-0dcfb7ca-6d54-11e7-892b-2f2e6ca755a7.png) - -## Default Configuration - -```yml -toc: - min_level: 1 - max_level: 6 - no_toc_section_class: no_toc_section - list_class: section-nav - sublist_class: '' - item_class: toc-entry - item_prefix: toc- -``` - -## Customization - -### TOC levels - -The toc levels can be configured on `_config.yml`: - -```yml -toc: - min_level: 2 # default: 1 - max_level: 5 # default: 6 -``` - -The default level range is `

` to `

`. - -### Skip TOC - -The heading is ignored in the toc when you add `no_toc` to the class. - -```html -

h1

-

This heading is ignored in the toc

-

h2

-``` - -### Skip TOC Section - -The headings are ignored inside the element which has `no_toc_section` class. - -```html -

h1

-
-

This heading is ignored in the toc

-

This heading is ignored in the toc

-
-

h4

-``` - -Which would result in only the `

` & `

` within the example being included in the TOC. - -The class can be configured on `_config.yml`: - -```yml -toc: - no_toc_section_class: exclude # default: no_toc_section -``` - -Configuring multiple classes for `no_toc_section_class` is allowed: - -```yml -toc: - no_toc_section_class: - - no_toc_section - - exclude - - your_custom_skip_class_name -``` - -### CSS Styling - -The toc can be modified with CSS. The sample CSS is the following. - -```css -.section-nav { - background-color: #fff; - margin: 5px 0; - padding: 10px 30px; - border: 1px solid #e8e8e8; - border-radius: 3px; -} -``` - -![screenshot](https://user-images.githubusercontent.com/803398/28401455-0ba60868-6d55-11e7-8159-0ae7591aee66.png) - -Each TOC `li` entry has two CSS classes for further styling. The general `toc-entry` is applied to all `li` elements in the `ul.section-nav`. - -Depending on the heading level each specific entry refers to, it has a second CSS class `toc-XX`, where `XX` is the HTML heading tag name. For example, the TOC entry linking to a heading `

...

` (a single -`#` in Markdown) will get the CSS class `toc-h1`. - -### Custom CSS Class - -You can apply custom CSS classes to the generated `\n) + i += nest_entries.count + end + # Add the closing tag for the current entry in the list + toc_list << %(\n) + elsif entry[:h_num] > min_h_num + # If the current entry should be indented in the list, generate a sublist + nest_entries = get_nest_entries(entries[i, entries.count], min_h_num) + toc_list << build_toc_list(nest_entries) + i += nest_entries.count - 1 + end + i += 1 + end + + toc_list + end + + # Returns the entries in a nested list + # The nested list starts at the first entry in entries (inclusive) + # The nested list ends at the first entry in entries with depth min_h_num or greater (exclusive) + def get_nest_entries(entries, min_h_num) + entries.inject([]) do |nest_entries, entry| + break nest_entries if entry[:h_num] == min_h_num + + nest_entries << entry + end + end + + def toc_headings + @configuration.toc_levels.map { |level| "h#{level}" }.join(',') + end + + def toc_headings_in_no_toc_section + if @configuration.no_toc_section_class.is_a? Array + @configuration.no_toc_section_class.map { |cls| toc_headings_within(cls) }.join(',') + else + toc_headings_within(@configuration.no_toc_section_class) + end + end + + def toc_headings_within(class_name) + @configuration.toc_levels.map { |level| ".#{class_name} h#{level}" }.join(',') + end + + def ul_attributes + @ul_attributes ||= @configuration.sublist_class.empty? ? '' : %( class="#{@configuration.sublist_class}") + end + end + end +end diff --git a/lib/version.rb b/lib/version.rb index 6a935cf..f9292c0 100644 --- a/lib/version.rb +++ b/lib/version.rb @@ -1,5 +1,5 @@ -# frozen_string_literal: true - -module JekyllToc - VERSION = '0.13.1' -end +# frozen_string_literal: true + +module JekyllToc + VERSION = '0.13.1' +end diff --git a/test/parser/test_inject_anchors_filter.rb b/test/parser/test_inject_anchors_filter.rb index c9158e5..9228a90 100644 --- a/test/parser/test_inject_anchors_filter.rb +++ b/test/parser/test_inject_anchors_filter.rb @@ -1,23 +1,23 @@ -# frozen_string_literal: true - -require 'test_helper' - -class TestInjectAnchorsFilter < Minitest::Test - include TestHelpers - - def setup - read_html_and_create_parser - end - - def test_injects_anchors_into_content - html = @parser.inject_anchors_into_html - - assert_match(%r{Simple H1}, html) - end - - def test_does_not_inject_toc - html = @parser.inject_anchors_into_html - - assert_nil(/