Import the pebble dev site into devsite/

This commit is contained in:
Katharine Berry 2025-02-17 17:02:33 -08:00
parent 3b92768480
commit 527858cf4c
1359 changed files with 265431 additions and 0 deletions

View file

@ -0,0 +1,153 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
module Pebble
# DocClass is a special type of DocElement for structs and unions.
# It acts like a DocGroup in that it parses an XML file, but it acts like a
# DocMember in that it belongs to a group etc.
class DocClass < DocElement
attr_reader :summary, :description, :kind, :position, :id, :name
def initialize(root, dir, platform, kind, id, group)
super(root, platform)
@dir = dir
@group = group
@kind = kind
@children = []
@xml = {}
@code = id
@doxygen_processor = DoxygenProcessor.new(platform)
load_xml(platform)
end
def load_xml(platform)
@xml[platform] = Nokogiri::XML(File.read("#{@dir}/#{platform}/xml/#{@kind}_#{@code}.xml"))
@data[platform] = {}
@name = @xml[platform].at_css('compounddef > compoundname').content.to_s
@id = @xml[platform].at_css('compounddef')['id']
@path = "#{@group.path}##{@name}"
create_members(platform)
end
def to_liquid
{
'name' => @name,
'summary' => @summary,
'description' => @description,
'url' => url,
'children' => @children,
'data' => @data,
'platforms' => @xml.keys,
'uniform' => uniform?
}
end
def process(mapping)
@xml.each do |platform, xml|
@data[platform]['summary'] = @doxygen_processor.process_summary(
xml.at_css('compounddef > briefdescription'), mapping
)
description = xml.at_css('compounddef > detaileddescription')
process_simplesects(description, mapping, platform)
@data[platform]['description'] = @doxygen_processor.process_description(
description, mapping)
process_members(mapping, platform)
end
@children = @children.reject { |child| child.name.match(/^@/) }
@children.sort! { |m, n| m.position <=> n.position }
end
def uniform?
identical = @data['aplite'].to_json == @data['basalt'].to_json
identical &&= @children.all?(&:uniform?)
identical
end
private
def create_members(platform)
@xml[platform].css('memberdef').each do |child|
variable = DocClassVariable.new(@root, child, @group, platform)
existing = @children.select { |ex| ex.name == variable.name }.first
if existing.nil?
@children << variable
else
existing.add_platform(platform, child)
end
end
end
def process_members(mapping, platform)
@children.each { |child| child.process(mapping, platform) }
end
end
# DocClassVariable is a DocElement subclass that handles the members (or
# variables) of a struct or union.
class DocClassVariable < DocElement
attr_reader :name, :position
def initialize(root, node, group, platform)
super(root, platform)
@name = node.at_css('name').content.to_s
@group = group
@nodes = { platform => node }
@platforms = [platform]
@path = "#{@group.path}##{@name}"
@position = node.at_css(' > location')['line'].to_i
end
def add_platform(platform, node)
@nodes[platform] = node
@platforms << platform
@data[platform] = {}
end
def to_liquid
{
'name' => @name,
'data' => @data,
'url' => url,
'type' => @type,
'platforms' => @platforms
}
end
def uniform?
@data['aplite'].to_json == @data['basalt'].to_json
end
def process(mapping, platform)
return unless @platforms.include? platform
@data[platform]['summary'] = @doxygen_processor.process_summary(
@nodes[platform].at_css('briefdescription'), mapping
)
description = @nodes[platform].at_css('detaileddescription')
process_simplesects(description, mapping, platform)
process_type(mapping, platform)
@data[platform]['description'] = @doxygen_processor.process_description(
description, mapping)
end
def process_type(mapping, platform)
if @nodes[platform].at_css('type > ref').nil?
@data[platform]['type'] = @nodes[platform].at_css('type').content.to_s
else
type_node = @nodes[platform].at_css('type').clone()
@doxygen_processor.process_node_ref(type_node.at_css('ref'), mapping)
@data[platform]['type'] = type_node.to_html.to_s
end
end
end
end

View file

