diff --git a/assets/javascripts/vendor/prism.js b/assets/javascripts/vendor/prism.js index 1f4a1b70bf..96519c4a8d 100644 --- a/assets/javascripts/vendor/prism.js +++ b/assets/javascripts/vendor/prism.js @@ -1,5 +1,5 @@ /* PrismJS 1.30.0 -https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+bash+c+cpp+cmake+coffeescript+crystal+d+dart+diff+django+elixir+erlang+go+groovy+java+json+julia+kotlin+latex+lua+markdown+markup-templating+matlab+nginx+nim+nix+ocaml+perl+php+python+qml+r+jsx+ruby+rust+scss+scala+shell-session+sql+tcl+typescript+yaml+zig */ +https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+bash+c+cpp+cmake+coffeescript+crystal+d+dart+diff+django+dot+elixir+erlang+go+groovy+java+json+julia+kotlin+latex+lua+markdown+markup-templating+matlab+nginx+nim+nix+ocaml+perl+php+python+qml+r+jsx+ruby+rust+scss+scala+shell-session+sql+tcl+typescript+yaml+zig */ /// var _self = (typeof window !== 'undefined') @@ -2929,6 +2929,83 @@ Prism.languages.insertBefore('d', 'function', { }(Prism)); +// https://www.graphviz.org/doc/info/lang.html + +(function (Prism) { + + var ID = '(?:' + [ + // an identifier + /[a-zA-Z_\x80-\uFFFF][\w\x80-\uFFFF]*/.source, + // a number + /-?(?:\.\d+|\d+(?:\.\d*)?)/.source, + // a double-quoted string + /"[^"\\]*(?:\\[\s\S][^"\\]*)*"/.source, + // HTML-like string + /<(?:[^<>]|(?!)*>/.source + ].join('|') + ')'; + + var IDInside = { + 'markup': { + pattern: /(^<)[\s\S]+(?=>$)/, + lookbehind: true, + alias: ['language-markup', 'language-html', 'language-xml'], + inside: Prism.languages.markup + } + }; + + /** + * @param {string} source + * @param {string} flags + * @returns {RegExp} + */ + function withID(source, flags) { + return RegExp(source.replace(//g, function () { return ID; }), flags); + } + + Prism.languages.dot = { + 'comment': { + pattern: /\/\/.*|\/\*[\s\S]*?\*\/|^#.*/m, + greedy: true + }, + 'graph-name': { + pattern: withID(/(\b(?:digraph|graph|subgraph)[ \t\r\n]+)/.source, 'i'), + lookbehind: true, + greedy: true, + alias: 'class-name', + inside: IDInside + }, + 'attr-value': { + pattern: withID(/(=[ \t\r\n]*)/.source), + lookbehind: true, + greedy: true, + inside: IDInside + }, + 'attr-name': { + pattern: withID(/([\[;, \t\r\n])(?=[ \t\r\n]*=)/.source), + lookbehind: true, + greedy: true, + inside: IDInside + }, + 'keyword': /\b(?:digraph|edge|graph|node|strict|subgraph)\b/i, + 'compass-point': { + pattern: /(:[ \t\r\n]*)(?:[ewc_]|[ns][ew]?)(?![\w\x80-\uFFFF])/, + lookbehind: true, + alias: 'builtin' + }, + 'node': { + pattern: withID(/(^|[^-.\w\x80-\uFFFF\\])/.source), + lookbehind: true, + greedy: true, + inside: IDInside + }, + 'operator': /[=:]|-[->]/, + 'punctuation': /[\[\]{};,]/ + }; + + Prism.languages.gv = Prism.languages.dot; + +}(Prism)); + Prism.languages.elixir = { 'doc': { pattern: /@(?:doc|moduledoc)\s+(?:("""|''')[\s\S]*?\1|("|')(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2)/, diff --git a/lib/docs/core/requester.rb b/lib/docs/core/requester.rb index 28b7db2d57..7413a506db 100644 --- a/lib/docs/core/requester.rb +++ b/lib/docs/core/requester.rb @@ -54,6 +54,12 @@ def build_and_queue_request(url, options = {}, &block) end def handle_response(response) + if ENV['RETRY'] == '1' && [0, 500, 501, 502, 503, 504].include?(response.code.to_i) + instrument 'handle_response.retry', url: response.url do + build_and_queue_request(response.url) + end + return + end instrument 'handle_response.requester', url: response.url do on_response.each do |callback| result = callback.call(response) diff --git a/lib/docs/filters/graphviz/clean_html.rb b/lib/docs/filters/graphviz/clean_html.rb new file mode 100644 index 0000000000..86c4824024 --- /dev/null +++ b/lib/docs/filters/graphviz/clean_html.rb @@ -0,0 +1,47 @@ +module Docs + class Graphviz + class CleanHtmlFilter < Filter + def call + css('[tabindex]').remove_attribute('tabindex') + + content = at_css('.td-content') + @doc = content if content + + css('a:contains("Search the Graphviz codebase")').remove + css('.td-page-meta__lastmod').remove + + css('pre:has(code)').each do |node| + pre = Nokogiri::XML::Node.new('pre', @doc) + code = node.at_css('code') + + if code['data-lang'] + # Syntax highlighting is embedded into this HTML markup. + pre['data-language'] = code['data-lang'] + else + # Plain example source-code without highlighting. + # Let's guess the language. + sourcecode = code.content.strip + if sourcecode =~ /^\$/ + # Starts with '$'? Probably a shell session. + pre['data-language'] = 'shell-session' + elsif sourcecode =~ /^cmd / + # Command line example. No highlighting needed. + pre['data-language'] = '' + elsif sourcecode =~ /^void / + # C language. + pre['data-language'] = 'c' + else + # Nothing else? Let's guess DOT. + pre['data-language'] = 'dot' + end + end + pre.content = code.content + + node.replace(pre) + end + + doc + end + end + end +end diff --git a/lib/docs/filters/graphviz/entries.rb b/lib/docs/filters/graphviz/entries.rb new file mode 100644 index 0000000000..3f2fdfc95c --- /dev/null +++ b/lib/docs/filters/graphviz/entries.rb @@ -0,0 +1,28 @@ +module Docs + class Graphviz + class EntriesFilter < Docs::EntriesFilter + + def get_name + name = at_css('h1').content.strip + end + + def get_type + breadcrumbs = css('nav ol.breadcrumb li.breadcrumb-item') + category = breadcrumbs[1]&.content&.strip + + # These categories have several sub-pages. + return category if [ + 'Attribute Types', + 'Attributes', + 'Command Line', + 'Layout Engines', + 'Output Formats', + ].include?(category) + + # Several categories have only one page each. Let's group them together. + return 'Documentation' + end + + end + end +end diff --git a/lib/docs/scrapers/graphviz.rb b/lib/docs/scrapers/graphviz.rb new file mode 100644 index 0000000000..6b78d349cc --- /dev/null +++ b/lib/docs/scrapers/graphviz.rb @@ -0,0 +1,55 @@ +module Docs + class Graphviz < UrlScraper + self.name = 'Graphviz' + self.slug = 'graphviz' + self.type = 'simple' + + self.links = { + home: 'https://www.graphviz.org/', + code: 'https://gitlab.com/graphviz/graphviz' + } + + options[:container] = 'main' + + # These images are too large: + # 980KB https://www.graphviz.org/doc/info/plugins.png + # 650KB https://www.graphviz.org/Gallery/twopi/twopi2.svg + # All other files are under 100KB + options[:max_image_size] = 100_000 + + # TODO: the UrlScraper is very unreliable on this website. + # I often get several errors: + # - SSL connect error + # - Failure when receiving data from the peer + # - was slow to process (30s) + # Setting a :rate_limit doesn't help. + # We have to figure out a more reliable solution. + #options[:rate_limit] = 100 + + options[:attribution] = <<-HTML + © 2025 The Graphviz Authors
+ Licensed under the Eclipse Public License 1.0. + HTML + + html_filters.push 'graphviz/entries', 'graphviz/clean_html' + + self.release = '14.01' + self.base_url = 'https://www.graphviz.org/' + self.root_path = 'documentation/' + options[:only_patterns] = [ + /^documentation\//, + /^doc\//, + /^docs\//, + ] + options[:replace_paths] = { + # Redirections: + 'docs/outputs/cmap/' => 'docs/outputs/imap/', + 'doc/info/output.html' => 'docs/outputs/', + } + + def get_latest_version(opts) + tags = get_gitlab_tags('gitlab.com', 'graphviz', 'graphviz', opts) + tags[0]['name'] + end + end +end diff --git a/public/icons/docs/graphviz/16.png b/public/icons/docs/graphviz/16.png new file mode 100644 index 0000000000..bc8936906f Binary files /dev/null and b/public/icons/docs/graphviz/16.png differ diff --git a/public/icons/docs/graphviz/16@2x.png b/public/icons/docs/graphviz/16@2x.png new file mode 100644 index 0000000000..7027ab993f Binary files /dev/null and b/public/icons/docs/graphviz/16@2x.png differ diff --git a/public/icons/docs/graphviz/SOURCE b/public/icons/docs/graphviz/SOURCE new file mode 100644 index 0000000000..dc9fcad8e6 --- /dev/null +++ b/public/icons/docs/graphviz/SOURCE @@ -0,0 +1 @@ +https://gitlab.com/graphviz/graphviz.gitlab.io/-/blob/main/static/Resources/favicon.png