bookmaker initial checkin
bookmaker is a tool that generates documentation backends from a canonical markup. Documentation for bookmaker itself is evolving at docs/usingBookmaker.bmh, which is visible online at skia.org/user/api/bmh_usingBookmaker Change-Id: Ic76ddf29134895b5c2ebfbc84603e40ff08caf09 Reviewed-on: https://skia-review.googlesource.com/28000 Commit-Queue: Cary Clark <caryclark@google.com> Reviewed-by: Cary Clark <caryclark@google.com>
This commit is contained in:
parent
acaa607328
commit
8032b983fa
17
BUILD.gn
17
BUILD.gn
@ -1213,6 +1213,23 @@ if (skia_enable_tools) {
|
||||
}
|
||||
}
|
||||
|
||||
test_app("bookmaker") {
|
||||
sources = [
|
||||
"tools/bookmaker/bookmaker.cpp",
|
||||
"tools/bookmaker/fiddleParser.cpp",
|
||||
"tools/bookmaker/includeParser.cpp",
|
||||
"tools/bookmaker/includeWriter.cpp",
|
||||
"tools/bookmaker/mdOut.cpp",
|
||||
"tools/bookmaker/parserCommon.cpp",
|
||||
"tools/bookmaker/spellCheck.cpp",
|
||||
]
|
||||
deps = [
|
||||
":flags",
|
||||
":skia",
|
||||
":tool_utils",
|
||||
]
|
||||
}
|
||||
|
||||
import("gn/samples.gni")
|
||||
test_lib("samples") {
|
||||
public_include_dirs = [ "samplecode" ]
|
||||
|
5721
docs/SkCanvas.bmh
Normal file
5721
docs/SkCanvas.bmh
Normal file
File diff suppressed because it is too large
Load Diff
5280
docs/SkPaint.bmh
Normal file
5280
docs/SkPaint.bmh
Normal file
File diff suppressed because it is too large
Load Diff
5801
docs/SkPath.bmh
Normal file
5801
docs/SkPath.bmh
Normal file
File diff suppressed because it is too large
Load Diff
88
docs/markup.bmh
Normal file
88
docs/markup.bmh
Normal file
@ -0,0 +1,88 @@
|
||||
#Topic Bookmaker_Markup
|
||||
|
||||
# redefine markup character so examples below will not be parsed
|
||||
###$
|
||||
|
||||
Text, except for the single markup character, requires no annotation.
|
||||
|
||||
# comments are preceded by a hash symbol and whitespace
|
||||
# comments may terminated by linefeed or double hash ## <- end of comment
|
||||
|
||||
Keywords are preceded by a single hash symbol without whitespace.
|
||||
#Keyword
|
||||
|
||||
Keywords are terminated by double hash and may be labeled
|
||||
## <- end of #keyword
|
||||
|
||||
#Keyword
|
||||
#Keyword ## <- alternate labeled end of #Keyword
|
||||
|
||||
Tables use single hash symbols to delimit columns, and double to end row.
|
||||
#Table
|
||||
#Legend
|
||||
# first column in table # next column in table ##
|
||||
## <- end of #Legend
|
||||
# a row # another row ##
|
||||
# another row # another row ##
|
||||
#Table ## <- or, just ##
|
||||
|
||||
$Table
|
||||
$Legend
|
||||
$ first column in table $ next column in table $$
|
||||
$$
|
||||
$ a row $ another row $$
|
||||
$ another row $ another row $$
|
||||
$Table $$
|
||||
|
||||
The markup character is initially # at the start of any .bmh file
|
||||
###x <- redefine the markup character as 'x'
|
||||
xxx# <- restore the default markup character
|
||||
|
||||
anchor, ala HTML
|
||||
anchors may start anywhere in the line
|
||||
#A text #_reference ##
|
||||
|
||||
class description
|
||||
#Class SkClassName
|
||||
description
|
||||
methods
|
||||
##
|
||||
|
||||
if the example is not named, it inherits the name of its container
|
||||
#Example
|
||||
#Description
|
||||
##
|
||||
#Image
|
||||
#Width
|
||||
#Height
|
||||
code...
|
||||
#StdOut
|
||||
expected example output
|
||||
##
|
||||
##
|
||||
|
||||
#Enum __required_reference
|
||||
description
|
||||
#Code
|
||||
##
|
||||
#Example
|
||||
##
|
||||
#Enum ##
|
||||
|
||||
method description
|
||||
the _method_reference must be unique within the class
|
||||
#Method type name(params..)
|
||||
description
|
||||
#Param name description ##
|
||||
#Return return ##
|
||||
#Example
|
||||
##
|
||||
#SeeAlso ##
|
||||
##
|
||||
|
||||
#ToDo description ##
|
||||
|
||||
$ restore markup character
|
||||
$$$#
|
||||
|
||||
##
|
8
docs/overview.bmh
Normal file
8
docs/overview.bmh
Normal file
@ -0,0 +1,8 @@
|
||||
overview
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Skia draws 2D primitives, paths and bitmaps, using the styles in the SkPaint, to
|
||||
the device contained by the SkCanvas.
|
||||
|
||||
--------------------------------------------------------------------------------
|
528
docs/undocumented.bmh
Normal file
528
docs/undocumented.bmh
Normal file
@ -0,0 +1,528 @@
|
||||
# external references that will be documented eventually ...
|
||||
#External
|
||||
DirectWrite TrueType Windows Linux Android
|
||||
FreeType FreeType-based Harfbuzz
|
||||
PostScript PostScript_arct
|
||||
OS_X Core_Graphics Core_Text iOS
|
||||
LCD RGB
|
||||
Premultiplied Unpremultiplied
|
||||
Unicode Unicode5 UTF-8 UTF-16 UTF-32 ASCII Unichar
|
||||
HTML_Canvas HTML_Canvas_arcTo
|
||||
API
|
||||
CPU
|
||||
GPU GPU-backed GPU_Context OpenGL Vulkan
|
||||
NULL
|
||||
RFC
|
||||
Bezier Coons
|
||||
SkUserConfig.h # not external, but still thinking about how markup refers to this
|
||||
Skia # ditto
|
||||
SK_USE_FREETYPE_EMBOLDEN # ditto
|
||||
SK_SUPPORT_LEGACY_PAINT_TEXTDECORATION # ditto
|
||||
SK_BUILD_FOR_ANDROID_FRAMEWORK # ditto
|
||||
Developer_Mode # ditto
|
||||
Draw_Layer # ditto
|
||||
Raster_Engine # ditto
|
||||
|
||||
# FreeType related
|
||||
FT_LOAD_TARGET_LIGHT
|
||||
FT_LOAD_TARGET_NORMAL
|
||||
FT_LOAD_TARGET_LCD
|
||||
FT_LOAD_TARGET_LCD_V
|
||||
FT_LOAD_NO_HINTING
|
||||
FT_Load_Glyph
|
||||
|
||||
#External ##
|
||||
|
||||
#Topic Arc
|
||||
#Substitute arcs
|
||||
#Topic ##
|
||||
|
||||
#Topic BBH_Factory
|
||||
#Class SkBBHFactory
|
||||
##
|
||||
##
|
||||
|
||||
#Topic Bitmap
|
||||
#Class SkBitmap
|
||||
#Subtopic Row_Bytes
|
||||
##
|
||||
#Class ##
|
||||
##
|
||||
|
||||
#Topic Blend_Mode
|
||||
#EnumClass SkBlendMode
|
||||
#Const kSrc 1
|
||||
##
|
||||
#Const kSrcOver 3
|
||||
##
|
||||
#Const kPlus 12
|
||||
##
|
||||
#EnumClass ##
|
||||
#Topic ##
|
||||
|
||||
#Topic Circle
|
||||
#Substitute circles
|
||||
#Topic ##
|
||||
|
||||
#Topic Clip_Op
|
||||
#EnumClass SkClipOp
|
||||
#Const kDifference 0
|
||||
##
|
||||
#Const kIntersect 1
|
||||
##
|
||||
##
|
||||
##
|
||||
|
||||
#Topic Color
|
||||
#Typedef SkColor
|
||||
#Typedef ##
|
||||
|
||||
# fixme: defines, not methods, need new markup type
|
||||
#Method int SkColorGetA(color)
|
||||
##
|
||||
#Method int SkColorGetR(color)
|
||||
##
|
||||
#Method int SkColorGetG(color)
|
||||
##
|
||||
#Method int SkColorGetB(color)
|
||||
##
|
||||
#Method int SkColorSetARGB(a, r, g, b)
|
||||
##
|
||||
|
||||
#Const SK_ColorBLACK 0xFF000000
|
||||
##
|
||||
#Const SK_ColorBLUE 0xFF0000FF
|
||||
##
|
||||
#Const SK_ColorGREEN 0xFF00FF00
|
||||
##
|
||||
#Const SK_ColorRED 0xFFFF0000
|
||||
##
|
||||
#Const SK_ColorWHITE 0xFFFFFFFF
|
||||
##
|
||||
#Subtopic Alpha
|
||||
#Substitute alpha
|
||||
#Subtopic ##
|
||||
#Subtopic RGB
|
||||
#Substitute RGB
|
||||
#Subtopic Red
|
||||
#Substitute red
|
||||
#Subtopic ##
|
||||
#Subtopic Blue
|
||||
#Substitute blue
|
||||
#Subtopic ##
|
||||
#Subtopic Green
|
||||
#Substitute green
|
||||
#Subtopic ##
|
||||
#Subtopic ##
|
||||
#Subtopic ARGB
|
||||
#Substitute ARGB
|
||||
#Subtopic ##
|
||||
|
||||
#Subtopic RBG
|
||||
#Substitute RBG
|
||||
#Subtopic ##
|
||||
|
||||
#Subtopic RGB-565
|
||||
#Substitute RGB-565
|
||||
#Alias Color_RGB-565 # quit changing - to _ !
|
||||
#Subtopic ##
|
||||
#Topic ##
|
||||
|
||||
#Topic Color_Filter
|
||||
#Class SkColorFilter
|
||||
#Class ##
|
||||
#Topic ##
|
||||
|
||||
#Topic Color_Space
|
||||
##
|
||||
|
||||
#Topic Curve
|
||||
#Alias Curves
|
||||
##
|
||||
|
||||
#Topic Data
|
||||
##
|
||||
|
||||
#Topic Device
|
||||
#Class SkBaseDevice
|
||||
##
|
||||
#Topic ##
|
||||
|
||||
#Topic Document
|
||||
#Class SkDocument
|
||||
#Method SkCanvas* beginPage(SkScalar width, SkScalar height,
|
||||
const SkRect* content = NULL)
|
||||
##
|
||||
##
|
||||
#Subtopic PDF
|
||||
##
|
||||
##
|
||||
|
||||
#Topic Draw_Filter
|
||||
#Class SkDrawFilter
|
||||
##
|
||||
##
|
||||
|
||||
#Topic Draw_Looper
|
||||
#Class SkDrawLooper
|
||||
#Class ##
|
||||
#Topic ##
|
||||
|
||||
#Topic Drawable
|
||||
#Class SkDrawable
|
||||
#Method void draw(SkCanvas*, const SkMatrix* = NULL)
|
||||
##
|
||||
##
|
||||
##
|
||||
|
||||
#Topic Dump_Canvas
|
||||
#Class SkDumpCanvas
|
||||
##
|
||||
#Topic ##
|
||||
|
||||
#Topic Filter_Quality
|
||||
#Enum SkFilterQuality
|
||||
#Const kNone_SkFilterQuality 0
|
||||
##
|
||||
#Const kLow_SkFilterQuality 1
|
||||
##
|
||||
#Const kMedium_SkFilterQuality 2
|
||||
##
|
||||
#Const kHigh_SkFilterQuality 3
|
||||
##
|
||||
#Enum ##
|
||||
#Topic ##
|
||||
|
||||
#Topic Font
|
||||
#Subtopic Advance
|
||||
#Subtopic ##
|
||||
#Subtopic Engine
|
||||
##
|
||||
#Topic ##
|
||||
|
||||
#Topic Font_Manager
|
||||
#Topic ##
|
||||
|
||||
#Topic Glyph
|
||||
##
|
||||
|
||||
#Topic Image
|
||||
#Subtopic Alpha_Type
|
||||
#Enum SkAlphaType
|
||||
#Const kPremul_SkAlphaType 2
|
||||
##
|
||||
##
|
||||
#Subtopic ##
|
||||
#Subtopic Color_Type
|
||||
#Enum SkColorType
|
||||
#Const kUnknown_SkColorType 0
|
||||
##
|
||||
#Const kAlpha_8_SkColorType 1
|
||||
##
|
||||
#Const kRGB_565_SkColorType 2
|
||||
##
|
||||
#Const kARGB_4444_SkColorType 3
|
||||
##
|
||||
#Const kRGBA_8888_SkColorType 4
|
||||
##
|
||||
#Const kBGRA_8888_SkColorType 5
|
||||
##
|
||||
#Const kIndex_8_SkColorType 6
|
||||
##
|
||||
#Const kGray_8_SkColorType 7
|
||||
##
|
||||
#Const kRGBA_F16_SkColorType 8
|
||||
##
|
||||
#ToDo this is a lie; need to not require values for consts ##
|
||||
#Const kN32_SkColorType 4
|
||||
##
|
||||
#Enum ##
|
||||
#Subtopic ##
|
||||
#Subtopic Info
|
||||
#Struct SkImageInfo
|
||||
#Method SkImageInfo()
|
||||
##
|
||||
##
|
||||
#Subtopic ##
|
||||
#Class SkImage
|
||||
#Method sk_sp<SkShader> makeShader(SkShader::TileMode, SkShader::TileMode,
|
||||
const SkMatrix* localMatrix = nullptr) const
|
||||
##
|
||||
##
|
||||
#Topic ##
|
||||
|
||||
#Topic Image_Filter
|
||||
#Subtopic Scaling
|
||||
#Subtopic ##
|
||||
#Class SkImageFilter
|
||||
#Class ##
|
||||
#Topic ##
|
||||
|
||||
#Topic Image_Scaling
|
||||
##
|
||||
|
||||
#Topic IRect
|
||||
#Struct SkIRect
|
||||
##
|
||||
##
|
||||
|
||||
#Topic Line
|
||||
#Substitute lines
|
||||
#Alias Lines
|
||||
#Topic ##
|
||||
|
||||
#Topic Mask
|
||||
#Topic ##
|
||||
|
||||
#Topic Mask_Alpha
|
||||
#Topic ##
|
||||
|
||||
#Topic Mask_Filter
|
||||
#Class SkMaskFilter
|
||||
#Class ##
|
||||
#Topic ##
|
||||
|
||||
#Topic Matrix
|
||||
#Struct SkMatrix
|
||||
#Struct ##
|
||||
#Topic ##
|
||||
|
||||
#Topic Nine_Patch
|
||||
##
|
||||
|
||||
#Topic Number_Types
|
||||
#Typedef SkGlyphID
|
||||
#Typedef ##
|
||||
#Typedef SkScalar
|
||||
#Typedef ##
|
||||
#Const SK_ScalarMax
|
||||
to be written
|
||||
##
|
||||
#Const SK_ScalarInfinity
|
||||
to be written
|
||||
##
|
||||
#Const SK_ScalarNegativeInfinity
|
||||
to be written
|
||||
##
|
||||
#Const SK_ScalarNaN
|
||||
to be written
|
||||
##
|
||||
#Typedef SkUnichar
|
||||
#Typedef ##
|
||||
#Typedef U8CPU
|
||||
#Typedef ##
|
||||
#Topic ##
|
||||
|
||||
#Topic Oval
|
||||
#Substitute ovals
|
||||
#Topic ##
|
||||
|
||||
#Topic Paint_Defaults
|
||||
#Const SkPaintDefaults_Flags 0
|
||||
##
|
||||
#Const SkPaintDefaults_Hinting 2
|
||||
##
|
||||
#Const SkPaintDefaults_TextSize 12
|
||||
##
|
||||
#Const SkPaintDefaults_MiterLimit 4
|
||||
##
|
||||
#Topic ##
|
||||
|
||||
#Topic Patch
|
||||
#Substitute patches
|
||||
#Topic ##
|
||||
|
||||
#Topic Path_Effect
|
||||
#Class SkPathEffect
|
||||
#Class ##
|
||||
#Topic ##
|
||||
|
||||
#Topic Path_Measure
|
||||
#Class SkPathMeasure
|
||||
#Method void dump() const
|
||||
##
|
||||
##
|
||||
##
|
||||
|
||||
#Topic PathOps
|
||||
#Method bool SK_API Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result)
|
||||
##
|
||||
#Topic ##
|
||||
|
||||
#Topic Picture
|
||||
#Subtopic Recorder
|
||||
#Class SkPictureRecorder
|
||||
#Method SkCanvas* beginRecording(const SkRect& bounds,
|
||||
SkBBHFactory* bbhFactory = NULL,
|
||||
uint32_t recordFlags = 0)
|
||||
##
|
||||
##
|
||||
##
|
||||
##
|
||||
|
||||
#Topic Pixel
|
||||
#Subtopic Storage
|
||||
##
|
||||
##
|
||||
|
||||
#Topic Pixmap
|
||||
#Class SkPixmap
|
||||
##
|
||||
##
|
||||
|
||||
#Topic Point
|
||||
#Alias Points
|
||||
#Struct SkPoint
|
||||
#Method bool equalsWithinTolerance(const SkPoint& p) const
|
||||
##
|
||||
#Struct ##
|
||||
#Subtopic Array
|
||||
#Substitute SkPoint arrays
|
||||
#Subtopic ##
|
||||
#Topic ##
|
||||
|
||||
#Topic Raster_Handle_Allocator
|
||||
#Class SkRasterHandleAllocator
|
||||
#Struct Rec
|
||||
##
|
||||
#Method static std::unique_ptr<SkCanvas> MakeCanvas(std::unique_ptr<SkRasterHandleAllocator>, const SkImageInfo&, const Rec* rec = nullptr)
|
||||
##
|
||||
##
|
||||
##
|
||||
|
||||
#Topic Rasterizer
|
||||
#Class SkRasterizer
|
||||
#Class ##
|
||||
#Subtopic Layer
|
||||
#Subtopic ##
|
||||
#Topic ##
|
||||
|
||||
#Topic Rect
|
||||
#Alias Rects
|
||||
#Struct SkRect
|
||||
#Method static constexpr SkRect SK_WARN_UNUSED_RESULT MakeEmpty()
|
||||
##
|
||||
#Method void dump() const
|
||||
##
|
||||
#Method void dumpHex() const
|
||||
##
|
||||
#Struct ##
|
||||
#Topic ##
|
||||
|
||||
#Topic Reference_Count
|
||||
#Substitute SkRefCnt
|
||||
#Class sk_sp
|
||||
#Class ##
|
||||
#Topic ##
|
||||
|
||||
#Topic Region
|
||||
#Class SkRegion
|
||||
##
|
||||
#Topic ##
|
||||
|
||||
#Topic Round_Rect
|
||||
#Class SkRRect
|
||||
#Method void dump() const
|
||||
##
|
||||
#Method void dumpHex() const
|
||||
##
|
||||
##
|
||||
#Topic ##
|
||||
|
||||
#Topic RSXform
|
||||
#Struct SkRSXform
|
||||
##
|
||||
##
|
||||
|
||||
#Topic Shader
|
||||
#Class SkShader
|
||||
#Enum TileMode
|
||||
#Const kClamp_TileMode 0
|
||||
##
|
||||
##
|
||||
#Method static sk_sp<SkShader> MakeBitmapShader(const SkBitmap& src, TileMode tmx, TileMode tmy,
|
||||
const SkMatrix* localMatrix = nullptr)
|
||||
##
|
||||
#Class ##
|
||||
#Subtopic Gradient
|
||||
#Subtopic ##
|
||||
#Topic ##
|
||||
|
||||
#Topic Sprite
|
||||
#Substitute sprites
|
||||
#Topic ##
|
||||
|
||||
#Topic Stream
|
||||
#Class SkFlattenable
|
||||
#Class ##
|
||||
#Topic ##
|
||||
|
||||
#Topic String
|
||||
#Class SkString
|
||||
#Class ##
|
||||
#Topic ##
|
||||
|
||||
#Topic Surface
|
||||
#Class SkSurface
|
||||
#Method static sk_sp<SkSurface> MakeRasterDirect(const SkImageInfo&, void* pixels, size_t rowBytes,
|
||||
const SkSurfaceProps* = nullptr)
|
||||
##
|
||||
##
|
||||
#Subtopic Properties
|
||||
#Class SkSurfaceProps
|
||||
#Enum InitType
|
||||
#Const kLegacyFontHost_InitType 0
|
||||
##
|
||||
##
|
||||
##
|
||||
##
|
||||
#Subtopic GPU
|
||||
#Alias GPU_Surface
|
||||
##
|
||||
#Subtopic Raster
|
||||
#Alias Raster_Surface
|
||||
##
|
||||
##
|
||||
|
||||
#Topic SVG
|
||||
#Subtopic Canvas
|
||||
##
|
||||
#Subtopic Arc
|
||||
##
|
||||
##
|
||||
|
||||
#Topic Text
|
||||
#Topic ##
|
||||
|
||||
#Topic Text_Blob
|
||||
#Class SkTextBlob
|
||||
#Class ##
|
||||
#Topic ##
|
||||
|
||||
#Topic Typeface
|
||||
#Class SkTypeface
|
||||
#Class ##
|
||||
#Topic ##
|
||||
|
||||
#Topic Vector
|
||||
#Struct SkVector
|
||||
##
|
||||
##
|
||||
|
||||
#Topic Vertices
|
||||
#Substitute vertices
|
||||
#Subtopic Colors
|
||||
##
|
||||
#Subtopic Texs
|
||||
##
|
||||
#Topic ##
|
||||
|
||||
#Topic Read_Buffer
|
||||
#Struct SkReadBuffer
|
||||
#Struct ##
|
||||
##
|
||||
|
||||
#Topic Write_Buffer
|
||||
#Struct SkWriteBuffer
|
||||
#Struct ##
|
||||
#Topic ##
|
95
docs/usingBookmaker.bmh
Normal file
95
docs/usingBookmaker.bmh
Normal file
@ -0,0 +1,95 @@
|
||||
#External
|
||||
SkXXX
|
||||
bmh_SkXXX
|
||||
CL
|
||||
C
|
||||
Visual_Studio
|
||||
##
|
||||
|
||||
#Topic Bookmaker
|
||||
|
||||
How to use the Bookmaker utility.
|
||||
|
||||
Get the fiddle command line interface tool.
|
||||
|
||||
#Code
|
||||
$ go get go.skia.org/infra/fiddle/go/fiddlecli
|
||||
##
|
||||
|
||||
Get the Bookmaker CL and build it.
|
||||
|
||||
#Code
|
||||
$ git cl patch 9919
|
||||
$ ninja -C out/dir bookmaker
|
||||
##
|
||||
|
||||
Generate an starter Bookmaker file from an existing include.
|
||||
This writes SkXXX.bmh in the current directory, which is
|
||||
out/dir/obj/ from an IDE.
|
||||
|
||||
#Code
|
||||
$ ./out/dir/bookmaker -t -i include/core/SkXXX.h
|
||||
##
|
||||
|
||||
Use your favorite editor to fill out SkXXX.bmh.
|
||||
|
||||
Generate fiddle.json from all examples, including the ones you just wrote.
|
||||
Error checking is syntatic: starting keywords are closed, keywords have the
|
||||
correct parents.
|
||||
If you run Bookmaker inside Visual_Studio, you can click on errors and it
|
||||
will take you to the source line in question.
|
||||
|
||||
#Code
|
||||
$ ./out/dir/bookmaker -e fiddle.json -b current_directory
|
||||
##
|
||||
|
||||
Once complete, run fiddlecli to generate the example hashes.
|
||||
Errors are contained by the output but aren't reported yet.
|
||||
|
||||
#Code
|
||||
$ $GOPATH/bin/fiddlecli --input fiddle.json --output fiddleout.json
|
||||
##
|
||||
|
||||
Generate bmh_SkXXX.md from SkXXX.bmh and fiddleout.json.
|
||||
Error checking includes: undefined references, fiddle compiler errors,
|
||||
missing or mismatched printf output.
|
||||
Again, you can click on any errors inside Visual_Studio.
|
||||
|
||||
#Code
|
||||
$ ./out/dir/bookmaker -r site/user/api -b current_directory -f fiddleout.json
|
||||
##
|
||||
|
||||
The original include may have changed since you started creating the markdown.
|
||||
Check to see if it is up to date.
|
||||
This reports if a method no longer exists or its parameters have changed.
|
||||
|
||||
#Code
|
||||
$ ./out/dir/bookmaker -x -b current_directory/SkXXX.bmh -i include/core/SkXXX.h
|
||||
##
|
||||
|
||||
#Topic Bugs
|
||||
#List
|
||||
overaggressive reference finding in code block
|
||||
missing examples
|
||||
redundant examples -- got tired so used the same one more than once
|
||||
some examples need vertical resizing
|
||||
list doesn't work (ironic, huh)
|
||||
##
|
||||
##
|
||||
|
||||
#Topic To_Do
|
||||
#List
|
||||
check that all methods have one line descriptions in overview
|
||||
see also -- anything that can be done automatically? maybe any ref shows up everywhere
|
||||
index by example png
|
||||
generate pdf or pdf-like out
|
||||
generate b/w out instead of color -- have b/w versions of examples?
|
||||
formalize voice / syntax for parts of topic and method
|
||||
write bmh data back into include
|
||||
have a way to write one block that covers multiple nearly indentical methods?
|
||||
may want to do this for pdf view as well
|
||||
write a one-method-per-page online view?
|
||||
##
|
||||
##
|
||||
|
||||
#Topic Bookmaker ##
|
2198
tools/bookmaker/bookmaker.cpp
Normal file
2198
tools/bookmaker/bookmaker.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1844
tools/bookmaker/bookmaker.h
Normal file
1844
tools/bookmaker/bookmaker.h
Normal file
File diff suppressed because it is too large
Load Diff
231
tools/bookmaker/fiddleParser.cpp
Normal file
231
tools/bookmaker/fiddleParser.cpp
Normal file
@ -0,0 +1,231 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "bookmaker.h"
|
||||
|
||||
static Definition* find_fiddle(Definition* def, const string& name) {
|
||||
if (MarkType::kExample == def->fMarkType && name == def->fFiddle) {
|
||||
return def;
|
||||
}
|
||||
for (auto& child : def->fChildren) {
|
||||
Definition* result = find_fiddle(child, name);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Definition* FiddleParser::findExample(const string& name) const {
|
||||
for (const auto& topic : fBmhParser->fTopicMap) {
|
||||
if (topic.second->fParent) {
|
||||
continue;
|
||||
}
|
||||
Definition* def = find_fiddle(topic.second, name);
|
||||
if (def) {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool FiddleParser::parseFiddles() {
|
||||
if (!this->skipExact("{\n")) {
|
||||
return false;
|
||||
}
|
||||
while (!this->eof()) {
|
||||
if (!this->skipExact(" \"")) {
|
||||
return false;
|
||||
}
|
||||
const char* nameLoc = fChar;
|
||||
if (!this->skipToEndBracket("\"")) {
|
||||
return false;
|
||||
}
|
||||
string name(nameLoc, fChar - nameLoc);
|
||||
if (!this->skipExact("\": {\n")) {
|
||||
return false;
|
||||
}
|
||||
if (!this->skipExact(" \"compile_errors\": [")) {
|
||||
return false;
|
||||
}
|
||||
if (']' != this->peek()) {
|
||||
// report compiler errors
|
||||
int brackets = 1;
|
||||
const char* errorStart = fChar;
|
||||
do {
|
||||
if ('[' == this->peek()) {
|
||||
++brackets;
|
||||
} else if (']' == this->peek()) {
|
||||
--brackets;
|
||||
}
|
||||
} while (!this->eof() && this->next() && brackets > 0);
|
||||
SkDebugf("fiddle compile error in %s: %.*s\n", name.c_str(), (int) (fChar - errorStart),
|
||||
errorStart);
|
||||
}
|
||||
if (!this->skipExact("],\n")) {
|
||||
return false;
|
||||
}
|
||||
if (!this->skipExact(" \"runtime_error\": \"")) {
|
||||
return false;
|
||||
}
|
||||
if ('"' != this->peek()) {
|
||||
const char* errorStart = fChar;
|
||||
if (!this->skipToEndBracket('"')) {
|
||||
return false;
|
||||
}
|
||||
SkDebugf("fiddle runtime error in %s: %.*s\n", name.c_str(), (int) (fChar - errorStart),
|
||||
errorStart);
|
||||
}
|
||||
if (!this->skipExact("\",\n")) {
|
||||
return false;
|
||||
}
|
||||
if (!this->skipExact(" \"fiddleHash\": \"")) {
|
||||
return false;
|
||||
}
|
||||
const char* hashStart = fChar;
|
||||
if (!this->skipToEndBracket('"')) {
|
||||
return false;
|
||||
}
|
||||
Definition* example = this->findExample(name);
|
||||
if (!example) {
|
||||
SkDebugf("missing example %s\n", name.c_str());
|
||||
}
|
||||
string hash(hashStart, fChar - hashStart);
|
||||
if (example) {
|
||||
example->fHash = hash;
|
||||
}
|
||||
if (!this->skipExact("\",\n")) {
|
||||
return false;
|
||||
}
|
||||
if (!this->skipExact(" \"text\": \"")) {
|
||||
return false;
|
||||
}
|
||||
if ('"' != this->peek()) {
|
||||
const char* stdOutStart = fChar;
|
||||
do {
|
||||
if ('\\' == this->peek()) {
|
||||
this->next();
|
||||
} else if ('"' == this->peek()) {
|
||||
break;
|
||||
}
|
||||
} while (!this->eof() && this->next());
|
||||
const char* stdOutEnd = fChar;
|
||||
if (example) {
|
||||
bool foundStdOut = false;
|
||||
for (auto& textOut : example->fChildren) {
|
||||
if (MarkType::kStdOut != textOut->fMarkType) {
|
||||
continue;
|
||||
}
|
||||
foundStdOut = true;
|
||||
bool foundVolatile = false;
|
||||
for (auto& stdOutChild : textOut->fChildren) {
|
||||
if (MarkType::kVolatile == stdOutChild->fMarkType) {
|
||||
foundVolatile = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
TextParser bmh(textOut);
|
||||
EscapeParser fiddle(stdOutStart, stdOutEnd);
|
||||
do {
|
||||
bmh.skipWhiteSpace();
|
||||
fiddle.skipWhiteSpace();
|
||||
const char* bmhEnd = bmh.trimmedLineEnd();
|
||||
const char* fiddleEnd = fiddle.trimmedLineEnd();
|
||||
ptrdiff_t bmhLen = bmhEnd - bmh.fChar;
|
||||
SkASSERT(bmhLen > 0);
|
||||
ptrdiff_t fiddleLen = fiddleEnd - fiddle.fChar;
|
||||
SkASSERT(fiddleLen > 0);
|
||||
if (bmhLen != fiddleLen) {
|
||||
if (!foundVolatile) {
|
||||
SkDebugf("mismatched stdout len in %s\n", name.c_str());
|
||||
}
|
||||
} else if (strncmp(bmh.fChar, fiddle.fChar, fiddleLen)) {
|
||||
if (!foundVolatile) {
|
||||
SkDebugf("mismatched stdout text in %s\n", name.c_str());
|
||||
}
|
||||
}
|
||||
bmh.skipToLineStart();
|
||||
fiddle.skipToLineStart();
|
||||
} while (!bmh.eof() && !fiddle.eof());
|
||||
if (!foundStdOut) {
|
||||
SkDebugf("bmh %s missing stdout\n", name.c_str());
|
||||
} else if (!bmh.eof() || !fiddle.eof()) {
|
||||
if (!foundVolatile) {
|
||||
SkDebugf("%s mismatched stdout eof\n", name.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!this->skipExact("\"\n")) {
|
||||
return false;
|
||||
}
|
||||
if (!this->skipExact(" }")) {
|
||||
return false;
|
||||
}
|
||||
if ('\n' == this->peek()) {
|
||||
break;
|
||||
}
|
||||
if (!this->skipExact(",\n")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
// compare the text output with the expected output in the markup tree
|
||||
this->skipToSpace();
|
||||
SkASSERT(' ' == fChar[0]);
|
||||
this->next();
|
||||
const char* nameLoc = fChar;
|
||||
this->skipToNonAlphaNum();
|
||||
const char* nameEnd = fChar;
|
||||
string name(nameLoc, nameEnd - nameLoc);
|
||||
const Definition* example = this->findExample(name);
|
||||
if (!example) {
|
||||
return this->reportError<bool>("missing stdout name");
|
||||
}
|
||||
SkASSERT(':' == fChar[0]);
|
||||
this->next();
|
||||
this->skipSpace();
|
||||
const char* stdOutLoc = fChar;
|
||||
do {
|
||||
this->skipToLineStart();
|
||||
} while (!this->eof() && !this->startsWith("fiddles.htm:"));
|
||||
const char* stdOutEnd = fChar;
|
||||
for (auto& textOut : example->fChildren) {
|
||||
if (MarkType::kStdOut != textOut->fMarkType) {
|
||||
continue;
|
||||
}
|
||||
TextParser bmh(textOut);
|
||||
TextParser fiddle(fFileName, stdOutLoc, stdOutEnd, fLineCount);
|
||||
do {
|
||||
bmh.skipWhiteSpace();
|
||||
fiddle.skipWhiteSpace();
|
||||
const char* bmhEnd = bmh.trimmedLineEnd();
|
||||
const char* fiddleEnd = fiddle.trimmedLineEnd();
|
||||
ptrdiff_t bmhLen = bmhEnd - bmh.fChar;
|
||||
SkASSERT(bmhLen > 0);
|
||||
ptrdiff_t fiddleLen = fiddleEnd - fiddle.fChar;
|
||||
SkASSERT(fiddleLen > 0);
|
||||
if (bmhLen != fiddleLen) {
|
||||
return this->reportError<bool>("mismatched stdout len");
|
||||
}
|
||||
if (strncmp(bmh.fChar, fiddle.fChar, fiddleLen)) {
|
||||
return this->reportError<bool>("mismatched stdout text");
|
||||
}
|
||||
bmh.skipToLineStart();
|
||||
fiddle.skipToLineStart();
|
||||
} while (!bmh.eof() && !fiddle.eof());
|
||||
if (!bmh.eof() || (!fiddle.eof() && !fiddle.startsWith("</pre>"))) {
|
||||
return this->reportError<bool>("mismatched stdout eof");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
1733
tools/bookmaker/includeParser.cpp
Normal file
1733
tools/bookmaker/includeParser.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1272
tools/bookmaker/includeWriter.cpp
Normal file
1272
tools/bookmaker/includeWriter.cpp
Normal file
File diff suppressed because it is too large
Load Diff
929
tools/bookmaker/mdOut.cpp
Normal file
929
tools/bookmaker/mdOut.cpp
Normal file
@ -0,0 +1,929 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "bookmaker.h"
|
||||
|
||||
#include "SkOSFile.h"
|
||||
#include "SkOSPath.h"
|
||||
|
||||
static void add_ref(const string& leadingSpaces, const string& ref, string* result) {
|
||||
*result += leadingSpaces + ref;
|
||||
}
|
||||
|
||||
// FIXME: preserve inter-line spaces and don't add new ones
|
||||
string MdOut::addReferences(const char* refStart, const char* refEnd,
|
||||
BmhParser::Resolvable resolvable) {
|
||||
string result;
|
||||
MethodParser t(fRoot ? fRoot->fName : string(), fFileName, refStart, refEnd, fLineCount);
|
||||
bool lineStart = true;
|
||||
string ref;
|
||||
string leadingSpaces;
|
||||
do {
|
||||
const char* base = t.fChar;
|
||||
t.skipWhiteSpace();
|
||||
const char* wordStart = t.fChar;
|
||||
t.skipToMethodStart();
|
||||
const char* start = t.fChar;
|
||||
if (wordStart < start) {
|
||||
if (lineStart) {
|
||||
lineStart = false;
|
||||
} else {
|
||||
wordStart = base;
|
||||
}
|
||||
result += string(wordStart, start - wordStart);
|
||||
if ('\n' != result.back()) {
|
||||
while (start > wordStart && '\n' == start[-1]) {
|
||||
result += '\n';
|
||||
--start;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lineStart) {
|
||||
lineStart = false;
|
||||
} else {
|
||||
leadingSpaces = string(base, wordStart - base);
|
||||
}
|
||||
t.skipToMethodEnd();
|
||||
if (base == t.fChar) {
|
||||
break;
|
||||
}
|
||||
if (start >= t.fChar) {
|
||||
continue;
|
||||
}
|
||||
if (!t.eof() && '"' == t.peek() && start > wordStart && '"' == start[-1]) {
|
||||
continue;
|
||||
}
|
||||
ref = string(start, t.fChar - start);
|
||||
if (const Definition* def = this->isDefined(t, ref,
|
||||
BmhParser::Resolvable::kOut != resolvable)) {
|
||||
SkASSERT(def->fFiddle.length());
|
||||
if (!t.eof() && '(' == t.peek() && t.strnchr(')', t.fEnd)) {
|
||||
if (!t.skipToEndBracket(')')) {
|
||||
t.reportError("missing close paren");
|
||||
return result;
|
||||
}
|
||||
t.next();
|
||||
string fullRef = string(start, t.fChar - start);
|
||||
// if _2 etc alternates are defined, look for paren match
|
||||
// may ignore () if ref is all lower case
|
||||
// otherwise flag as error
|
||||
int suffix = '2';
|
||||
bool foundMatch = false;
|
||||
const Definition* altDef = def;
|
||||
while (altDef && suffix <= '9') {
|
||||
if ((foundMatch = altDef->paramsMatch(fullRef, ref))) {
|
||||
def = altDef;
|
||||
ref = fullRef;
|
||||
break;
|
||||
}
|
||||
string altTest = ref + '_';
|
||||
altTest += suffix++;
|
||||
altDef = this->isDefined(t, altTest, false);
|
||||
}
|
||||
if (suffix > '9') {
|
||||
t.reportError("too many alts");
|
||||
return result;
|
||||
}
|
||||
if (!foundMatch) {
|
||||
if (!(def = this->isDefined(t, fullRef, true))) {
|
||||
return result;
|
||||
}
|
||||
ref = fullRef;
|
||||
}
|
||||
}
|
||||
result += linkRef(leadingSpaces, def, ref);
|
||||
continue;
|
||||
}
|
||||
if (!t.eof() && '(' == t.peek()) {
|
||||
if (!t.skipToEndBracket(')')) {
|
||||
t.reportError("missing close paren");
|
||||
return result;
|
||||
}
|
||||
t.next();
|
||||
ref = string(start, t.fChar - start);
|
||||
if (const Definition* def = this->isDefined(t, ref, true)) {
|
||||
SkASSERT(def->fFiddle.length());
|
||||
result += linkRef(leadingSpaces, def, ref);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// class, struct, and enum start with capitals
|
||||
// methods may start with upper (static) or lower (most)
|
||||
|
||||
// see if this should have been a findable reference
|
||||
|
||||
// look for Sk / sk / SK ..
|
||||
if (!ref.compare(0, 2, "Sk") && ref != "Skew" && ref != "Skews" &&
|
||||
ref != "Skip" && ref != "Skips") {
|
||||
t.reportError("missed Sk prefixed");
|
||||
return result;
|
||||
}
|
||||
if (!ref.compare(0, 2, "SK")) {
|
||||
if (BmhParser::Resolvable::kOut != resolvable) {
|
||||
t.reportError("missed SK prefixed");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
if (!isupper(start[0])) {
|
||||
// TODO:
|
||||
// look for all lowercase w/o trailing parens as mistaken method matches
|
||||
// will also need to see if Example Description matches var in example
|
||||
const Definition* def;
|
||||
if (fMethod && (def = fMethod->hasParam(ref))) {
|
||||
result += linkRef(leadingSpaces, def, ref);
|
||||
continue;
|
||||
} else if (!fInDescription && ref[0] != '0'
|
||||
&& string::npos != ref.find_first_of("ABCDEFGHIJKLMNOPQRSTUVWXYZ")) {
|
||||
// FIXME: see isDefined(); check to see if fXX is a member of xx.fXX
|
||||
if (('f' != ref[0] && string::npos == ref.find("()"))
|
||||
|| '.' != t.backup(ref.c_str())) {
|
||||
if (BmhParser::Resolvable::kOut != resolvable) {
|
||||
t.reportError("missed camelCase");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
add_ref(leadingSpaces, ref, &result);
|
||||
continue;
|
||||
}
|
||||
auto topicIter = fBmhParser.fTopicMap.find(ref);
|
||||
if (topicIter != fBmhParser.fTopicMap.end()) {
|
||||
result += linkRef(leadingSpaces, topicIter->second, ref);
|
||||
continue;
|
||||
}
|
||||
bool startsSentence = t.sentenceEnd(start);
|
||||
if (!t.eof() && ' ' != t.peek()) {
|
||||
add_ref(leadingSpaces, ref, &result);
|
||||
continue;
|
||||
}
|
||||
if (t.fChar + 1 >= t.fEnd || (!isupper(t.fChar[1]) && startsSentence)) {
|
||||
add_ref(leadingSpaces, ref, &result);
|
||||
continue;
|
||||
}
|
||||
if (isupper(t.fChar[1]) && startsSentence) {
|
||||
TextParser next(t.fFileName, &t.fChar[1], t.fEnd, t.fLineCount);
|
||||
string nextWord(next.fChar, next.wordEnd() - next.fChar);
|
||||
if (this->isDefined(t, nextWord, true)) {
|
||||
add_ref(leadingSpaces, ref, &result);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Definition* test = fRoot;
|
||||
do {
|
||||
if (!test->isRoot()) {
|
||||
continue;
|
||||
}
|
||||
for (string prefix : { "_", "::" } ) {
|
||||
RootDefinition* root = test->asRoot();
|
||||
string prefixed = root->fName + prefix + ref;
|
||||
if (const Definition* def = root->find(prefixed)) {
|
||||
result += linkRef(leadingSpaces, def, ref);
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
} while ((test = test->fParent));
|
||||
found:
|
||||
if (!test) {
|
||||
if (BmhParser::Resolvable::kOut != resolvable) {
|
||||
t.reportError("undefined reference");
|
||||
}
|
||||
}
|
||||
} while (!t.eof());
|
||||
return result;
|
||||
}
|
||||
|
||||
bool MdOut::buildReferences(const char* fileOrPath, const char* outDir) {
|
||||
if (!sk_isdir(fileOrPath)) {
|
||||
if (!this->buildRefFromFile(fileOrPath, outDir)) {
|
||||
SkDebugf("failed to parse %s\n", fileOrPath);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
SkOSFile::Iter it(fileOrPath, ".bmh");
|
||||
for (SkString file; it.next(&file); ) {
|
||||
SkString p = SkOSPath::Join(fileOrPath, file.c_str());
|
||||
const char* hunk = p.c_str();
|
||||
if (!SkStrEndsWith(hunk, ".bmh")) {
|
||||
continue;
|
||||
}
|
||||
if (SkStrEndsWith(hunk, "markup.bmh")) { // don't look inside this for now
|
||||
continue;
|
||||
}
|
||||
if (!this->buildRefFromFile(hunk, outDir)) {
|
||||
SkDebugf("failed to parse %s\n", hunk);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MdOut::buildRefFromFile(const char* name, const char* outDir) {
|
||||
fFileName = string(name);
|
||||
string filename(name);
|
||||
if (filename.substr(filename.length() - 4) == ".bmh") {
|
||||
filename = filename.substr(0, filename.length() - 4);
|
||||
}
|
||||
size_t start = filename.length();
|
||||
while (start > 0 && (isalnum(filename[start - 1]) || '_' == filename[start - 1])) {
|
||||
--start;
|
||||
}
|
||||
string match = filename.substr(start);
|
||||
string header = match;
|
||||
filename = "bmh_" + match + ".md";
|
||||
match += ".bmh";
|
||||
fOut = nullptr;
|
||||
for (const auto& topic : fBmhParser.fTopicMap) {
|
||||
Definition* topicDef = topic.second;
|
||||
if (topicDef->fParent) {
|
||||
continue;
|
||||
}
|
||||
if (!topicDef->isRoot()) {
|
||||
return this->reportError<bool>("expected root topic");
|
||||
}
|
||||
fRoot = topicDef->asRoot();
|
||||
if (string::npos == fRoot->fFileName.rfind(match)) {
|
||||
continue;
|
||||
}
|
||||
if (!fOut) {
|
||||
string fullName(outDir);
|
||||
if ('/' != fullName.back()) {
|
||||
fullName += '/';
|
||||
}
|
||||
fullName += filename;
|
||||
fOut = fopen(fullName.c_str(), "wb");
|
||||
if (!fOut) {
|
||||
SkDebugf("could not open output file %s\n", fullName.c_str());
|
||||
return false;
|
||||
}
|
||||
fprintf(fOut, "Experimental %s", header.c_str());
|
||||
this->lfAlways(1);
|
||||
fprintf(fOut, "===");
|
||||
}
|
||||
this->markTypeOut(topicDef);
|
||||
}
|
||||
if (fOut) {
|
||||
this->writePending();
|
||||
fclose(fOut);
|
||||
fOut = nullptr;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MdOut::childrenOut(const Definition* def, const char* start) {
|
||||
const char* end;
|
||||
fLineCount = def->fLineCount;
|
||||
if (def->isRoot()) {
|
||||
fRoot = const_cast<RootDefinition*>(def->asRoot());
|
||||
}
|
||||
BmhParser::Resolvable resolvable = this->resolvable(def->fMarkType);
|
||||
for (auto& child : def->fChildren) {
|
||||
end = child->fStart;
|
||||
if (BmhParser::Resolvable::kNo != resolvable) {
|
||||
this->resolveOut(start, end, resolvable);
|
||||
}
|
||||
this->markTypeOut(child);
|
||||
start = child->fTerminator;
|
||||
}
|
||||
if (BmhParser::Resolvable::kNo != resolvable) {
|
||||
end = def->fContentEnd;
|
||||
this->resolveOut(start, end, resolvable);
|
||||
}
|
||||
}
|
||||
|
||||
const Definition* MdOut::isDefined(const TextParser& parser, const string& ref, bool report) const {
|
||||
auto rootIter = fBmhParser.fClassMap.find(ref);
|
||||
if (rootIter != fBmhParser.fClassMap.end()) {
|
||||
return &rootIter->second;
|
||||
}
|
||||
auto typedefIter = fBmhParser.fTypedefMap.find(ref);
|
||||
if (typedefIter != fBmhParser.fTypedefMap.end()) {
|
||||
return &typedefIter->second;
|
||||
}
|
||||
auto enumIter = fBmhParser.fEnumMap.find(ref);
|
||||
if (enumIter != fBmhParser.fEnumMap.end()) {
|
||||
return &enumIter->second;
|
||||
}
|
||||
auto constIter = fBmhParser.fConstMap.find(ref);
|
||||
if (constIter != fBmhParser.fConstMap.end()) {
|
||||
return &constIter->second;
|
||||
}
|
||||
auto methodIter = fBmhParser.fMethodMap.find(ref);
|
||||
if (methodIter != fBmhParser.fMethodMap.end()) {
|
||||
return &methodIter->second;
|
||||
}
|
||||
auto aliasIter = fBmhParser.fAliasMap.find(ref);
|
||||
if (aliasIter != fBmhParser.fAliasMap.end()) {
|
||||
return aliasIter->second;
|
||||
}
|
||||
for (const auto& external : fBmhParser.fExternals) {
|
||||
if (external.fName == ref) {
|
||||
return &external;
|
||||
}
|
||||
}
|
||||
if (fRoot) {
|
||||
if (ref == fRoot->fName) {
|
||||
return fRoot;
|
||||
}
|
||||
if (const Definition* definition = fRoot->find(ref)) {
|
||||
return definition;
|
||||
}
|
||||
Definition* test = fRoot;
|
||||
do {
|
||||
if (!test->isRoot()) {
|
||||
continue;
|
||||
}
|
||||
RootDefinition* root = test->asRoot();
|
||||
for (auto& leaf : root->fBranches) {
|
||||
if (ref == leaf.first) {
|
||||
return leaf.second;
|
||||
}
|
||||
const Definition* definition = leaf.second->find(ref);
|
||||
if (definition) {
|
||||
return definition;
|
||||
}
|
||||
}
|
||||
for (string prefix : { "::", "_" } ) {
|
||||
string prefixed = root->fName + prefix + ref;
|
||||
if (const Definition* definition = root->find(prefixed)) {
|
||||
return definition;
|
||||
}
|
||||
if (isupper(prefixed[0])) {
|
||||
auto topicIter = fBmhParser.fTopicMap.find(prefixed);
|
||||
if (topicIter != fBmhParser.fTopicMap.end()) {
|
||||
return topicIter->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while ((test = test->fParent));
|
||||
}
|
||||
size_t doubleColon = ref.find("::");
|
||||
if (string::npos != doubleColon) {
|
||||
string className = ref.substr(0, doubleColon);
|
||||
auto classIter = fBmhParser.fClassMap.find(className);
|
||||
if (classIter != fBmhParser.fClassMap.end()) {
|
||||
const RootDefinition& classDef = classIter->second;
|
||||
const Definition* result = classDef.find(ref);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (!ref.compare(0, 2, "SK") || !ref.compare(0, 3, "sk_")
|
||||
|| (('k' == ref[0] || 'g' == ref[0] || 'f' == ref[0]) &&
|
||||
ref.length() > 1 && isupper(ref[1]))) {
|
||||
// try with a prefix
|
||||
if ('k' == ref[0]) {
|
||||
for (auto const& iter : fBmhParser.fEnumMap) {
|
||||
if (iter.second.find(ref)) {
|
||||
return &iter.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ('f' == ref[0]) {
|
||||
// FIXME : find def associated with prior, e.g.: r.fX where 'SkPoint r' was earlier
|
||||
// need to have pushed last resolve on stack to do this
|
||||
// for now, just try to make sure that it's there and error if not
|
||||
if ('.' != parser.backup(ref.c_str())) {
|
||||
parser.reportError("fX member undefined");
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
if (report) {
|
||||
parser.reportError("SK undefined");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (isupper(ref[0])) {
|
||||
auto topicIter = fBmhParser.fTopicMap.find(ref);
|
||||
if (topicIter != fBmhParser.fTopicMap.end()) {
|
||||
return topicIter->second;
|
||||
}
|
||||
size_t pos = ref.find('_');
|
||||
if (string::npos != pos) {
|
||||
// see if it is defined by another base class
|
||||
string className(ref, 0, pos);
|
||||
auto classIter = fBmhParser.fClassMap.find(className);
|
||||
if (classIter != fBmhParser.fClassMap.end()) {
|
||||
if (const Definition* definition = classIter->second.find(ref)) {
|
||||
return definition;
|
||||
}
|
||||
}
|
||||
auto enumIter = fBmhParser.fEnumMap.find(className);
|
||||
if (enumIter != fBmhParser.fEnumMap.end()) {
|
||||
if (const Definition* definition = enumIter->second.find(ref)) {
|
||||
return definition;
|
||||
}
|
||||
}
|
||||
if (report) {
|
||||
parser.reportError("_ undefined");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
string MdOut::linkName(const Definition* ref) const {
|
||||
string result = ref->fName;
|
||||
size_t under = result.find('_');
|
||||
if (string::npos != under) {
|
||||
string classPart = result.substr(0, under);
|
||||
string namePart = result.substr(under + 1, result.length());
|
||||
if (fRoot && (fRoot->fName == classPart
|
||||
|| (fRoot->fParent && fRoot->fParent->fName == classPart))) {
|
||||
result = namePart;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// for now, hard-code to html links
|
||||
// def should not include SkXXX_
|
||||
string MdOut::linkRef(const string& leadingSpaces, const Definition* def,
|
||||
const string& ref) const {
|
||||
string buildup;
|
||||
const string* str = &def->fFiddle;
|
||||
SkASSERT(str->length() > 0);
|
||||
size_t under = str->find('_');
|
||||
Definition* curRoot = fRoot;
|
||||
string classPart = string::npos != under ? str->substr(0, under) : *str;
|
||||
bool classMatch = curRoot->fName == classPart;
|
||||
while (curRoot->fParent) {
|
||||
curRoot = curRoot->fParent;
|
||||
classMatch |= curRoot->fName == classPart;
|
||||
}
|
||||
const Definition* defRoot;
|
||||
do {
|
||||
defRoot = def;
|
||||
if (!(def = def->fParent)) {
|
||||
break;
|
||||
}
|
||||
classMatch |= def != defRoot && def->fName == classPart;
|
||||
} while (true);
|
||||
string namePart = string::npos != under ? str->substr(under + 1, str->length()) : *str;
|
||||
SkASSERT(fRoot);
|
||||
SkASSERT(fRoot->fFileName.length());
|
||||
if (false && classMatch) {
|
||||
str = &namePart;
|
||||
} else if (true || (curRoot != defRoot && defRoot->isRoot())) {
|
||||
string filename = defRoot->asRoot()->fFileName;
|
||||
if (filename.substr(filename.length() - 4) == ".bmh") {
|
||||
filename = filename.substr(0, filename.length() - 4);
|
||||
}
|
||||
size_t start = filename.length();
|
||||
while (start > 0 && (isalnum(filename[start - 1]) || '_' == filename[start - 1])) {
|
||||
--start;
|
||||
}
|
||||
buildup = "bmh_" + filename.substr(start) + "?cl=9919#"
|
||||
+ (classMatch ? namePart : *str);
|
||||
str = &buildup;
|
||||
}
|
||||
string refOut(ref);
|
||||
std::replace(refOut.begin(), refOut.end(), '_', ' ');
|
||||
if (ref.length() > 2 && islower(ref[0]) && "()" == ref.substr(ref.length() - 2)) {
|
||||
refOut = refOut.substr(0, refOut.length() - 2);
|
||||
}
|
||||
return leadingSpaces + "<a href=\"" + *str + "\">" + refOut + "</a>";
|
||||
}
|
||||
|
||||
void MdOut::markTypeOut(Definition* def) {
|
||||
string printable = def->printableName();
|
||||
const char* textStart = def->fContentStart;
|
||||
if (MarkType::kParam != def->fMarkType && MarkType::kConst != def->fMarkType &&
|
||||
(!def->fParent || MarkType::kConst != def->fParent->fMarkType) &&
|
||||
TableState::kNone != fTableState) {
|
||||
this->writePending();
|
||||
fprintf(fOut, "</table>");
|
||||
this->lf(2);
|
||||
fTableState = TableState::kNone;
|
||||
}
|
||||
switch (def->fMarkType) {
|
||||
case MarkType::kAlias:
|
||||
break;
|
||||
case MarkType::kAnchor:
|
||||
break;
|
||||
case MarkType::kBug:
|
||||
break;
|
||||
case MarkType::kClass:
|
||||
this->mdHeaderOut(1);
|
||||
fprintf(fOut, "<a name=\"%s\"></a> Class %s", this->linkName(def).c_str(),
|
||||
def->fName.c_str());
|
||||
this->lf(1);
|
||||
break;
|
||||
case MarkType::kCode:
|
||||
this->lfAlways(2);
|
||||
fprintf(fOut, "<pre style=\"padding: 1em 1em 1em 1em;"
|
||||
"width: 44em; background-color: #f0f0f0\">");
|
||||
this->lf(1);
|
||||
break;
|
||||
case MarkType::kColumn:
|
||||
this->writePending();
|
||||
if (fInList) {
|
||||
fprintf(fOut, " <td>");
|
||||
} else {
|
||||
fprintf(fOut, "| ");
|
||||
}
|
||||
break;
|
||||
case MarkType::kComment:
|
||||
break;
|
||||
case MarkType::kConst: {
|
||||
if (TableState::kNone == fTableState) {
|
||||
this->mdHeaderOut(3);
|
||||
fprintf(fOut, "Constants\n"
|
||||
"\n"
|
||||
"<table>");
|
||||
fTableState = TableState::kRow;
|
||||
this->lf(1);
|
||||
}
|
||||
if (TableState::kRow == fTableState) {
|
||||
this->writePending();
|
||||
fprintf(fOut, " <tr>");
|
||||
this->lf(1);
|
||||
fTableState = TableState::kColumn;
|
||||
}
|
||||
this->writePending();
|
||||
fprintf(fOut, " <td><a name=\"%s\"></a> <code><strong>%s </strong></code></td>",
|
||||
def->fName.c_str(), def->fName.c_str());
|
||||
const char* lineEnd = strchr(textStart, '\n');
|
||||
SkASSERT(lineEnd < def->fTerminator);
|
||||
SkASSERT(lineEnd > textStart);
|
||||
SkASSERT((int) (lineEnd - textStart) == lineEnd - textStart);
|
||||
fprintf(fOut, "<td>%.*s</td>", (int) (lineEnd - textStart), textStart);
|
||||
fprintf(fOut, "<td>");
|
||||
textStart = lineEnd;
|
||||
} break;
|
||||
case MarkType::kDefine:
|
||||
break;
|
||||
case MarkType::kDefinedBy:
|
||||
break;
|
||||
case MarkType::kDeprecated:
|
||||
break;
|
||||
case MarkType::kDescription:
|
||||
fInDescription = true;
|
||||
this->writePending();
|
||||
fprintf(fOut, "<div>");
|
||||
break;
|
||||
case MarkType::kDoxygen:
|
||||
break;
|
||||
case MarkType::kEnum:
|
||||
case MarkType::kEnumClass:
|
||||
this->mdHeaderOut(2);
|
||||
fprintf(fOut, "<a name=\"%s\"></a> Enum %s", def->fName.c_str(), def->fName.c_str());
|
||||
this->lf(2);
|
||||
break;
|
||||
case MarkType::kError:
|
||||
break;
|
||||
case MarkType::kExample: {
|
||||
this->mdHeaderOut(3);
|
||||
fprintf(fOut, "Example\n"
|
||||
"\n");
|
||||
fHasFiddle = true;
|
||||
const Definition* platform = def->hasChild(MarkType::kPlatform);
|
||||
if (platform) {
|
||||
TextParser platParse(platform);
|
||||
fHasFiddle = !platParse.strnstr("!fiddle", platParse.fEnd);
|
||||
}
|
||||
if (fHasFiddle) {
|
||||
fprintf(fOut, "<div><fiddle-embed name=\"%s\">", def->fHash.c_str());
|
||||
} else {
|
||||
fprintf(fOut, "<pre style=\"padding: 1em 1em 1em 1em;"
|
||||
"width: 44em; background-color: #f0f0f0\">");
|
||||
this->lf(1);
|
||||
}
|
||||
} break;
|
||||
case MarkType::kExperimental:
|
||||
break;
|
||||
case MarkType::kExternal:
|
||||
break;
|
||||
case MarkType::kFile:
|
||||
break;
|
||||
case MarkType::kFormula:
|
||||
break;
|
||||
case MarkType::kFunction:
|
||||
break;
|
||||
case MarkType::kHeight:
|
||||
break;
|
||||
case MarkType::kImage:
|
||||
break;
|
||||
case MarkType::kLegend:
|
||||
break;
|
||||
case MarkType::kLink:
|
||||
break;
|
||||
case MarkType::kList:
|
||||
fInList = true;
|
||||
this->lfAlways(2);
|
||||
fprintf(fOut, "<table>");
|
||||
this->lf(1);
|
||||
break;
|
||||
case MarkType::kMarkChar:
|
||||
fBmhParser.fMC = def->fContentStart[0];
|
||||
break;
|
||||
case MarkType::kMember: {
|
||||
TextParser tp(def->fFileName, def->fStart, def->fContentStart, def->fLineCount);
|
||||
tp.skipExact("#Member");
|
||||
tp.skipWhiteSpace();
|
||||
const char* end = tp.trimmedBracketEnd('\n', TextParser::OneLine::kYes);
|
||||
this->lfAlways(2);
|
||||
fprintf(fOut, "<code><strong>%.*s</strong></code>", (int) (end - tp.fChar), tp.fChar);
|
||||
this->lf(2);
|
||||
} break;
|
||||
case MarkType::kMethod: {
|
||||
string method_name = def->methodName();
|
||||
string formattedStr = def->formatFunction();
|
||||
|
||||
if (!def->isClone()) {
|
||||
this->lfAlways(2);
|
||||
fprintf(fOut, "<a name=\"%s\"></a>", def->fiddleName().c_str());
|
||||
this->mdHeaderOutLF(2, 1);
|
||||
fprintf(fOut, "%s", method_name.c_str());
|
||||
this->lf(2);
|
||||
}
|
||||
|
||||
// TODO: put in css spec that we can define somewhere else (if markup supports that)
|
||||
// TODO: 50em below should match limt = 80 in formatFunction()
|
||||
this->writePending();
|
||||
fprintf(fOut, "<pre style=\"padding: 1em 1em 1em 1em;"
|
||||
"width: 50em; background-color: #f0f0f0\">\n"
|
||||
"%s\n"
|
||||
"</pre>", formattedStr.c_str());
|
||||
this->lf(2);
|
||||
fTableState = TableState::kNone;
|
||||
fMethod = def;
|
||||
} break;
|
||||
case MarkType::kNoExample:
|
||||
break;
|
||||
case MarkType::kParam: {
|
||||
if (TableState::kNone == fTableState) {
|
||||
this->mdHeaderOut(3);
|
||||
fprintf(fOut,
|
||||
"Parameters\n"
|
||||
"\n"
|
||||
"<table>"
|
||||
);
|
||||
this->lf(1);
|
||||
fTableState = TableState::kRow;
|
||||
}
|
||||
if (TableState::kRow == fTableState) {
|
||||
fprintf(fOut, " <tr>");
|
||||
this->lf(1);
|
||||
fTableState = TableState::kColumn;
|
||||
}
|
||||
TextParser paramParser(def->fFileName, def->fStart, def->fContentStart,
|
||||
def->fLineCount);
|
||||
paramParser.skipWhiteSpace();
|
||||
SkASSERT(paramParser.startsWith("#Param"));
|
||||
paramParser.next(); // skip hash
|
||||
paramParser.skipToNonAlphaNum(); // skip Param
|
||||
paramParser.skipSpace();
|
||||
const char* paramName = paramParser.fChar;
|
||||
paramParser.skipToSpace();
|
||||
fprintf(fOut,
|
||||
" <td><code><strong>%.*s </strong></code></td> <td>",
|
||||
(int) (paramParser.fChar - paramName), paramName);
|
||||
} break;
|
||||
case MarkType::kPlatform:
|
||||
break;
|
||||
case MarkType::kPrivate:
|
||||
break;
|
||||
case MarkType::kReturn:
|
||||
this->mdHeaderOut(3);
|
||||
fprintf(fOut, "Return Value");
|
||||
this->lf(2);
|
||||
break;
|
||||
case MarkType::kRow:
|
||||
if (fInList) {
|
||||
fprintf(fOut, " <tr>");
|
||||
this->lf(1);
|
||||
}
|
||||
break;
|
||||
case MarkType::kSeeAlso:
|
||||
this->mdHeaderOut(3);
|
||||
fprintf(fOut, "See Also");
|
||||
this->lf(2);
|
||||
break;
|
||||
case MarkType::kStdOut: {
|
||||
TextParser code(def);
|
||||
this->mdHeaderOut(4);
|
||||
fprintf(fOut,
|
||||
"Example Output\n"
|
||||
"\n"
|
||||
"~~~~");
|
||||
this->lfAlways(1);
|
||||
code.skipSpace();
|
||||
while (!code.eof()) {
|
||||
const char* end = code.trimmedLineEnd();
|
||||
fprintf(fOut, "%.*s\n", (int) (end - code.fChar), code.fChar);
|
||||
code.skipToLineStart();
|
||||
}
|
||||
fprintf(fOut, "~~~~");
|
||||
this->lf(2);
|
||||
} break;
|
||||
case MarkType::kStruct:
|
||||
fRoot = def->asRoot();
|
||||
this->mdHeaderOut(1);
|
||||
fprintf(fOut, "<a name=\"%s\"></a> Struct %s", def->fName.c_str(), def->fName.c_str());
|
||||
this->lf(1);
|
||||
break;
|
||||
case MarkType::kSubstitute:
|
||||
break;
|
||||
case MarkType::kSubtopic:
|
||||
this->mdHeaderOut(2);
|
||||
fprintf(fOut, "<a name=\"%s\"></a> %s", def->fName.c_str(), printable.c_str());
|
||||
this->lf(2);
|
||||
break;
|
||||
case MarkType::kTable:
|
||||
this->lf(2);
|
||||
break;
|
||||
case MarkType::kTemplate:
|
||||
break;
|
||||
case MarkType::kText:
|
||||
break;
|
||||
case MarkType::kTime:
|
||||
break;
|
||||
case MarkType::kToDo:
|
||||
break;
|
||||
case MarkType::kTopic:
|
||||
this->mdHeaderOut(1);
|
||||
fprintf(fOut, "<a name=\"%s\"></a> %s", this->linkName(def).c_str(),
|
||||
printable.c_str());
|
||||
this->lf(1);
|
||||
break;
|
||||
case MarkType::kTrack:
|
||||
// don't output children
|
||||
return;
|
||||
case MarkType::kTypedef:
|
||||
break;
|
||||
case MarkType::kUnion:
|
||||
break;
|
||||
case MarkType::kVolatile:
|
||||
break;
|
||||
case MarkType::kWidth:
|
||||
break;
|
||||
default:
|
||||
SkDebugf("fatal error: MarkType::k%s unhandled in %s()\n",
|
||||
fBmhParser.fMaps[(int) def->fMarkType].fName, __func__);
|
||||
SkASSERT(0); // handle everything
|
||||
break;
|
||||
}
|
||||
this->childrenOut(def, textStart);
|
||||
switch (def->fMarkType) { // post child work, at least for tables
|
||||
case MarkType::kCode:
|
||||
this->writePending();
|
||||
fprintf(fOut, "</pre>");
|
||||
this->lf(2);
|
||||
break;
|
||||
case MarkType::kColumn:
|
||||
if (fInList) {
|
||||
this->writePending();
|
||||
fprintf(fOut, "</td>");
|
||||
this->lf(1);
|
||||
} else {
|
||||
fprintf(fOut, " ");
|
||||
}
|
||||
break;
|
||||
case MarkType::kDescription:
|
||||
this->writePending();
|
||||
fprintf(fOut, "</div>");
|
||||
fInDescription = false;
|
||||
break;
|
||||
case MarkType::kEnum:
|
||||
case MarkType::kEnumClass:
|
||||
this->lfAlways(2);
|
||||
break;
|
||||
case MarkType::kExample:
|
||||
this->writePending();
|
||||
if (fHasFiddle) {
|
||||
fprintf(fOut, "</fiddle-embed></div>");
|
||||
} else {
|
||||
fprintf(fOut, "</pre>");
|
||||
}
|
||||
this->lf(2);
|
||||
break;
|
||||
case MarkType::kList:
|
||||
fInList = false;
|
||||
this->writePending();
|
||||
fprintf(fOut, "</table>");
|
||||
this->lf(2);
|
||||
break;
|
||||
case MarkType::kLegend: {
|
||||
SkASSERT(def->fChildren.size() == 1);
|
||||
const Definition* row = def->fChildren[0];
|
||||
SkASSERT(MarkType::kRow == row->fMarkType);
|
||||
size_t columnCount = row->fChildren.size();
|
||||
SkASSERT(columnCount > 0);
|
||||
this->writePending();
|
||||
for (size_t index = 0; index < columnCount; ++index) {
|
||||
fprintf(fOut, "| --- ");
|
||||
}
|
||||
fprintf(fOut, " |");
|
||||
this->lf(1);
|
||||
} break;
|
||||
case MarkType::kMethod:
|
||||
fMethod = nullptr;
|
||||
this->lfAlways(2);
|
||||
fprintf(fOut, "---");
|
||||
this->lf(2);
|
||||
break;
|
||||
case MarkType::kConst:
|
||||
case MarkType::kParam:
|
||||
SkASSERT(TableState::kColumn == fTableState);
|
||||
fTableState = TableState::kRow;
|
||||
this->writePending();
|
||||
fprintf(fOut, "</td>\n");
|
||||
fprintf(fOut, " </tr>");
|
||||
this->lf(1);
|
||||
break;
|
||||
case MarkType::kReturn:
|
||||
case MarkType::kSeeAlso:
|
||||
this->lf(2);
|
||||
break;
|
||||
case MarkType::kRow:
|
||||
if (fInList) {
|
||||
fprintf(fOut, " </tr>");
|
||||
} else {
|
||||
fprintf(fOut, "|");
|
||||
}
|
||||
this->lf(1);
|
||||
break;
|
||||
case MarkType::kStruct:
|
||||
fRoot = fRoot->rootParent();
|
||||
break;
|
||||
case MarkType::kTable:
|
||||
this->lf(2);
|
||||
break;
|
||||
case MarkType::kPrivate:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MdOut::mdHeaderOutLF(int depth, int lf) {
|
||||
this->lfAlways(lf);
|
||||
for (int index = 0; index < depth; ++index) {
|
||||
fprintf(fOut, "#");
|
||||
}
|
||||
fprintf(fOut, " ");
|
||||
}
|
||||
|
||||
void MdOut::resolveOut(const char* start, const char* end, BmhParser::Resolvable resolvable) {
|
||||
// FIXME: this needs the markdown character present when the def was defined,
|
||||
// not the last markdown character the parser would have seen...
|
||||
while (fBmhParser.fMC == end[-1]) {
|
||||
--end;
|
||||
}
|
||||
if (start >= end) {
|
||||
return;
|
||||
}
|
||||
string resolved = this->addReferences(start, end, resolvable);
|
||||
trim_end_spaces(resolved);
|
||||
if (resolved.length()) {
|
||||
TextParser paragraph(fFileName, &*resolved.begin(), &*resolved.end(), fLineCount);
|
||||
TextParser original(fFileName, start, end, fLineCount);
|
||||
while (!original.eof() && '\n' == original.peek()) {
|
||||
original.next();
|
||||
}
|
||||
original.skipSpace();
|
||||
while (!paragraph.eof()) {
|
||||
paragraph.skipWhiteSpace();
|
||||
const char* contentStart = paragraph.fChar;
|
||||
paragraph.skipToEndBracket('\n');
|
||||
ptrdiff_t lineLength = paragraph.fChar - contentStart;
|
||||
if (lineLength) {
|
||||
this->writePending();
|
||||
fprintf(fOut, "%.*s", (int) lineLength, contentStart);
|
||||
}
|
||||
int linefeeds = 0;
|
||||
while (lineLength > 0 && '\n' == contentStart[--lineLength]) {
|
||||
++linefeeds;
|
||||
}
|
||||
if (lineLength > 0) {
|
||||
this->nl();
|
||||
}
|
||||
fLinefeeds += linefeeds;
|
||||
if (paragraph.eof()) {
|
||||
break;
|
||||
}
|
||||
if ('\n' == paragraph.next()) {
|
||||
linefeeds = 1;
|
||||
if (!paragraph.eof() && '\n' == paragraph.peek()) {
|
||||
linefeeds = 2;
|
||||
}
|
||||
this->lf(linefeeds);
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
while (end > start && end[0] == '\n') {
|
||||
fprintf(fOut, "\n");
|
||||
--end;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
51
tools/bookmaker/parserCommon.cpp
Normal file
51
tools/bookmaker/parserCommon.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "bookmaker.h"
|
||||
|
||||
bool ParserCommon::parseSetup(const char* path) {
|
||||
this->reset();
|
||||
sk_sp<SkData> data = SkData::MakeFromFileName(path);
|
||||
if (nullptr == data.get()) {
|
||||
SkDebugf("%s missing\n", path);
|
||||
return false;
|
||||
}
|
||||
const char* rawText = (const char*) data->data();
|
||||
bool hasCR = false;
|
||||
size_t dataSize = data->size();
|
||||
for (size_t index = 0; index < dataSize; ++index) {
|
||||
if ('\r' == rawText[index]) {
|
||||
hasCR = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
string name(path);
|
||||
if (hasCR) {
|
||||
vector<char> lfOnly;
|
||||
for (size_t index = 0; index < dataSize; ++index) {
|
||||
char ch = rawText[index];
|
||||
if ('\r' == rawText[index]) {
|
||||
ch = '\n';
|
||||
if ('\n' == rawText[index + 1]) {
|
||||
++index;
|
||||
}
|
||||
}
|
||||
lfOnly.push_back(ch);
|
||||
}
|
||||
fLFOnly[name] = lfOnly;
|
||||
dataSize = lfOnly.size();
|
||||
rawText = &fLFOnly[name].front();
|
||||
}
|
||||
fRawData[name] = data;
|
||||
fStart = rawText;
|
||||
fLine = rawText;
|
||||
fChar = rawText;
|
||||
fEnd = rawText + dataSize;
|
||||
fFileName = string(path);
|
||||
fLineCount = 1;
|
||||
return true;
|
||||
}
|
455
tools/bookmaker/spellCheck.cpp
Normal file
455
tools/bookmaker/spellCheck.cpp
Normal file
@ -0,0 +1,455 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "bookmaker.h"
|
||||
|
||||
#include "SkOSFile.h"
|
||||
#include "SkOSPath.h"
|
||||
|
||||
/*
|
||||
things to do
|
||||
if cap word is beginning of sentence, add it to table as lower-case
|
||||
word must have only a single initial capital
|
||||
|
||||
if word is camel cased, look for :: matches on suffix
|
||||
|
||||
when function crosses lines, whole thing isn't seen as a 'word' e.g., search for largeArc in path
|
||||
|
||||
words in external not seen
|
||||
*/
|
||||
struct CheckEntry {
|
||||
string fFile;
|
||||
int fLine;
|
||||
int fCount;
|
||||
};
|
||||
|
||||
class SpellCheck : public ParserCommon {
|
||||
public:
|
||||
SpellCheck(const BmhParser& bmh) : ParserCommon()
|
||||
, fBmhParser(bmh) {
|
||||
this->reset();
|
||||
}
|
||||
bool check(const char* match);
|
||||
void report();
|
||||
private:
|
||||
enum class TableState {
|
||||
kNone,
|
||||
kRow,
|
||||
kColumn,
|
||||
};
|
||||
|
||||
bool check(Definition* );
|
||||
bool checkable(MarkType markType);
|
||||
void childCheck(const Definition* def, const char* start);
|
||||
void leafCheck(const char* start, const char* end);
|
||||
bool parseFromFile(const char* path) override { return true; }
|
||||
void printCheck(const string& str);
|
||||
|
||||
void reset() override {
|
||||
INHERITED::resetCommon();
|
||||
fMethod = nullptr;
|
||||
fRoot = nullptr;
|
||||
fTableState = TableState::kNone;
|
||||
fInCode = false;
|
||||
fInConst = false;
|
||||
fInDescription = false;
|
||||
fInStdOut = false;
|
||||
}
|
||||
|
||||
void wordCheck(const string& str);
|
||||
void wordCheck(ptrdiff_t len, const char* ch);
|
||||
|
||||
unordered_map<string, CheckEntry> fCode;
|
||||
unordered_map<string, CheckEntry> fColons;
|
||||
unordered_map<string, CheckEntry> fDigits;
|
||||
unordered_map<string, CheckEntry> fDots;
|
||||
unordered_map<string, CheckEntry> fParens; // also hold destructors, operators
|
||||
unordered_map<string, CheckEntry> fUnderscores;
|
||||
unordered_map<string, CheckEntry> fWords;
|
||||
const BmhParser& fBmhParser;
|
||||
Definition* fMethod;
|
||||
RootDefinition* fRoot;
|
||||
TableState fTableState;
|
||||
bool fInCode;
|
||||
bool fInConst;
|
||||
bool fInDescription;
|
||||
bool fInStdOut;
|
||||
typedef ParserCommon INHERITED;
|
||||
};
|
||||
|
||||
/* This doesn't perform a traditional spell or grammar check, although
|
||||
maybe it should. Instead it looks for words used uncommonly and lower
|
||||
case words that match capitalized words that are not sentence starters.
|
||||
It also looks for articles preceeding capitalized words and their
|
||||
modifiers to try to maintain a consistent voice.
|
||||
Maybe also look for passive verbs (e.g. 'is') and suggest active ones?
|
||||
*/
|
||||
void BmhParser::spellCheck(const char* match) const {
|
||||
SpellCheck checker(*this);
|
||||
checker.check(match);
|
||||
checker.report();
|
||||
}
|
||||
|
||||
bool SpellCheck::check(const char* match) {
|
||||
for (const auto& topic : fBmhParser.fTopicMap) {
|
||||
Definition* topicDef = topic.second;
|
||||
if (topicDef->fParent) {
|
||||
continue;
|
||||
}
|
||||
if (!topicDef->isRoot()) {
|
||||
return this->reportError<bool>("expected root topic");
|
||||
}
|
||||
fRoot = topicDef->asRoot();
|
||||
if (string::npos == fRoot->fFileName.rfind(match)) {
|
||||
continue;
|
||||
}
|
||||
this->check(topicDef);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SpellCheck::check(Definition* def) {
|
||||
fFileName = def->fFileName;
|
||||
fLineCount = def->fLineCount;
|
||||
string printable = def->printableName();
|
||||
const char* textStart = def->fContentStart;
|
||||
if (MarkType::kParam != def->fMarkType && MarkType::kConst != def->fMarkType &&
|
||||
TableState::kNone != fTableState) {
|
||||
fTableState = TableState::kNone;
|
||||
}
|
||||
switch (def->fMarkType) {
|
||||
case MarkType::kAlias:
|
||||
break;
|
||||
case MarkType::kAnchor:
|
||||
break;
|
||||
case MarkType::kBug:
|
||||
break;
|
||||
case MarkType::kClass:
|
||||
this->wordCheck(def->fName);
|
||||
break;
|
||||
case MarkType::kCode:
|
||||
fInCode = true;
|
||||
break;
|
||||
case MarkType::kColumn:
|
||||
break;
|
||||
case MarkType::kComment:
|
||||
break;
|
||||
case MarkType::kConst: {
|
||||
fInConst = true;
|
||||
if (TableState::kNone == fTableState) {
|
||||
fTableState = TableState::kRow;
|
||||
}
|
||||
if (TableState::kRow == fTableState) {
|
||||
fTableState = TableState::kColumn;
|
||||
}
|
||||
this->wordCheck(def->fName);
|
||||
const char* lineEnd = strchr(textStart, '\n');
|
||||
this->wordCheck(lineEnd - textStart, textStart);
|
||||
textStart = lineEnd;
|
||||
} break;
|
||||
case MarkType::kDefine:
|
||||
break;
|
||||
case MarkType::kDefinedBy:
|
||||
break;
|
||||
case MarkType::kDeprecated:
|
||||
break;
|
||||
case MarkType::kDescription:
|
||||
fInDescription = true;
|
||||
break;
|
||||
case MarkType::kDoxygen:
|
||||
break;
|
||||
case MarkType::kEnum:
|
||||
case MarkType::kEnumClass:
|
||||
this->wordCheck(def->fName);
|
||||
break;
|
||||
case MarkType::kError:
|
||||
break;
|
||||
case MarkType::kExample:
|
||||
break;
|
||||
case MarkType::kExternal:
|
||||
break;
|
||||
case MarkType::kFile:
|
||||
break;
|
||||
case MarkType::kFormula:
|
||||
break;
|
||||
case MarkType::kFunction:
|
||||
break;
|
||||
case MarkType::kHeight:
|
||||
break;
|
||||
case MarkType::kImage:
|
||||
break;
|
||||
case MarkType::kLegend:
|
||||
break;
|
||||
case MarkType::kList:
|
||||
break;
|
||||
case MarkType::kMember:
|
||||
break;
|
||||
case MarkType::kMethod: {
|
||||
string method_name = def->methodName();
|
||||
string formattedStr = def->formatFunction();
|
||||
if (!def->isClone()) {
|
||||
this->wordCheck(method_name);
|
||||
}
|
||||
fTableState = TableState::kNone;
|
||||
fMethod = def;
|
||||
} break;
|
||||
case MarkType::kParam: {
|
||||
if (TableState::kNone == fTableState) {
|
||||
fTableState = TableState::kRow;
|
||||
}
|
||||
if (TableState::kRow == fTableState) {
|
||||
fTableState = TableState::kColumn;
|
||||
}
|
||||
TextParser paramParser(def->fFileName, def->fStart, def->fContentStart,
|
||||
def->fLineCount);
|
||||
paramParser.skipWhiteSpace();
|
||||
SkASSERT(paramParser.startsWith("#Param"));
|
||||
paramParser.next(); // skip hash
|
||||
paramParser.skipToNonAlphaNum(); // skip Param
|
||||
paramParser.skipSpace();
|
||||
const char* paramName = paramParser.fChar;
|
||||
paramParser.skipToSpace();
|
||||
fInCode = true;
|
||||
this->wordCheck(paramParser.fChar - paramName, paramName);
|
||||
fInCode = false;
|
||||
} break;
|
||||
case MarkType::kPlatform:
|
||||
break;
|
||||
case MarkType::kReturn:
|
||||
break;
|
||||
case MarkType::kRow:
|
||||
break;
|
||||
case MarkType::kSeeAlso:
|
||||
break;
|
||||
case MarkType::kStdOut: {
|
||||
fInStdOut = true;
|
||||
TextParser code(def);
|
||||
code.skipSpace();
|
||||
while (!code.eof()) {
|
||||
const char* end = code.trimmedLineEnd();
|
||||
this->wordCheck(end - code.fChar, code.fChar);
|
||||
code.skipToLineStart();
|
||||
}
|
||||
fInStdOut = false;
|
||||
} break;
|
||||
case MarkType::kStruct:
|
||||
fRoot = def->asRoot();
|
||||
this->wordCheck(def->fName);
|
||||
break;
|
||||
case MarkType::kSubtopic:
|
||||
this->printCheck(printable);
|
||||
break;
|
||||
case MarkType::kTable:
|
||||
break;
|
||||
case MarkType::kTemplate:
|
||||
break;
|
||||
case MarkType::kText:
|
||||
break;
|
||||
case MarkType::kTime:
|
||||
break;
|
||||
case MarkType::kToDo:
|
||||
break;
|
||||
case MarkType::kTopic:
|
||||
this->printCheck(printable);
|
||||
break;
|
||||
case MarkType::kTrack:
|
||||
// don't output children
|
||||
return true;
|
||||
case MarkType::kTypedef:
|
||||
break;
|
||||
case MarkType::kUnion:
|
||||
break;
|
||||
case MarkType::kWidth:
|
||||
break;
|
||||
default:
|
||||
SkASSERT(0); // handle everything
|
||||
break;
|
||||
}
|
||||
this->childCheck(def, textStart);
|
||||
switch (def->fMarkType) { // post child work, at least for tables
|
||||
case MarkType::kCode:
|
||||
fInCode = false;
|
||||
break;
|
||||
case MarkType::kColumn:
|
||||
break;
|
||||
case MarkType::kDescription:
|
||||
fInDescription = false;
|
||||
break;
|
||||
case MarkType::kEnum:
|
||||
case MarkType::kEnumClass:
|
||||
break;
|
||||
case MarkType::kExample:
|
||||
break;
|
||||
case MarkType::kLegend:
|
||||
break;
|
||||
case MarkType::kMethod:
|
||||
fMethod = nullptr;
|
||||
break;
|
||||
case MarkType::kConst:
|
||||
fInConst = false;
|
||||
case MarkType::kParam:
|
||||
SkASSERT(TableState::kColumn == fTableState);
|
||||
fTableState = TableState::kRow;
|
||||
break;
|
||||
case MarkType::kReturn:
|
||||
case MarkType::kSeeAlso:
|
||||
break;
|
||||
case MarkType::kRow:
|
||||
break;
|
||||
case MarkType::kStruct:
|
||||
fRoot = fRoot->rootParent();
|
||||
break;
|
||||
case MarkType::kTable:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SpellCheck::checkable(MarkType markType) {
|
||||
return BmhParser::Resolvable::kYes == fBmhParser.fMaps[(int) markType].fResolve;
|
||||
}
|
||||
|
||||
void SpellCheck::childCheck(const Definition* def, const char* start) {
|
||||
const char* end;
|
||||
fLineCount = def->fLineCount;
|
||||
if (def->isRoot()) {
|
||||
fRoot = const_cast<RootDefinition*>(def->asRoot());
|
||||
}
|
||||
for (auto& child : def->fChildren) {
|
||||
end = child->fStart;
|
||||
if (this->checkable(def->fMarkType)) {
|
||||
this->leafCheck(start, end);
|
||||
}
|
||||
this->check(child);
|
||||
start = child->fTerminator;
|
||||
}
|
||||
if (this->checkable(def->fMarkType)) {
|
||||
end = def->fContentEnd;
|
||||
this->leafCheck(start, end);
|
||||
}
|
||||
}
|
||||
|
||||
void SpellCheck::leafCheck(const char* start, const char* end) {
|
||||
TextParser text("", start, end, fLineCount);
|
||||
do {
|
||||
const char* lineStart = text.fChar;
|
||||
text.skipToAlpha();
|
||||
if (text.eof()) {
|
||||
break;
|
||||
}
|
||||
const char* wordStart = text.fChar;
|
||||
text.fChar = lineStart;
|
||||
text.skipTo(wordStart); // advances line number
|
||||
text.skipToNonAlphaNum();
|
||||
fLineCount = text.fLineCount;
|
||||
string word(wordStart, text.fChar - wordStart);
|
||||
wordCheck(word);
|
||||
} while (!text.eof());
|
||||
}
|
||||
|
||||
void SpellCheck::printCheck(const string& str) {
|
||||
string word;
|
||||
for (std::stringstream stream(str); stream >> word; ) {
|
||||
wordCheck(word);
|
||||
}
|
||||
}
|
||||
|
||||
void SpellCheck::report() {
|
||||
for (auto iter : fWords) {
|
||||
if (string::npos != iter.second.fFile.find("undocumented.bmh")) {
|
||||
continue;
|
||||
}
|
||||
if (string::npos != iter.second.fFile.find("markup.bmh")) {
|
||||
continue;
|
||||
}
|
||||
if (string::npos != iter.second.fFile.find("usingBookmaker.bmh")) {
|
||||
continue;
|
||||
}
|
||||
if (iter.second.fCount == 1) {
|
||||
SkDebugf("%s %s %d\n", iter.first.c_str(), iter.second.fFile.c_str(),
|
||||
iter.second.fLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SpellCheck::wordCheck(const string& str) {
|
||||
bool hasColon = false;
|
||||
bool hasDot = false;
|
||||
bool hasParen = false;
|
||||
bool hasUnderscore = false;
|
||||
bool sawDash = false;
|
||||
bool sawDigit = false;
|
||||
bool sawSpecial = false;
|
||||
SkASSERT(str.length() > 0);
|
||||
SkASSERT(isalpha(str[0]) || '~' == str[0]);
|
||||
for (char ch : str) {
|
||||
if (isalpha(ch) || '-' == ch) {
|
||||
sawDash |= '-' == ch;
|
||||
continue;
|
||||
}
|
||||
bool isColon = ':' == ch;
|
||||
hasColon |= isColon;
|
||||
bool isDot = '.' == ch;
|
||||
hasDot |= isDot;
|
||||
bool isParen = '(' == ch || ')' == ch || '~' == ch || '=' == ch || '!' == ch;
|
||||
hasParen |= isParen;
|
||||
bool isUnderscore = '_' == ch;
|
||||
hasUnderscore |= isUnderscore;
|
||||
if (isColon || isDot || isUnderscore || isParen) {
|
||||
continue;
|
||||
}
|
||||
if (isdigit(ch)) {
|
||||
sawDigit = true;
|
||||
continue;
|
||||
}
|
||||
if ('&' == ch || ',' == ch || ' ' == ch) {
|
||||
sawSpecial = true;
|
||||
continue;
|
||||
}
|
||||
SkASSERT(0);
|
||||
}
|
||||
if (sawSpecial && !hasParen) {
|
||||
SkASSERT(0);
|
||||
}
|
||||
bool inCode = fInCode;
|
||||
if (hasUnderscore && isupper(str[0]) && ('S' != str[0] || 'K' != str[1])
|
||||
&& !hasColon && !hasDot && !hasParen && !fInStdOut && !inCode && !fInConst
|
||||
&& !sawDigit && !sawSpecial && !sawDash) {
|
||||
std::istringstream ss(str);
|
||||
string token;
|
||||
while (std::getline(ss, token, '_')) {
|
||||
this->wordCheck(token);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!hasColon && !hasDot && !hasParen && !hasUnderscore
|
||||
&& !fInStdOut && !inCode && !fInConst && !sawDigit
|
||||
&& islower(str[0]) && isupper(str[1])) {
|
||||
inCode = true;
|
||||
}
|
||||
auto& mappy = hasColon ? fColons :
|
||||
hasDot ? fDots :
|
||||
hasParen ? fParens :
|
||||
hasUnderscore ? fUnderscores :
|
||||
fInStdOut || inCode || fInConst ? fCode :
|
||||
sawDigit ? fDigits : fWords;
|
||||
auto iter = mappy.find(str);
|
||||
if (mappy.end() != iter) {
|
||||
iter->second.fCount += 1;
|
||||
} else {
|
||||
CheckEntry* entry = &mappy[str];
|
||||
entry->fFile = fFileName;
|
||||
entry->fLine = fLineCount;
|
||||
entry->fCount = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void SpellCheck::wordCheck(ptrdiff_t len, const char* ch) {
|
||||
leafCheck(ch, ch + len);
|
||||
}
|
Loading…
Reference in New Issue
Block a user