@ -0,0 +1,98 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
module Pebble
# DocElement is an abstract C Documentation class that is subclasses for
# each of the various items that build up a documentation page, symbol or
# tree item.
class DocElement
KNOWN_SECT_TYPES = %w(return note)
attr_reader :url
def initialize(root, platform)
@root = root
@data = {}
@data[platform] = {}
@doxygen_processor = DoxygenProcessor.new(platform)
end
def to_symbol
{
name: @name,
url: url,
summary: default_data('summary')
}
end
def to_mapping
{
id: @id,
url: url
}
end
private
def default_data(key)
return @data['basalt'][key] unless @data['basalt'].nil? || @data['basalt'][key].nil?
return @data['aplite'][key] unless @data['aplite'].nil? || @data['aplite'][key].nil?
''
end
def url
"#{@root}#{@path}"
end
def add_data(type, value, platform)
@data[platform] = {} if @data[platform].nil?
@data[platform][type] = [] if @data[platform][type].nil?
@data[platform][type] << value
end
def process_simplesects(node, mapping, platform)
if node.name.to_s == 'detaileddescription'
desc = node
else
desc = node.at_css('detaileddescription')
end
return if desc.nil?
desc.css('simplesect').each do |sect|
if KNOWN_SECT_TYPES.include?(sect['kind'])
process_simplesect_basic(sect, mapping, platform)
elsif sect['kind'] == 'see'
process_simplesect_see(sect, mapping, platform)
end
end
end
def process_simplesect_basic(sect, mapping, platform)
value = @doxygen_processor.process_summary(sect.clone, mapping)
add_data(sect['kind'], value, platform)
sect.remove
end
def process_simplesect_see(sect, mapping, platform)
if sect.at_css('para > ref').nil?
add_data(sect['kind'],
@doxygen_processor.process_paragraph(sect.at_css('para'),
mapping), platform)
else
see_node = sect.at_css('para > ref').clone
@doxygen_processor.process_node_ref(see_node, mapping)
add_data(sect['kind'], see_node.to_html.to_s, platform)
end
sect.remove
end
end
end

View file

@ -0,0 +1,58 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
module Pebble
# DocEnumValue is a DocElement that is one of the possible values of an enum.
class DocEnumValue < DocElement
attr_reader :position, :summary, :id, :platforms, :name, :data
def initialize(root, node, group, platform)
super(root, platform)
@name = node.at_css('name').content.to_s
@id = node['id']
@group = group
@path = "#{@group.path}##{@name}"
@nodes = { platform => node }
@platforms = [platform]
@doxygen_processor = DoxygenProcessor.new(platform)
end
def add_platform(node, platform)
@nodes[platform] = node
@platforms << platform
@data[platform] = {}
end
def process(mapping, platform)
return unless @platforms.include? platform
process_simplesects(@nodes[platform], mapping, platform)
@data[platform]['summary'] = @doxygen_processor.process_summary(
@nodes[platform].at_css('briefdescription'), mapping
)
end
def uniform?
data['aplite'].to_json == data['basalt'].to_json
end
def to_liquid
{
'name' => @name,
'data' => @data,
'url' => url,
'platforms' => @platforms
}
end
end
end

View file

@ -0,0 +1,178 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require_relative 'doc_element.rb'
require_relative 'doc_member.rb'
require_relative 'doc_class.rb'
require_relative 'doxygen_processor.rb'
module Pebble
# A DocGroup is a a collection of members, structs and subgroups that will
# become a page in the C documentation.
class DocGroup < DocElement
attr_accessor :groups, :members, :name, :path, :menu_path, :classes, :id
def initialize(root, dir, platform, id, menu_path = [])
super(root, platform)
@dir = dir
@menu_path = Array.new(menu_path)
@xml = {}
@groups = []
@members = []
@classes = []
@group_id = id
@doxygen_processor = DoxygenProcessor.new(platform)
@root = root
load_xml(platform)
end
def load_xml(platform)
@xml[platform] = Nokogiri::XML(File.read("#{@dir}/#{platform}/xml/group___#{@group_id}.xml"))
@id = @xml[platform].at_css('compounddef')['id']
@name = @xml[platform].at_css('compounddef > title').content.to_s
@menu_path << @name if @path.nil?
@path = @menu_path.join('/').gsub(' ', '_') + '/'
create_descendents(platform)
end
def process(mapping, platform)
return if @xml[platform].nil?
@data[platform] = {} if @data[platform].nil?
@data[platform]['summary'] = @doxygen_processor.process_summary(
@xml[platform].at_css('compounddef > briefdescription'), mapping)
description = @xml[platform].at_css('compounddef > detaileddescription')
process_simplesects(description, mapping, platform)
@data[platform]['description'] = @doxygen_processor.process_description(
description, mapping)
process_descendents(mapping, platform)
end
def to_page(site)
PageDocC.new(site, @root, site.source, "#{@path}index.html", self)
end
def to_branch
{
'name' => @name,
'url' => url,
'children' => @groups.map(&:to_branch),
'summary' => default_data('summary')
}
end
def mapping_array
mapping = [to_mapping]
@groups.each { |group| mapping += group.mapping_array }
@members.each { |member| mapping << member.to_mapping }
@classes.each { |cls| mapping << cls.to_mapping }
mapping
end
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
def to_liquid
{
'name' => @name,
'url' => url,
'path' => @menu_path,
'groups' => @groups,
'members' => @members,
'functions' => @members.select { |member| member.kind == 'function' },
'enums' => @members.select { |member| member.kind == 'enum' },
'defines' => @members.select { |member| member.kind == 'define' },
'typedefs' => @members.select { |member| member.kind == 'typedef' },
'structs' => @classes.select { |member| member.kind == 'struct' },
'unions' => @classes.select { |member| member.kind == 'union' },
'data' => @data,
'basalt_only' => !@xml.key?('aplite'),
'summary' => default_data('summary'),
'description' => default_data('description')
}
end
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
private
def create_descendents(platform)
create_inner_groups(platform)
create_members(platform)
create_inner_classes(platform)
@members.sort! { |m, n| m.position <=> n.position }
end
def create_inner_groups(platform)
@xml[platform].css('innergroup').each do |child|
id = child['refid'].sub(/^group___/, '')
new_group = DocGroup.new(@root, @dir, platform, id, @menu_path)
group = @groups.select { |grp| new_group.name == grp.name }.first
if group.nil?
@groups << new_group
else
group.load_xml(platform)
end
end
end
def create_members(platform)
@xml[platform].css('memberdef').map do |child|
new_member = DocMember.new(@root, child, self, platform)
member = @members.select { |mem| mem.name == new_member.name }.first
if member.nil?
@members << new_member
else
member.add_platform(platform, child)
end
end
end
def create_inner_classes(platform)
@xml[platform].css('innerclass').map do |child|
next if child.content.to_s.match(/__unnamed__/)
next if child.content.to_s.match(/\./)
if child['refid'].match(/^struct_/)
create_struct(child, platform)
elsif child['refid'].match(/^union_/)
create_union(child, platform)
end
end
end
def create_struct(node, platform)
id = node['refid'].sub(/^struct_/, '')
new_struct = DocClass.new(@root, @dir, platform, 'struct', id, self)
struct = @classes.select { |str| new_struct.name == str.name }.first
if struct.nil?
@classes << new_struct
else
struct.load_xml(platform)
end
end
def create_union(node, platform)
id = node['refid'].sub(/^union_/, '')
new_union = DocClass.new(@root, @dir, platform, 'union', id, self)
union = @classes.select { |un| un.name == new_union.name }.first
if union.nil?
@classes << new_union
else
union.load_xml(platform)
end
end
def process_descendents(mapping, platform)
@groups.each { |group| group.process(mapping, platform) }
@members.each { |member| member.process(mapping, platform) }
@classes.each { |member| member.process(mapping) }
end
end
end

View file

@ -0,0 +1,165 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require_relative 'doc_enum_value.rb'
module Pebble
# A DocMember is a function, enum, typedef that will become a symbol
# and be a part of a documentation page. Belongs to a DocGroup.
class DocMember < DocElement
attr_accessor :children, :kind, :name, :summary, :children, :position, :data, :id
def initialize(root, node, group, platform)
super(root, platform)
@group = group
@children = []
@platforms = [platform]
@nodes = { platform => node }
@name = node.at_css('name').content.to_s
@kind = node['kind']
@id = node['id']
@path = "#{@group.path}##{@name}"
@position = node.at_css(' > location')['line'].to_i
@doxygen_processor = DoxygenProcessor.new(platform)
create_enum_values(node, platform) if @kind == 'enum'
end
def add_platform(platform, node)
@platforms << platform
@nodes[platform] = node
@data[platform] = {}
create_enum_values(node, platform) if @kind == 'enum'
end
def to_liquid
{
'name' => @name,
'url' => url,
'children' => @children,
'position' => @position,
'data' => @data,
'uniform' => uniform?,
'platforms' => @platforms
}
end
def process(mapping, platform)
return unless @platforms.include? platform
@data[platform]['summary'] = @doxygen_processor.process_summary(
@nodes[platform].at_css(' > briefdescription'), mapping
)
process_data(@nodes[platform], mapping, platform)
@data[platform]['description'] = @doxygen_processor.process_description(
@nodes[platform].at_css(' > detaileddescription'), mapping
)
@children.each { |child| child.process(mapping, platform) }
end
def uniform?
identical = data['aplite'].to_json == data['basalt'].to_json
identical &&= children.all?(&:uniform?) if @kind == 'enum'
identical
end
private
def create_enum_values(node, platform)
node.css('enumvalue').each do |value|
enum_value = DocEnumValue.new(@root, value, @group, platform)
existing_value = @children.select { |ev| ev.name == enum_value.name }.first
if existing_value.nil?
@children << enum_value
else
existing_value.add_platform(value, platform)
end
end
end
def process_data(node, mapping, platform)
process_typedef(node, mapping, platform) if @kind == 'typedef'
process_function(node, mapping, platform) if @kind == 'function'
process_define(node, mapping, platform) if @kind == 'define'
process_simplesects(node, mapping, platform)
end
def process_typedef(node, mapping, platform)
process_return_type(node, mapping, platform)
@data[platform]['argsstring'] = node.at_css('argsstring').content.to_s
process_parameter_list(node, mapping, platform)
end
def process_function(node, mapping, platform)
process_return_type(node, mapping, platform)
process_params(node, mapping, platform) unless node.css('param').nil?
process_parameter_list(node, mapping, platform)
end
def process_define(node, mapping, platform)
unless node.at_css('initializer').nil?
@data[platform]['initializer'] = process_to_html(
node.at_css('initializer'), mapping
)
end
process_return_type(node, mapping, platform)
process_parameter_list(node, mapping, platform)
process_params(node, mapping, platform) unless node.css('param').nil?
end
def process_return_type(node, mapping, platform)
@data[platform]['type'] = process_to_html(node.at_css('> type'), mapping)
end
def process_params(node, mapping, platform)
@data[platform]['params'] = node.css('param').map do |elem|
params = {}
unless elem.at_css('declname').nil?
params['name'] = elem.at_css('declname').content.to_s
end
unless elem.at_css('type').nil?
params['type'] = process_to_html(elem.at_css('type'), mapping)
end
unless elem.at_css('defname').nil?
params['name'] = elem.at_css('defname').content.to_s
end
params
end
end
def process_to_html(node, mapping)
return '' if node.nil?
node.css('ref').each do |ref|
@doxygen_processor.process_node_ref(ref, mapping)
end
node.inner_html.to_s
end
def process_parameter_list(node, mapping, platform)
return if node.at_css('parameterlist').nil?
parameter_list = node.at_css('parameterlist').clone
node.at_css('parameterlist').remove
@data[platform]['parameters'] = parameter_list.css('parameteritem').map do |item|
{
'name' => get_parameter_name(item),
'summary' => @doxygen_processor.process_summary(item.at_css('parameterdescription'), mapping)
}
end
end
def get_parameter_name(item)
name = item.at_css('parameternamelist > parametername')
direction = name.attr('direction').to_s
direction.nil? || direction == '' ? name.content.to_s : "#{name.content.to_s} (#{direction})"
end
end
end

View file

@ -0,0 +1,154 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
module Pebble
# Class of methods for processing Doxygen XML into 'sane' HTML.
# rubocop:disable Metrics/ClassLength
class DoxygenProcessor
def initialize(platform)
@platform = platform
end
def process_summary(node, mapping)
process_description(node, mapping)
end
def process_description(node, mapping)
return '' if node.nil?
node.children.map { |para| process_paragraph(para, mapping) }.join("\n").strip
end
# rubocop:disable Metrics/MethodLength, Methods/CyclomaticComplexity
# rubocop:disable Methods/AbcSize
def process_paragraph(node, mapping)
return '' if node.nil?
node.name = 'p'
node.children.each do |child|
case child.name
when 'ref'
process_node_ref(child, mapping)
when 'programlisting'
process_code(child)
when 'simplesect'
# puts node.content.to_s
# process_blockquote(child)
when 'heading'
process_node_heading(child)
when 'htmlonly'
process_node_htmlonly(child)
when 'itemizedlist'
process_list(child, mapping)
when 'image'
process_image(child)
when 'computeroutput'
process_computer_output(child)
when 'emphasis'
child.name = 'em'
when 'bold'
child.name = 'strong'
when 'linebreak'
child.name = 'br'
when 'preformatted'
child.name = 'pre'
when 'ndash'
child.name = 'span'
child.content = '-'
when 'ulink'
child.name = 'a'
child['href'] = child['url'].sub(%r{^https?://developer.pebble.com/}, '/')
child.remove_attribute('url')
when 'text'
# SKIP!
else
# puts child.name, node.content.to_s
end
end
node.to_html.to_s.strip
end
# rubocop:enable Metrics/MethodLength, Methods/CyclomaticComplexity
# rubocop:enable Methods/AbcSize
def process_code(node)
xml = node.to_xml.gsub('<sp/>', ' ')
doc = Nokogiri::XML(xml)
highlight = Pygments.highlight(doc.content.to_s.strip, lexer: 'c')
node.content = ''
node << Nokogiri::XML(highlight).at_css('pre')
node.name = 'div'
node['class'] = 'highlight'
end
def process_node_ref(child, mapping)
child.name = 'a'
map = mapping.select { |m| m[:id] == child['refid'] }.first
child['href'] = map[:url] unless map.nil?
end
def process_node_heading(node)
node.name = 'h' + node['level']
end
def process_node_htmlonly(node)
decoder = HTMLEntities.new
xml = Nokogiri::XML('<root>' + decoder.decode(node.content) + '</root>')
node_count = xml.root.children.size
process_node_htmlonly_simple(node, xml) if node_count == 2
process_node_htmlonly_complex(node, xml) if node_count > 2
end
def process_node_htmlonly_simple(node, xml)
child = xml.at_css('root').children[0]
node.name = child.name
child.attributes.each { |key, value| node[key] = value }
node.content = child.content
end
def process_node_htmlonly_complex(node, xml)
node.name = 'div'
node.content = ''
node << xml.root.children
end
def process_blockquote(node)
node.name = 'blockquote'
node['class'] = 'blockquote--' + node['kind']
process_paragraph(node.children[0]) if node.children[0].name == 'para'
node.to_html.to_s
end
def process_list(node, mapping)
node.name = 'ul'
node.children.each do |child|
process_list_item(child, mapping)
end
end
def process_list_item(node, mapping)
node.name = 'li'
node.children.each do |child|
process_paragraph(child, mapping) if child.name == 'para'
end
end
def process_image(node)
node.name = 'img'
node['src'] = "/assets/images/docs/c/#{@platform}/#{node['name']}"
end
def process_computer_output(node)
node.name = 'code'
end
end
# rubocop:enable Metrics/ClassLength
end