Browse Source

initial

master
alistair 1 year ago
commit
62c160a0a5
  1. 178
      .clang-format
  2. 155
      .clang-tidy
  3. 6
      .codespellrc
  4. 11
      .github/clang-14.profile
  5. 215
      .github/workflows/ci.yml
  6. 15
      .gitignore
  7. 3
      .gitmodules
  8. 64
      BUILDING.md
  9. 104
      CMakeLists.txt
  10. 163
      CMakePresets.json
  11. 5
      CODE_OF_CONDUCT.md
  12. 19
      CONTRIBUTING.md
  13. 155
      HACKING.md
  14. 19
      README.md
  15. 33
      cmake/coverage.cmake
  16. 28
      cmake/dev-mode.cmake
  17. 112
      cmake/docs-ci.cmake
  18. 46
      cmake/docs.cmake
  19. 21
      cmake/folders.cmake
  20. 8
      cmake/install-rules.cmake
  21. 33
      cmake/lint-targets.cmake
  22. 51
      cmake/lint.cmake
  23. 10
      cmake/prelude.cmake
  24. 6
      cmake/project-is-top-level.cmake
  25. 22
      cmake/spell-targets.cmake
  26. 29
      cmake/spell.cmake
  27. 28
      cmake/variables.cmake
  28. 86
      compile_commands.json
  29. 114
      conan.lock
  30. 19
      conanfile.py
  31. BIN
      database.db
  32. 32
      docs/Doxyfile.in
  33. 6
      docs/conf.py.in
  34. 7
      docs/pages/about.dox
  35. 85
      source/db.cpp
  36. 16
      source/db.hpp
  37. 28
      source/langs.h
  38. 260
      source/lib.cpp
  39. 94
      source/lib.hpp
  40. 90
      source/main.cpp
  41. 33
      test/CMakeLists.txt
  42. 1
      test/source/testts_test.cpp
  43. 32
      tree-sitter-cmake/.github/workflows/build.yml
  44. 3
      tree-sitter-cmake/.gitignore
  45. 121
      tree-sitter-cmake/CMakeLists.txt
  46. 82
      tree-sitter-cmake/README.md
  47. 27
      tree-sitter-cmake/include/tree_sitter/langs.h
  48. 30
      tree-sitter-cmake/tree-sitter-config.cmake

178
.clang-format

@ -0,0 +1,178 @@ @@ -0,0 +1,178 @@
---
Language: Cpp
# BasedOnStyle: Chromium
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: false
AlignConsecutiveAssignments: false
AlignConsecutiveBitFields: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: DontAlign
AlignOperands: DontAlign
AlignTrailingComments: false
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortEnumsOnASingleLine: false
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: false
BinPackParameters: false
BraceWrapping:
AfterCaseLabel: false
AfterClass: true
AfterControlStatement: MultiLine
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterObjCDeclaration: false
AfterStruct: true
AfterUnion: true
AfterExternBlock: true
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: true
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Custom
# BreakBeforeInheritanceComma: true
BreakInheritanceList: BeforeComma
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: true
BreakConstructorInitializers: BeforeComma
BreakAfterJavaFieldAnnotations: true
BreakStringLiterals: true
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: false
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Regroup
IncludeCategories:
# Standard library headers come before anything else
- Regex: '^<[a-z_]+>'
Priority: -1
- Regex: '^<.+\.h(pp)?>'
Priority: 1
- Regex: '^<.*'
Priority: 2
- Regex: '.*'
Priority: 3
IncludeIsMainRegex: ''
IncludeIsMainSourceRegex: ''
IndentCaseLabels: true
IndentCaseBlocks: false
IndentGotoLabels: true
IndentPPDirectives: AfterHash
IndentExternBlock: NoIndent
IndentWidth: 2
IndentWrappedFunctionNames: false
InsertTrailingCommas: Wrapped
JavaScriptQuotes: Double
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Never
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
RawStringFormats:
- Language: Cpp
Delimiters:
- cc
- CC
- cpp
- Cpp
- CPP
- 'c++'
- 'C++'
CanonicalDelimiter: ''
BasedOnStyle: google
- Language: TextProto
Delimiters:
- pb
- PB
- proto
- PROTO
EnclosingFunctions:
- EqualsProto
- EquivToProto
- PARSE_PARTIAL_TEXT_PROTO
- PARSE_TEST_PROTO
- PARSE_TEXT_PROTO
- ParseTextOrDie
- ParseTextProtoOrDie
- ParseTestProto
- ParsePartialTestProto
CanonicalDelimiter: ''
BasedOnStyle: google
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: true
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatementsExceptForEachMacros
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
Standard: Auto
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8
UseCRLF: false
UseTab: Never
WhitespaceSensitiveMacros:
- STRINGIZE
- PP_STRINGIZE
- BOOST_PP_STRINGIZE
...

155
.clang-tidy

@ -0,0 +1,155 @@ @@ -0,0 +1,155 @@
---
# Enable ALL the things! Except not really
# misc-non-private-member-variables-in-classes: the options don't do anything
# modernize-use-nodiscard: too aggressive, attribute is situationally useful
Checks: "*,\
-google-readability-todo,\
-altera-*,\
-fuchsia-*,\
fuchsia-multiple-inheritance,\
-llvm-header-guard,\
-llvm-include-order,\
-llvmlibc-*,\
-modernize-use-nodiscard,\
-misc-non-private-member-variables-in-classes"
WarningsAsErrors: ''
CheckOptions:
- key: 'bugprone-argument-comment.StrictMode'
value: 'true'
# Prefer using enum classes with 2 values for parameters instead of bools
- key: 'bugprone-argument-comment.CommentBoolLiterals'
value: 'true'
- key: 'bugprone-misplaced-widening-cast.CheckImplicitCasts'
value: 'true'
- key: 'bugprone-sizeof-expression.WarnOnSizeOfIntegerExpression'
value: 'true'
- key: 'bugprone-suspicious-string-compare.WarnOnLogicalNotComparison'
value: 'true'
- key: 'readability-simplify-boolean-expr.ChainedConditionalReturn'
value: 'true'
- key: 'readability-simplify-boolean-expr.ChainedConditionalAssignment'
value: 'true'
- key: 'readability-uniqueptr-delete-release.PreferResetCall'
value: 'true'
- key: 'cppcoreguidelines-init-variables.MathHeader'
value: '<cmath>'
- key: 'cppcoreguidelines-narrowing-conversions.PedanticMode'
value: 'true'
- key: 'readability-else-after-return.WarnOnUnfixable'
value: 'true'
- key: 'readability-else-after-return.WarnOnConditionVariables'
value: 'true'
- key: 'readability-inconsistent-declaration-parameter-name.Strict'
value: 'true'
- key: 'readability-qualified-auto.AddConstToQualified'
value: 'true'
- key: 'readability-redundant-access-specifiers.CheckFirstDeclaration'
value: 'true'
# These seem to be the most common identifier styles
- key: 'readability-identifier-naming.AbstractClassCase'
value: 'lower_case'
- key: 'readability-identifier-naming.ClassCase'
value: 'lower_case'
- key: 'readability-identifier-naming.ClassConstantCase'
value: 'lower_case'
- key: 'readability-identifier-naming.ClassMemberCase'
value: 'lower_case'
- key: 'readability-identifier-naming.ClassMethodCase'
value: 'lower_case'
- key: 'readability-identifier-naming.ConstantCase'
value: 'lower_case'
- key: 'readability-identifier-naming.ConstantMemberCase'
value: 'lower_case'
- key: 'readability-identifier-naming.ConstantParameterCase'
value: 'lower_case'
- key: 'readability-identifier-naming.ConstantPointerParameterCase'
value: 'lower_case'
- key: 'readability-identifier-naming.ConstexprFunctionCase'
value: 'lower_case'
- key: 'readability-identifier-naming.ConstexprMethodCase'
value: 'lower_case'
- key: 'readability-identifier-naming.ConstexprVariableCase'
value: 'lower_case'
- key: 'readability-identifier-naming.EnumCase'
value: 'lower_case'
- key: 'readability-identifier-naming.EnumConstantCase'
value: 'lower_case'
- key: 'readability-identifier-naming.FunctionCase'
value: 'lower_case'
- key: 'readability-identifier-naming.GlobalConstantCase'
value: 'lower_case'
- key: 'readability-identifier-naming.GlobalConstantPointerCase'
value: 'lower_case'
- key: 'readability-identifier-naming.GlobalFunctionCase'
value: 'lower_case'
- key: 'readability-identifier-naming.GlobalPointerCase'
value: 'lower_case'
- key: 'readability-identifier-naming.GlobalVariableCase'
value: 'lower_case'
- key: 'readability-identifier-naming.InlineNamespaceCase'
value: 'lower_case'
- key: 'readability-identifier-naming.LocalConstantCase'
value: 'lower_case'
- key: 'readability-identifier-naming.LocalConstantPointerCase'
value: 'lower_case'
- key: 'readability-identifier-naming.LocalPointerCase'
value: 'lower_case'
- key: 'readability-identifier-naming.LocalVariableCase'
value: 'lower_case'
- key: 'readability-identifier-naming.MacroDefinitionCase'
value: 'UPPER_CASE'
- key: 'readability-identifier-naming.MemberCase'
value: 'lower_case'
- key: 'readability-identifier-naming.MethodCase'
value: 'lower_case'
- key: 'readability-identifier-naming.NamespaceCase'
value: 'lower_case'
- key: 'readability-identifier-naming.ParameterCase'
value: 'lower_case'
- key: 'readability-identifier-naming.ParameterPackCase'
value: 'lower_case'
- key: 'readability-identifier-naming.PointerParameterCase'
value: 'lower_case'
- key: 'readability-identifier-naming.PrivateMemberCase'
value: 'lower_case'
- key: 'readability-identifier-naming.PrivateMemberPrefix'
value: 'm_'
- key: 'readability-identifier-naming.PrivateMethodCase'
value: 'lower_case'
- key: 'readability-identifier-naming.ProtectedMemberCase'
value: 'lower_case'
- key: 'readability-identifier-naming.ProtectedMemberPrefix'
value: 'm_'
- key: 'readability-identifier-naming.ProtectedMethodCase'
value: 'lower_case'
- key: 'readability-identifier-naming.PublicMemberCase'
value: 'lower_case'
- key: 'readability-identifier-naming.PublicMethodCase'
value: 'lower_case'
- key: 'readability-identifier-naming.ScopedEnumConstantCase'
value: 'lower_case'
- key: 'readability-identifier-naming.StaticConstantCase'
value: 'lower_case'
- key: 'readability-identifier-naming.StaticVariableCase'
value: 'lower_case'
- key: 'readability-identifier-naming.StructCase'
value: 'lower_case'
- key: 'readability-identifier-naming.TemplateParameterCase'
value: 'CamelCase'
- key: 'readability-identifier-naming.TemplateTemplateParameterCase'
value: 'CamelCase'
- key: 'readability-identifier-naming.TypeAliasCase'
value: 'lower_case'
- key: 'readability-identifier-naming.TypedefCase'
value: 'lower_case'
- key: 'readability-identifier-naming.TypeTemplateParameterCase'
value: 'CamelCase'
- key: 'readability-identifier-naming.UnionCase'
value: 'lower_case'
- key: 'readability-identifier-naming.ValueTemplateParameterCase'
value: 'CamelCase'
- key: 'readability-identifier-naming.VariableCase'
value: 'lower_case'
- key: 'readability-identifier-naming.VirtualMethodCase'
value: 'lower_case'
...

6
.codespellrc

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
[codespell]
builtin = clear,rare,en-GB_to_en-US,names,informal,code
check-filenames =
check-hidden =
skip = */.git,*/build,*/prefix,*/conan
quiet-level = 2

11
.github/clang-14.profile

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
[settings]
arch=x86_64
build_type=Release
compiler=clang
compiler.libcxx=libstdc++11
compiler.version=14
os=Linux
[env]
CC=clang-14
CXX=clang++-14

215
.github/workflows/ci.yml

@ -0,0 +1,215 @@ @@ -0,0 +1,215 @@
name: Continuous Integration
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
lint:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with: { python-version: "3.8" }
- name: Install codespell
run: pip3 install codespell
- name: Lint
run: cmake -D FORMAT_COMMAND=clang-format-14 -P cmake/lint.cmake
- name: Spell check
if: always()
run: cmake -P cmake/spell.cmake
coverage:
needs: [lint]
runs-on: ubuntu-22.04
# To enable coverage, delete the last line from the conditional below and
# edit the "<name>" placeholder to your GitHub name.
# If you do not wish to use codecov, then simply delete this job from the
# workflow.
if: github.repository_owner == '<name>'
&& false
steps:
- uses: actions/checkout@v3
- name: Install LCov
run: sudo apt-get update -q
&& sudo apt-get install lcov -q -y
- name: Install Python
uses: actions/setup-python@v4
with: { python-version: "3.8" }
- name: Install dependencies
run: |
pip3 install "conan<2"
conan profile new default --detect
conan profile update settings.compiler.libcxx=libstdc++11 default
conan install . -b missing
- name: Configure
run: cmake --preset=ci-coverage
- name: Build
run: cmake --build build/coverage -j 2
- name: Test
working-directory: build/coverage
run: ctest --output-on-failure --no-tests=error -j 2
- name: Process coverage info
run: cmake --build build/coverage -t coverage
- name: Submit to codecov.io
uses: codecov/codecov-action@v3
with:
file: build/coverage/coverage.info
sanitize:
needs: [lint]
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- name: Install Python
uses: actions/setup-python@v4
with: { python-version: "3.8" }
- name: Install dependencies
run: |
pip3 install "conan<2"
conan profile new default
cp .github/clang-14.profile "$(conan config home)/profiles/default"
conan install . -b missing
- name: Configure
env: { CXX: clang++-14 }
run: cmake --preset=ci-sanitize
- name: Build
run: cmake --build build/sanitize -j 2
- name: Test
working-directory: build/sanitize
env:
ASAN_OPTIONS: "strict_string_checks=1:\
detect_stack_use_after_return=1:\
check_initialization_order=1:\
strict_init_order=1:\
detect_leaks=1"
UBSAN_OPTIONS: print_stacktrace=1
run: ctest --output-on-failure --no-tests=error -j 2
test:
needs: [lint]
strategy:
matrix:
os: [macos-12, ubuntu-22.04, windows-2022]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Install static analyzers
if: matrix.os == 'ubuntu-22.04'
run: >-
sudo apt-get install clang-tidy-14 cppcheck -y -q
sudo update-alternatives --install
/usr/bin/clang-tidy clang-tidy
/usr/bin/clang-tidy-14 140
- name: Install Python
uses: actions/setup-python@v4
with: { python-version: "3.8" }
- name: Install dependencies
shell: bash
run: |
pip3 install "conan<2"
conan profile new default --detect
if [ ${{ matrix.os }} = ubuntu-22.04 ]; then
conan profile update settings.compiler.libcxx=libstdc++11 default
fi
conan install . -b missing
- name: Setup MultiToolTask
if: matrix.os == 'windows-2022'
run: |
Add-Content "$env:GITHUB_ENV" 'UseMultiToolTask=true'
Add-Content "$env:GITHUB_ENV" 'EnforceProcessCountAcrossBuilds=true'
- name: Configure
shell: pwsh
run: cmake "--preset=ci-$("${{ matrix.os }}".split("-")[0])"
- name: Build
run: cmake --build build --config Release -j 2
- name: Install
run: cmake --install build --config Release --prefix prefix
- name: Test
working-directory: build
run: ctest --output-on-failure --no-tests=error -C Release -j 2
docs:
# Deploy docs only when builds succeed
needs: [sanitize, test]
runs-on: ubuntu-22.04
# To enable, first you have to create an orphaned gh-pages branch:
#
# git switch --orphan gh-pages
# git commit --allow-empty -m "Initial commit"
# git push -u origin gh-pages
#
# Edit the <name> placeholder below to your GitHub name, so this action
# runs only in your repository and no one else's fork. After these, delete
# this comment and the last line in the conditional below.
# If you do not wish to use GitHub Pages for deploying documentation, then
# simply delete this job similarly to the coverage one.
if: github.ref == 'refs/heads/master'
&& github.event_name == 'push'
&& github.repository_owner == '<name>'
&& false
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with: { python-version: "3.8" }
- name: Install m.css dependencies
run: pip3 install jinja2 Pygments
- name: Install Doxygen
run: sudo apt-get update -q
&& sudo apt-get install doxygen -q -y
- name: Build docs
run: cmake "-DPROJECT_SOURCE_DIR=$PWD" "-DPROJECT_BINARY_DIR=$PWD/build"
-P cmake/docs-ci.cmake
- name: Deploy docs
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: build/docs/html

15
.gitignore vendored

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
.idea/
.vs/
.vscode/
build/
cmake/open-cpp-coverage.cmake
cmake-build-*/
conan/
prefix/
CMakeLists.txt.user
CMakeUserPresets.json
conanbuildinfo.txt
conaninfo.txt
graph_info.json
.ccls*/**
.cache/**

3
.gitmodules vendored

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
[submodule "tree-sitter-c"]
path = tree-sitter-c
url = https://github.com/tree-sitter/tree-sitter-c

64
BUILDING.md

@ -0,0 +1,64 @@ @@ -0,0 +1,64 @@
# Building with CMake
## Dependencies
For a list of dependencies, please refer to [conanfile.py](conanfile.py).
## Build
This project doesn't require any special command-line flags to build to keep
things simple.
Here are the steps for building in release mode with a single-configuration
generator, like the Unix Makefiles one:
```sh
cmake -S . -B build -D CMAKE_BUILD_TYPE=Release
cmake --build build
```
Here are the steps for building in release mode with a multi-configuration
generator, like the Visual Studio ones:
```sh
cmake -S . -B build
cmake --build build --config Release
```
### Building with MSVC
Note that MSVC by default is not standards compliant and you need to pass some
flags to make it behave properly. See the `flags-windows` preset in the
[CMakePresets.json](CMakePresets.json) file for the flags and with what
variable to provide them to CMake during configuration.
### Building on Apple Silicon
CMake supports building on Apple Silicon properly since 3.20.1. Make sure you
have the [latest version][1] installed.
## Install
This project doesn't require any special command-line flags to install to keep
things simple. As a prerequisite, the project has to be built with the above
commands already.
The below commands require at least CMake 3.15 to run, because that is the
version in which [Install a Project][2] was added.
Here is the command for installing the release mode artifacts with a
single-configuration generator, like the Unix Makefiles one:
```sh
cmake --install build
```
Here is the command for installing the release mode artifacts with a
multi-configuration generator, like the Visual Studio ones:
```sh
cmake --install build --config Release
```
[1]: https://cmake.org/download/
[2]: https://cmake.org/cmake/help/latest/manual/cmake.1.html#install-a-project

104
CMakeLists.txt

@ -0,0 +1,104 @@ @@ -0,0 +1,104 @@
cmake_minimum_required(VERSION 3.14)
include(cmake/prelude.cmake)
project(
testts
VERSION 0.1.0
DESCRIPTION "Short description"
HOMEPAGE_URL "https://example.com/"
LANGUAGES CXX
)
include(cmake/project-is-top-level.cmake)
include(cmake/variables.cmake)
add_subdirectory(tree-sitter-cmake)
set ("CONAN_DISABLE_CHECK_COMPILER")
# ---- Declare library ----
add_library(
testts_db OBJECT
source/db.cpp
)
add_library(
testts_lib OBJECT
source/lib.cpp
)
target_include_directories(
testts_db ${warning_guard}
PUBLIC
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/source>"
)
target_include_directories(
testts_lib ${warning_guard}
PUBLIC
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/source>"
)
target_compile_features(testts_db PUBLIC cxx_std_20)
target_compile_features(testts_lib PUBLIC cxx_std_20)
find_package(cxxopts REQUIRED)
find_package(SQLiteCpp REQUIRED)
find_package(fmt REQUIRED)
find_package(tree-sitter REQUIRED)
find_package(Tree-Sitter CONFIG)
find_package(Tree-Sitter CONFIG REQUIRED C)
target_link_libraries(testts_db PRIVATE fmt::fmt)
target_link_libraries(testts_db PRIVATE Tree-Sitter::Tree-Sitter Tree-Sitter::Tree-Sitter-C tree-sitter::tree-sitter)
target_link_libraries(testts_db PRIVATE SQLiteCpp)
target_link_libraries(testts_lib PRIVATE fmt::fmt)
target_link_libraries(testts_lib PRIVATE Tree-Sitter::Tree-Sitter Tree-Sitter::Tree-Sitter-C tree-sitter::tree-sitter)
# ---- Declare executable ----
add_executable(testts_exe source/main.cpp)
add_executable(testts::exe ALIAS testts_exe)
set_property(TARGET testts_exe PROPERTY OUTPUT_NAME testts)
target_compile_features(testts_exe PRIVATE cxx_std_20)
target_link_libraries(testts_exe PRIVATE testts_lib)
target_link_libraries(testts_exe PRIVATE testts_db)
target_link_libraries(testts_exe PRIVATE Tree-Sitter::Tree-Sitter Tree-Sitter::Tree-Sitter-C tree-sitter::tree-sitter)
target_link_libraries(testts_exe PRIVATE fmt::fmt)
target_link_libraries(testts_exe PRIVATE cxxopts::cxxopts)
# ---- Install rules ----
if(NOT CMAKE_SKIP_INSTALL_RULES)
include(cmake/install-rules.cmake)
endif()
# ---- Developer mode ----
if(NOT testts_DEVELOPER_MODE)
return()
elseif(NOT PROJECT_IS_TOP_LEVEL)
message(
AUTHOR_WARNING
"Developer mode is intended for developers of testts"
)
endif()
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
add_compile_options(-fdiagnostics-color=always)
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
add_compile_options(-fcolor-diagnostics)
endif()
include(cmake/dev-mode.cmake)

163
CMakePresets.json

@ -0,0 +1,163 @@ @@ -0,0 +1,163 @@
{
"version": 2,
"cmakeMinimumRequired": {
"major": 3,
"minor": 14,
"patch": 0
},
"configurePresets": [
{
"name": "cmake-pedantic",
"hidden": true,
"warnings": {
"dev": true,
"deprecated": true,
"uninitialized": true,
"unusedCli": true,
"systemVars": false
},
"errors": {
"dev": true,
"deprecated": true
}
},
{
"name": "dev-mode",
"hidden": true,
"inherits": "cmake-pedantic",
"cacheVariables": {
"testts_DEVELOPER_MODE": "ON"
}
},
{
"name": "conan",
"hidden": true,
"cacheVariables": {
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/conan/conan_toolchain.cmake",
"CMAKE_POLICY_DEFAULT_CMP0091": "NEW"
}
},
{
"name": "cppcheck",
"hidden": true,
"cacheVariables": {
"CMAKE_CXX_CPPCHECK": "cppcheck;--inline-suppr"
}
},
{
"name": "clang-tidy",
"hidden": true,
"cacheVariables": {
"CMAKE_CXX_CLANG_TIDY": "clang-tidy;--header-filter=^${sourceDir}/"
}
},
{
"name": "ci-std",
"description": "This preset makes sure the project actually builds with at least the specified standard",
"hidden": true,
"cacheVariables": {
"CMAKE_CXX_EXTENSIONS": "OFF",
"CMAKE_CXX_STANDARD": "20",
"CMAKE_CXX_STANDARD_REQUIRED": "ON"
}
},
{
"name": "flags-linux",
"hidden": true,
"cacheVariables": {
"CMAKE_CXX_FLAGS": "-D_FORTIFY_SOURCE=3 -fstack-protector-strong -fcf-protection=full -fstack-clash-protection -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wcast-qual -Wformat=2 -Wundef -Werror=float-equal -Wshadow -Wcast-align -Wunused -Wnull-dereference -Wdouble-promotion -Wimplicit-fallthrough -Wextra-semi -Woverloaded-virtual -Wnon-virtual-dtor -Wold-style-cast",
"CMAKE_EXE_LINKER_FLAGS": "-Wl,--allow-shlib-undefined,--as-needed,-z,noexecstack,-z,relro,-z,now",
"CMAKE_SHARED_LINKER_FLAGS": "-Wl,--allow-shlib-undefined,--as-needed,-z,noexecstack,-z,relro,-z,now"
}
},
{
"name": "flags-darwin",
"hidden": true,
"cacheVariables": {
"CMAKE_CXX_FLAGS": "-fstack-protector-strong -fcf-protection=full -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wcast-qual -Wformat=2 -Wundef -Werror=float-equal -Wshadow -Wcast-align -Wunused -Wnull-dereference -Wdouble-promotion -Wimplicit-fallthrough -Wextra-semi -Woverloaded-virtual -Wnon-virtual-dtor -Wold-style-cast"
}
},
{
"name": "flags-windows",
"description": "Note that all the flags after /W4 are required for MSVC to conform to the language standard",
"hidden": true,
"cacheVariables": {
"CMAKE_CXX_FLAGS": "/sdl /analyze /analyze:external- /guard:cf /utf-8 /w14165 /w44242 /w44254 /w44263 /w34265 /w34287 /w44296 /w44365 /w44388 /w44464 /w14545 /w14546 /w14547 /w14549 /w14555 /w34619 /w34640 /w24826 /w14905 /w14906 /w14928 /w45038 /W4 /permissive- /volatile:iso /Zc:preprocessor /Zc:__cplusplus /Zc:externConstexpr /Zc:throwingNew /EHsc",
"CMAKE_EXE_LINKER_FLAGS": "/machine:x64 /guard:cf"
}
},
{
"name": "ci-linux",
"generator": "Unix Makefiles",
"hidden": true,
"inherits": ["flags-linux", "ci-std"],
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "ci-darwin",
"generator": "Unix Makefiles",
"hidden": true,
"inherits": ["flags-darwin", "ci-std"],
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "ci-win64",
"inherits": ["flags-windows", "ci-std"],
"generator": "Visual Studio 17 2022",
"architecture": "x64",
"hidden": true
},
{
"name": "coverage-linux",
"binaryDir": "${sourceDir}/build/coverage",
"inherits": "ci-linux",
"hidden": true,
"cacheVariables": {
"ENABLE_COVERAGE": "ON",
"CMAKE_BUILD_TYPE": "Coverage",
"CMAKE_CXX_FLAGS_COVERAGE": "-Og -g --coverage -fkeep-inline-functions -fkeep-static-functions",
"CMAKE_EXE_LINKER_FLAGS_COVERAGE": "--coverage",
"CMAKE_SHARED_LINKER_FLAGS_COVERAGE": "--coverage",
"CMAKE_MAP_IMPORTED_CONFIG_COVERAGE": "Coverage;RelWithDebInfo;Release;Debug;"
}
},
{
"name": "ci-coverage",
"inherits": ["coverage-linux", "dev-mode", "conan"],
"cacheVariables": {
"COVERAGE_HTML_COMMAND": ""
}
},
{
"name": "ci-sanitize",
"binaryDir": "${sourceDir}/build/sanitize",
"inherits": ["ci-linux", "dev-mode", "conan"],
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Sanitize",
"CMAKE_CXX_FLAGS_SANITIZE": "-O2 -g -fsanitize=address,undefined -fno-omit-frame-pointer -fno-common",
"CMAKE_MAP_IMPORTED_CONFIG_SANITIZE": "Sanitize;RelWithDebInfo;Release;Debug;"
}
},
{
"name": "ci-build",
"binaryDir": "${sourceDir}/build",
"hidden": true
},
{
"name": "ci-macos",
"inherits": ["ci-build", "ci-darwin", "dev-mode", "conan"]
},
{
"name": "ci-ubuntu",
"inherits": ["ci-build", "ci-linux", "clang-tidy", "conan", "cppcheck", "dev-mode"]
},
{
"name": "ci-windows",
"inherits": ["ci-build", "ci-win64", "dev-mode", "conan"]
}
]
}

5
CODE_OF_CONDUCT.md

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
# Code of Conduct
* You will be judged by your contributions first, and your sense of humor
second.
* Nobody owes you anything.

19
CONTRIBUTING.md

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
# Contributing
<!--
Short overview, rules, general guidelines, notes about pull requests and
style should go here.
-->
## Code of Conduct
Please see the [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md) document.
## Getting started
Helpful notes for developers can be found in the [`HACKING.md`](HACKING.md)
document.
In addition to he above, if you use the presets file as instructed, then you
should NOT check it into source control, just as the CMake documentation
suggests.

155
HACKING.md

@ -0,0 +1,155 @@ @@ -0,0 +1,155 @@
# Hacking
Here is some wisdom to help you build and test this project as a developer and
potential contributor.
If you plan to contribute, please read the [CONTRIBUTING](CONTRIBUTING.md)
guide.
## Developer mode
Build system targets that are only useful for developers of this project are
hidden if the `testts_DEVELOPER_MODE` option is disabled. Enabling this
option makes tests and other developer targets and options available. Not
enabling this option means that you are a consumer of this project and thus you
have no need for these targets and options.
Developer mode is always set to on in CI workflows.
### Presets
This project makes use of [presets][1] to simplify the process of configuring
the project. As a developer, you are recommended to always have the [latest
CMake version][2] installed to make use of the latest Quality-of-Life
additions.
You have a few options to pass `testts_DEVELOPER_MODE` to the configure
command, but this project prefers to use presets.
As a developer, you should create a `CMakeUserPresets.json` file at the root of
the project:
```json
{
"version": 2,
"cmakeMinimumRequired": {
"major": 3,
"minor": 14,
"patch": 0
},
"configurePresets": [
{
"name": "dev",
"binaryDir": "${sourceDir}/build/dev",
"inherits": ["dev-mode", "conan", "ci-<os>"],
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
}
],
"buildPresets": [
{
"name": "dev",
"configurePreset": "dev",
"configuration": "Debug"
}
],
"testPresets": [
{
"name": "dev",
"configurePreset": "dev",
"configuration": "Debug",
"output": {
"outputOnFailure": true
}
}
]
}
```
You should replace `<os>` in your newly created presets file with the name of
the operating system you have, which may be `win64`, `linux` or `darwin`. You
can see what these correspond to in the
[`CMakePresets.json`](CMakePresets.json) file.
`CMakeUserPresets.json` is also the perfect place in which you can put all
sorts of things that you would otherwise want to pass to the configure command
in the terminal.
### Dependency manager
The above preset will make use of the [conan][conan] dependency manager. After
installing it, download the dependencies and generate the necessary CMake
files by running this command in the project root:
```sh
conan install . -s build_type=Debug -b missing
```
Note that if your conan profile does not specify the same compiler used by
CMake, then that could potentially cause issues. See the [conan docs][profile]
on profiles.
[conan]: https://conan.io/
[profile]: https://docs.conan.io/en/latest/using_packages/using_profiles.html
### Configure, build and test
If you followed the above instructions, then you can configure, build and test
the project respectively with the following commands from the project root on
any operating system with any build system:
```sh
cmake --preset=dev
cmake --build --preset=dev
ctest --preset=dev
```
If you are using a compatible editor (e.g. VSCode) or IDE (e.g. CLion, VS), you
will also be able to select the above created user presets for automatic
integration.
Please note that both the build and test commands accept a `-j` flag to specify
the number of jobs to use, which should ideally be specified to the number of
threads your CPU has. You may also want to add that to your preset using the
`jobs` property, see the [presets documentation][1] for more details.
### Developer mode targets
These are targets you may invoke using the build command from above, with an
additional `-t <target>` flag:
#### `coverage`
Available if `ENABLE_COVERAGE` is enabled. This target processes the output of
the previously run tests when built with coverage configuration. The commands
this target runs can be found in the `COVERAGE_TRACE_COMMAND` and
`COVERAGE_HTML_COMMAND` cache variables. The trace command produces an info
file by default, which can be submitted to services with CI integration. The
HTML command uses the trace command's output to generate an HTML document to
`<binary-dir>/coverage_html` by default.
#### `docs`
Available if `BUILD_MCSS_DOCS` is enabled. Builds to documentation using
Doxygen and m.css. The output will go to `<binary-dir>/docs` by default
(customizable using `DOXYGEN_OUTPUT_DIRECTORY`).
#### `format-check` and `format-fix`
These targets run the clang-format tool on the codebase to check errors and to
fix them respectively. Customization available using the `FORMAT_PATTERNS` and
`FORMAT_COMMAND` cache variables.
#### `run-exe`
Runs the executable target `testts_exe`.
#### `spell-check` and `spell-fix`
These targets run the codespell tool on the codebase to check errors and to fix
them respectively. Customization available using the `SPELL_COMMAND` cache
variable.
[1]: https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html
[2]: https://cmake.org/download/

19
README.md

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
# testts
This is the testts project.
# Building and installing
See the [BUILDING](BUILDING.md) document.
# Contributing
See the [CONTRIBUTING](CONTRIBUTING.md) document.
# Licensing
<!--
Please go to https://choosealicense.com/licenses/ and choose a license that
fits your needs. The recommended license for a project of this type is the
GNU AGPLv3.
-->

33
cmake/coverage.cmake

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
# ---- Variables ----
# We use variables separate from what CTest uses, because those have
# customization issues
set(
COVERAGE_TRACE_COMMAND
lcov -c -q
-o "${PROJECT_BINARY_DIR}/coverage.info"
-d "${PROJECT_BINARY_DIR}"
--include "${PROJECT_SOURCE_DIR}/*"
CACHE STRING
"; separated command to generate a trace for the 'coverage' target"
)
set(
COVERAGE_HTML_COMMAND
genhtml --legend -f -q
"${PROJECT_BINARY_DIR}/coverage.info"
-p "${PROJECT_SOURCE_DIR}"
-o "${PROJECT_BINARY_DIR}/coverage_html"
CACHE STRING
"; separated command to generate an HTML report for the 'coverage' target"
)
# ---- Coverage target ----
add_custom_target(
coverage
COMMAND ${COVERAGE_TRACE_COMMAND}
COMMAND ${COVERAGE_HTML_COMMAND}
COMMENT "Generating coverage report"
VERBATIM
)

28
cmake/dev-mode.cmake

@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
include(cmake/folders.cmake)
include(CTest)
if(BUILD_TESTING)
add_subdirectory(test)
endif()
add_custom_target(
run-exe
COMMAND testts_exe
VERBATIM
)
add_dependencies(run-exe testts_exe)
option(BUILD_MCSS_DOCS "Build documentation using Doxygen and m.css" OFF)
if(BUILD_MCSS_DOCS)
include(cmake/docs.cmake)
endif()
option(ENABLE_COVERAGE "Enable coverage support separate from CTest's" OFF)
if(ENABLE_COVERAGE)
include(cmake/coverage.cmake)
endif()
include(cmake/lint-targets.cmake)
include(cmake/spell-targets.cmake)
add_folders(Project)

112
cmake/docs-ci.cmake

@ -0,0 +1,112 @@ @@ -0,0 +1,112 @@
cmake_minimum_required(VERSION 3.14)
foreach(var IN ITEMS PROJECT_BINARY_DIR PROJECT_SOURCE_DIR)
if(NOT DEFINED "${var}")
message(FATAL_ERROR "${var} must be defined")
endif()
endforeach()
set(bin "${PROJECT_BINARY_DIR}")
set(src "${PROJECT_SOURCE_DIR}")
# ---- Dependencies ----
set(mcss_SOURCE_DIR "${bin}/docs/.ci")
if(NOT IS_DIRECTORY "${mcss_SOURCE_DIR}")
file(MAKE_DIRECTORY "${mcss_SOURCE_DIR}")
file(
DOWNLOAD
https://github.com/friendlyanon/m.css/releases/download/release-1/mcss.zip
"${mcss_SOURCE_DIR}/mcss.zip"
STATUS status
EXPECTED_MD5 00cd2757ebafb9bcba7f5d399b3bec7f
)
if(NOT status MATCHES "^0;")
message(FATAL_ERROR "Download failed with ${status}")
endif()
execute_process(
COMMAND "${CMAKE_COMMAND}" -E tar xf mcss.zip
WORKING_DIRECTORY "${mcss_SOURCE_DIR}"
RESULT_VARIABLE result
)
if(NOT result EQUAL "0")
message(FATAL_ERROR "Extraction failed with ${result}")
endif()
file(REMOVE "${mcss_SOURCE_DIR}/mcss.zip")
endif()
find_program(Python3_EXECUTABLE NAMES python3 python)
if(NOT Python3_EXECUTABLE)
message(FATAL_ERROR "Python executable was not found")
endif()
# ---- Process project() call in CMakeLists.txt ----
file(READ "${src}/CMakeLists.txt" content)
string(FIND "${content}" "project(" index)
if(index EQUAL "-1")
message(FATAL_ERROR "Could not find \"project(\"")
endif()
string(SUBSTRING "${content}" "${index}" -1 content)
string(FIND "${content}" "\n)\n" index)
if(index EQUAL "-1")
message(FATAL_ERROR "Could not find \"\\n)\\n\"")
endif()
string(SUBSTRING "${content}" 0 "${index}" content)
file(WRITE "${bin}/docs-ci.project.cmake" "docs_${content}\n)\n")
macro(list_pop_front list out)
list(GET "${list}" 0 "${out}")
list(REMOVE_AT "${list}" 0)
endmacro()
function(docs_project name)
cmake_parse_arguments(PARSE_ARGV 1 "" "" "VERSION;DESCRIPTION;HOMEPAGE_URL" LANGUAGES)
set(PROJECT_NAME "${name}" PARENT_SCOPE)
if(DEFINED _VERSION)
set(PROJECT_VERSION "${_VERSION}" PARENT_SCOPE)
string(REGEX MATCH "^[0-9]+(\\.[0-9]+)*" versions "${_VERSION}")
string(REPLACE . ";" versions "${versions}")
set(suffixes MAJOR MINOR PATCH TWEAK)
while(NOT versions STREQUAL "" AND NOT suffixes STREQUAL "")
list_pop_front(versions version)
list_pop_front(suffixes suffix)
set("PROJECT_VERSION_${suffix}" "${version}" PARENT_SCOPE)
endwhile()
endif()
if(DEFINED _DESCRIPTION)
set(PROJECT_DESCRIPTION "${_DESCRIPTION}" PARENT_SCOPE)
endif()
if(DEFINED _HOMEPAGE_URL)
set(PROJECT_HOMEPAGE_URL "${_HOMEPAGE_URL}" PARENT_SCOPE)
endif()
endfunction()
include("${bin}/docs-ci.project.cmake")
# ---- Generate docs ----
if(NOT DEFINED DOXYGEN_OUTPUT_DIRECTORY)
set(DOXYGEN_OUTPUT_DIRECTORY "${bin}/docs")
endif()
set(out "${DOXYGEN_OUTPUT_DIRECTORY}")
foreach(file IN ITEMS Doxyfile conf.py)
configure_file("${src}/docs/${file}.in" "${bin}/docs/${file}" @ONLY)
endforeach()
set(mcss_script "${mcss_SOURCE_DIR}/documentation/doxygen.py")
set(config "${bin}/docs/conf.py")
file(REMOVE_RECURSE "${out}/html" "${out}/xml")
execute_process(
COMMAND "${Python3_EXECUTABLE}" "${mcss_script}" "${config}"
WORKING_DIRECTORY "${bin}/docs"
RESULT_VARIABLE result
)
if(NOT result EQUAL "0")
message(FATAL_ERROR "m.css returned with ${result}")
endif()

46
cmake/docs.cmake

@ -0,0 +1,46 @@ @@ -0,0 +1,46 @@
# ---- Dependencies ----
set(extract_timestamps "")
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.24")
set(extract_timestamps DOWNLOAD_EXTRACT_TIMESTAMP YES)
endif()
include(FetchContent)
FetchContent_Declare(
mcss URL
https://github.com/friendlyanon/m.css/releases/download/release-1/mcss.zip
URL_MD5 00cd2757ebafb9bcba7f5d399b3bec7f
SOURCE_DIR "${PROJECT_BINARY_DIR}/mcss"
UPDATE_DISCONNECTED YES
${extract_timestamps}
)
FetchContent_MakeAvailable(mcss)
find_package(Python3 3.6 REQUIRED)
# ---- Declare documentation target ----
set(
DOXYGEN_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/docs"
CACHE PATH "Path for the generated Doxygen documentation"
)
set(working_dir "${PROJECT_BINARY_DIR}/docs")
foreach(file IN ITEMS Doxyfile conf.py)
configure_file("docs/${file}.in" "${working_dir}/${file}" @ONLY)
endforeach()
set(mcss_script "${mcss_SOURCE_DIR}/documentation/doxygen.py")
set(config "${working_dir}/conf.py")
add_custom_target(
docs
COMMAND "${CMAKE_COMMAND}" -E remove_directory
"${DOXYGEN_OUTPUT_DIRECTORY}/html"
"${DOXYGEN_OUTPUT_DIRECTORY}/xml"
COMMAND "${Python3_EXECUTABLE}" "${mcss_script}" "${config}"
COMMENT "Building documentation using Doxygen and m.css"
WORKING_DIRECTORY "${working_dir}"
VERBATIM
)

21
cmake/folders.cmake

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
set_property(GLOBAL PROPERTY USE_FOLDERS YES)
# Call this function at the end of a directory scope to assign a folder to
# targets created in that directory. Utility targets will be assigned to the
# UtilityTargets folder, otherwise to the ${name}Targets folder. If a target
# already has a folder assigned, then that target will be skipped.
function(add_folders name)
get_property(targets DIRECTORY PROPERTY BUILDSYSTEM_TARGETS)
foreach(target IN LISTS targets)
get_property(folder TARGET "${target}" PROPERTY FOLDER)
if(DEFINED folder)
continue()
endif()
set(folder Utility)
get_property(type TARGET "${target}" PROPERTY TYPE)
if(NOT type STREQUAL "UTILITY")
set(folder "${name}")
endif()
set_property(TARGET "${target}" PROPERTY FOLDER "${folder}Targets")
endforeach()
endfunction()

8
cmake/install-rules.cmake

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
install(
TARGETS testts_exe
RUNTIME COMPONENT testts_Runtime
)
if(PROJECT_IS_TOP_LEVEL)
include(CPack)
endif()

33
cmake/lint-targets.cmake

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
set(
FORMAT_PATTERNS
source/*.cpp source/*.hpp
include/*.hpp
test/*.cpp test/*.hpp
CACHE STRING
"; separated patterns relative to the project source dir to format"
)
set(FORMAT_COMMAND clang-format CACHE STRING "Formatter to use")
add_custom_target(
format-check
COMMAND "${CMAKE_COMMAND}"
-D "FORMAT_COMMAND=${FORMAT_COMMAND}"
-D "PATTERNS=${FORMAT_PATTERNS}"
-P "${PROJECT_SOURCE_DIR}/cmake/lint.cmake"
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
COMMENT "Linting the code"
VERBATIM
)
add_custom_target(
format-fix
COMMAND "${CMAKE_COMMAND}"
-D "FORMAT_COMMAND=${FORMAT_COMMAND}"
-D "PATTERNS=${FORMAT_PATTERNS}"
-D FIX=YES
-P "${PROJECT_SOURCE_DIR}/cmake/lint.cmake"
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
COMMENT "Fixing the code"
VERBATIM
)

51
cmake/lint.cmake

@ -0,0 +1,51 @@ @@ -0,0 +1,51 @@
cmake_minimum_required(VERSION 3.14)
macro(default name)
if(NOT DEFINED "${name}")
set("${name}" "${ARGN}")
endif()
endmacro()
default(FORMAT_COMMAND clang-format)
default(
PATTERNS
source/*.cpp source/*.hpp
include/*.hpp
test/*.cpp test/*.hpp
)
default(FIX NO)
set(flag --output-replacements-xml)
set(args OUTPUT_VARIABLE output)
if(FIX)
set(flag -i)
set(args "")
endif()
file(GLOB_RECURSE files ${PATTERNS})
set(badly_formatted "")
set(output "")
string(LENGTH "${CMAKE_SOURCE_DIR}/" path_prefix_length)
foreach(file IN LISTS files)
execute_process(
COMMAND "${FORMAT_COMMAND}" --style=file "${flag}" "${file}"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
RESULT_VARIABLE result
${args}
)
if(NOT result EQUAL "0")
message(FATAL_ERROR "'${file}': formatter returned with ${result}")
endif()
if(NOT FIX AND output MATCHES "\n<replacement offset")
string(SUBSTRING "${file}" "${path_prefix_length}" -1 relative_file)
list(APPEND badly_formatted "${relative_file}")
endif()
set(output "")
endforeach()
if(NOT badly_formatted STREQUAL "")
list(JOIN badly_formatted "\n" bad_list)
message("The following files are badly formatted:\n\n${bad_list}\n")
message(FATAL_ERROR "Run again with FIX=YES to fix these files.")
endif()

10
cmake/prelude.cmake

@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
# ---- In-source guard ----
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
message(
FATAL_ERROR
"In-source builds are not supported. "
"Please read the BUILDING document before trying to build this project. "
"You may need to delete 'CMakeCache.txt' and 'CMakeFiles/' first."
)
endif()

6
cmake/project-is-top-level.cmake

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
# This variable is set by project() in CMake 3.21+
string(
COMPARE EQUAL
"${CMAKE_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}"
PROJECT_IS_TOP_LEVEL
)

22
cmake/spell-targets.cmake

@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
set(SPELL_COMMAND codespell CACHE STRING "Spell checker to use")
add_custom_target(
spell-check
COMMAND "${CMAKE_COMMAND}"
-D "SPELL_COMMAND=${SPELL_COMMAND}"
-P "${PROJECT_SOURCE_DIR}/cmake/spell.cmake"
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
COMMENT "Checking spelling"
VERBATIM
)
add_custom_target(
spell-fix
COMMAND "${CMAKE_COMMAND}"
-D "SPELL_COMMAND=${SPELL_COMMAND}"
-D FIX=YES
-P "${PROJECT_SOURCE_DIR}/cmake/spell.cmake"
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
COMMENT "Fixing spelling errors"
VERBATIM
)

29
cmake/spell.cmake

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
cmake_minimum_required(VERSION 3.14)
macro(default name)
if(NOT DEFINED "${name}")
set("${name}" "${ARGN}")
endif()
endmacro()
default(SPELL_COMMAND codespell)
default(FIX NO)
set(flag "")
if(FIX)
set(flag -w)
endif()
execute_process(
COMMAND "${SPELL_COMMAND}" ${flag}
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
RESULT_VARIABLE result
)
if(result EQUAL "65")
message(FATAL_ERROR "Run again with FIX=YES to fix these errors.")
elseif(result EQUAL "64")
message(FATAL_ERROR "Spell checker printed the usage info. Bad arguments?")
elseif(NOT result EQUAL "0")
message(FATAL_ERROR "Spell checker returned with ${result}")
endif()

28
cmake/variables.cmake

@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
# ---- Developer mode ----
# Developer mode enables targets and code paths in the CMake scripts that are
# only relevant for the developer(s) of testts
# Targets necessary to build the project must be provided unconditionally, so
# consumers can trivially build and package the project
if(PROJECT_IS_TOP_LEVEL)
option(testts_DEVELOPER_MODE "Enable developer mode" OFF)
endif()
# ---- Warning guard ----
# target_include_directories with the SYSTEM modifier will request the compiler
# to omit warnings from the provided paths, if the compiler supports that
# This is to provide a user experience similar to find_package when
# add_subdirectory or FetchContent is used to consume this project
set(warning_guard "")
if(NOT PROJECT_IS_TOP_LEVEL)
option(
testts_INCLUDES_WITH_SYSTEM
"Use SYSTEM modifier for testts's includes, disabling warnings"
ON
)
mark_as_advanced(testts_INCLUDES_WITH_SYSTEM)
if(testts_INCLUDES_WITH_SYSTEM)
set(warning_guard SYSTEM)
endif()
endif()

86
compile_commands.json

@ -0,0 +1,86 @@ @@ -0,0 +1,86 @@
[
{
"directory": "/home/alistair/Documents/programming/testts/build/dev",
"command": "/usr/bin/c++ -D_GLIBCXX_USE_CXX11_ABI=0 -I/home/alistair/Documents/programming/testts/source -isystem /home/alistair/.conan/data/fmt/9.1.0/_/_/package/e0e003fc38e6dcc03cdfbaf52057d71baa23adad/include -isystem /home/alistair/.conan/data/tree-sitter/0.20.6/_/_/package/d13c97721d7bdc192ca6529684f2b79beeae8a7c/include -isystem /home/alistair/.conan/data/sqlitecpp/3.2.1/_/_/package/07edb47a886c224dfcbe7d7df487e4e73aa2ee6e/include -isystem /home/alistair/.conan/data/sqlite3/3.40.0/_/_/package/423c3ee42011b37f8a7a6a2c28aa0edfab60ff4d/include -D_FORTIFY_SOURCE=3 -fstack-protector-strong -fcf-protection=full -fstack-clash-protection -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wcast-qual -Wformat=2 -Wundef -Werror=float-equal -Wshadow -Wcast-align -Wunused -Wnull-dereference -Wdouble-promotion -Wimplicit-fallthrough -Wextra-semi -Woverloaded-virtual -Wnon-virtual-dtor -Wold-style-cast -g -std=c++20 -o CMakeFiles/testts_lib.dir/source/lib.cpp.o -c /home/alistair/Documents/programming/testts/source/lib.cpp",
"file": "/home/alistair/Documents/programming/testts/source/lib.cpp",
"output": "CMakeFiles/testts_lib.dir/source/lib.cpp.o"
},
{
"directory": "/home/alistair/Documents/programming/testts/build/dev",
"command": "/usr/bin/c++ -D_GLIBCXX_USE_CXX11_ABI=0 -I/home/alistair/Documents/programming/testts/source -isystem /home/alistair/.conan/data/tree-sitter/0.20.6/_/_/package/d13c97721d7bdc192ca6529684f2b79beeae8a7c/include -isystem /home/alistair/.conan/data/fmt/9.1.0/_/_/package/e0e003fc38e6dcc03cdfbaf52057d71baa23adad/include -isystem /home/alistair/.conan/data/sqlitecpp/3.2.1/_/_/package/07edb47a886c224dfcbe7d7df487e4e73aa2ee6e/include -isystem /home/alistair/.conan/data/sqlite3/3.40.0/_/_/package/423c3ee42011b37f8a7a6a2c28aa0edfab60ff4d/include -isystem /home/alistair/.conan/data/cxxopts/3.0.0/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include -D_FORTIFY_SOURCE=3 -fstack-protector-strong -fcf-protection=full -fstack-clash-protection -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wcast-qual -Wformat=2 -Wundef -Werror=float-equal -Wshadow -Wcast-align -Wunused -Wnull-dereference -Wdouble-promotion -Wimplicit-fallthrough -Wextra-semi -Woverloaded-virtual -Wnon-virtual-dtor -Wold-style-cast -g -std=c++20 -o CMakeFiles/testts_exe.dir/source/main.cpp.o -c /home/alistair/Documents/programming/testts/source/main.cpp",
"file": "/home/alistair/Documents/programming/testts/source/main.cpp",
"output": "CMakeFiles/testts_exe.dir/source/main.cpp.o"
},
{
"directory": "/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake",
"command": "/usr/bin/cc -D_GLIBCXX_USE_CXX11_ABI=0 -I/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-c/src -m64 -g -std=gnu11 -o CMakeFiles/Tree-Sitter-C.dir/tree-sitter-c/src/parser.c.o -c /home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-c/src/parser.c",
"file": "/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-c/src/parser.c",
"output": "tree-sitter-cmake/CMakeFiles/Tree-Sitter-C.dir/tree-sitter-c/src/parser.c.o"
},
{
"directory": "/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake",
"command": "/usr/bin/cc -D_GLIBCXX_USE_CXX11_ABI=0 -I/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter/lib/src -I/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter/lib/include -m64 -g -std=gnu11 -o CMakeFiles/Tree-Sitter.dir/tree-sitter/lib/src/lib.c.o -c /home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter/lib/src/lib.c",
"file": "/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter/lib/src/lib.c",
"output": "tree-sitter-cmake/CMakeFiles/Tree-Sitter.dir/tree-sitter/lib/src/lib.c.o"
},
{
"directory": "/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake",
"command": "/usr/bin/cc -D_GLIBCXX_USE_CXX11_ABI=0 -I/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-c-sharp/src -m64 -g -std=gnu11 -o CMakeFiles/Tree-Sitter-C-Sharp.dir/tree-sitter-c-sharp/src/parser.c.o -c /home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-c-sharp/src/parser.c",
"file": "/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-c-sharp/src/parser.c",
"output": "tree-sitter-cmake/CMakeFiles/Tree-Sitter-C-Sharp.dir/tree-sitter-c-sharp/src/parser.c.o"
},
{
"directory": "/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake",
"command": "/usr/bin/cc -D_GLIBCXX_USE_CXX11_ABI=0 -I/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-cpp/src -m64 -g -std=gnu11 -o CMakeFiles/Tree-Sitter-CPP.dir/tree-sitter-cpp/src/parser.c.o -c /home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-cpp/src/parser.c",
"file": "/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-cpp/src/parser.c",
"output": "tree-sitter-cmake/CMakeFiles/Tree-Sitter-CPP.dir/tree-sitter-cpp/src/parser.c.o"
},
{
"directory": "/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake",
"command": "/usr/bin/cc -D_GLIBCXX_USE_CXX11_ABI=0 -I/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-go/src -m64 -g -std=gnu11 -o CMakeFiles/Tree-Sitter-Go.dir/tree-sitter-go/src/parser.c.o -c /home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-go/src/parser.c",
"file": "/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-go/src/parser.c",
"output": "tree-sitter-cmake/CMakeFiles/Tree-Sitter-Go.dir/tree-sitter-go/src/parser.c.o"
},
{
"directory": "/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake",
"command": "/usr/bin/cc -D_GLIBCXX_USE_CXX11_ABI=0 -I/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-java/src -m64 -g -std=gnu11 -o CMakeFiles/Tree-Sitter-Java.dir/tree-sitter-java/src/parser.c.o -c /home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-java/src/parser.c",
"file": "/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-java/src/parser.c",
"output": "tree-sitter-cmake/CMakeFiles/Tree-Sitter-Java.dir/tree-sitter-java/src/parser.c.o"
},
{
"directory": "/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake",
"command": "/usr/bin/cc -D_GLIBCXX_USE_CXX11_ABI=0 -I/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-javascript/src -m64 -g -std=gnu11 -o CMakeFiles/Tree-Sitter-JavaScript.dir/tree-sitter-javascript/src/parser.c.o -c /home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-javascript/src/parser.c",
"file": "/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-javascript/src/parser.c",
"output": "tree-sitter-cmake/CMakeFiles/Tree-Sitter-JavaScript.dir/tree-sitter-javascript/src/parser.c.o"
},
{
"directory": "/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake",
"command": "/usr/bin/cc -D_GLIBCXX_USE_CXX11_ABI=0 -I/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-python/src -m64 -g -std=gnu11 -o CMakeFiles/Tree-Sitter-Python.dir/tree-sitter-python/src/parser.c.o -c /home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-python/src/parser.c",
"file": "/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-python/src/parser.c",
"output": "tree-sitter-cmake/CMakeFiles/Tree-Sitter-Python.dir/tree-sitter-python/src/parser.c.o"
},
{
"directory": "/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake",
"command": "/usr/bin/cc -D_GLIBCXX_USE_CXX11_ABI=0 -I/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-rust/src -m64 -g -std=gnu11 -o CMakeFiles/Tree-Sitter-Rust.dir/tree-sitter-rust/src/parser.c.o -c /home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-rust/src/parser.c",
"file": "/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-rust/src/parser.c",
"output": "tree-sitter-cmake/CMakeFiles/Tree-Sitter-Rust.dir/tree-sitter-rust/src/parser.c.o"
},
{
"directory": "/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake",
"command": "/usr/bin/cc -D_GLIBCXX_USE_CXX11_ABI=0 -I/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-typescript/typescript/src -m64 -g -std=gnu11 -o CMakeFiles/Tree-Sitter-TypeScript.dir/tree-sitter-typescript/typescript/src/parser.c.o -c /home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-typescript/typescript/src/parser.c",
"file": "/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-typescript/typescript/src/parser.c",
"output": "tree-sitter-cmake/CMakeFiles/Tree-Sitter-TypeScript.dir/tree-sitter-typescript/typescript/src/parser.c.o"
},
{
"directory": "/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake",
"command": "/usr/bin/cc -D_GLIBCXX_USE_CXX11_ABI=0 -I/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-typescript/tsx/src -m64 -g -std=gnu11 -o CMakeFiles/Tree-Sitter-TSX.dir/tree-sitter-typescript/tsx/src/parser.c.o -c /home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-typescript/tsx/src/parser.c",
"file": "/home/alistair/Documents/programming/testts/build/dev/tree-sitter-cmake/tree-sitter-typescript/tsx/src/parser.c",
"output": "tree-sitter-cmake/CMakeFiles/Tree-Sitter-TSX.dir/tree-sitter-typescript/tsx/src/parser.c.o"
},
{
"directory": "/home/alistair/Documents/programming/testts/build/dev/test",
"command": "/usr/bin/c++ -D_GLIBCXX_USE_CXX11_ABI=0 -I/home/alistair/Documents/programming/testts/source -isystem /home/alistair/.conan/data/catch2/3.3.1/_/_/package/6b952e07b144105007d6beb6165b6d6b25ff4787/include -D_FORTIFY_SOURCE=3 -fstack-protector-strong -fcf-protection=full -fstack-clash-protection -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wcast-qual -Wformat=2 -Wundef -Werror=float-equal -Wshadow -Wcast-align -Wunused -Wnull-dereference -Wdouble-promotion -Wimplicit-fallthrough -Wextra-semi -Woverloaded-virtual -Wnon-virtual-dtor -Wold-style-cast -g -std=c++20 -fdiagnostics-color=always -o CMakeFiles/testts_test.dir/source/testts_test.cpp.o -c /home/alistair/Documents/programming/testts/test/source/testts_test.cpp",
"file": "/home/alistair/Documents/programming/testts/test/source/testts_test.cpp",
"output": "test/CMakeFiles/testts_test.dir/source/testts_test.cpp.o"
}
]

114
conan.lock

@ -0,0 +1,114 @@ @@ -0,0 +1,114 @@
{
"graph_lock": {
"nodes": {
"0": {
"options": "cxxopts:unicode=False\nfmt:fPIC=True\nfmt:header_only=False\nfmt:shared=False\nfmt:with_fmt_alias=False\nfmt:with_os_api=True\nhttp_parser:fPIC=True\nhttp_parser:shared=False\nlibgit2:fPIC=True\nlibgit2:shared=False\nlibgit2:threadsafe=True\nlibgit2:with_https=openssl\nlibgit2:with_libssh2=True\nlibgit2:with_ntlmclient=True\nlibgit2:with_regex=builtin\nlibgit2:with_sha1=collisiondetection\nlibssh2:crypto_backend=openssl\nlibssh2:enable_crypt_none=False\nlibssh2:enable_debug_logging=False\nlibssh2:enable_mac_none=False\nlibssh2:fPIC=True\nlibssh2:shared=False\nlibssh2:with_zlib=True\nopenssl:386=False\nopenssl:enable_weak_ssl_ciphers=False\nopenssl:fPIC=True\nopenssl:no_aria=False\nopenssl:no_asm=False\nopenssl:no_async=False\nopenssl:no_bf=False\nopenssl:no_blake2=False\nopenssl:no_camellia=False\nopenssl:no_cast=False\nopenssl:no_chacha=False\nopenssl:no_cms=False\nopenssl:no_comp=False\nopenssl:no_ct=False\nopenssl:no_deprecated=False\nopenssl:no_des=False\nopenssl:no_dgram=False\nopenssl:no_dh=False\nopenssl:no_dsa=False\nopenssl:no_dso=False\nopenssl:no_ec=False\nopenssl:no_ecdh=False\nopenssl:no_ecdsa=False\nopenssl:no_engine=False\nopenssl:no_filenames=False\nopenssl:no_gost=False\nopenssl:no_hmac=False\nopenssl:no_idea=False\nopenssl:no_md4=False\nopenssl:no_md5=False\nopenssl:no_mdc2=False\nopenssl:no_ocsp=False\nopenssl:no_pinshared=False\nopenssl:no_rc2=False\nopenssl:no_rfc3779=False\nopenssl:no_rmd160=False\nopenssl:no_rsa=False\nopenssl:no_seed=False\nopenssl:no_sha=False\nopenssl:no_sm2=False\nopenssl:no_sm3=False\nopenssl:no_sm4=False\nopenssl:no_sock=False\nopenssl:no_srp=False\nopenssl:no_srtp=False\nopenssl:no_sse2=False\nopenssl:no_ssl=False\nopenssl:no_ssl3=False\nopenssl:no_stdio=False\nopenssl:no_tests=False\nopenssl:no_threads=False\nopenssl:no_tls1=False\nopenssl:no_ts=False\nopenssl:no_whirlpool=False\nopenssl:openssldir=None\nopenssl:shared=False\nsqlite3:build_executable=True\nsqlite3:disable_gethostuuid=False\nsqlite3:enable_column_metadata=True\nsqlite3:enable_dbpage_vtab=False\nsqlite3:enable_dbstat_vtab=False\nsqlite3:enable_default_secure_delete=False\nsqlite3:enable_default_vfs=True\nsqlite3:enable_explain_comments=False\nsqlite3:enable_fts3=False\nsqlite3:enable_fts3_parenthesis=False\nsqlite3:enable_fts4=False\nsqlite3:enable_fts5=False\nsqlite3:enable_json1=False\nsqlite3:enable_math_functions=True\nsqlite3:enable_preupdate_hook=False\nsqlite3:enable_rtree=True\nsqlite3:enable_soundex=False\nsqlite3:enable_unlock_notify=True\nsqlite3:fPIC=True\nsqlite3:max_blob_size=None\nsqlite3:max_column=None\nsqlite3:max_variable_number=None\nsqlite3:omit_deprecated=False\nsqlite3:omit_load_extension=False\nsqlite3:shared=False\nsqlite3:threadsafe=1\nsqlite3:use_alloca=False\nsqlitecpp:fPIC=True\nsqlitecpp:shared=False\nsqlitecpp:stack_protection=True\ntree-sitter:fPIC=True\ntree-sitter:shared=False\nzlib:fPIC=True\nzlib:shared=False",
"requires": [
"1",
"2",
"3",
"4",
"6"
],
"build_requires": [
"11"
],
"path": "conanfile.py",
"context": "host"
},
"1": {
"ref": "fmt/9.1.0",
"options": "fPIC=True\nheader_only=False\nshared=False\nwith_fmt_alias=False\nwith_os_api=True",
"package_id": "e0e003fc38e6dcc03cdfbaf52057d71baa23adad",
"prev": "0",
"context": "host"
},
"2": {
"ref": "cxxopts/3.0.0",
"options": "unicode=False",
"package_id": "5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9",
"prev": "0",
"context": "host"
},
"3": {
"ref": "tree-sitter/0.20.6",
"options": "fPIC=True\nshared=False",
"package_id": "d13c97721d7bdc192ca6529684f2b79beeae8a7c",
"prev": "0",
"context": "host"
},
"4": {
"ref": "sqlitecpp/3.2.1",
"options": "fPIC=True\nshared=False\nstack_protection=True\nsqlite3:build_executable=True\nsqlite3:disable_gethostuuid=False\nsqlite3:enable_column_metadata=True\nsqlite3:enable_dbpage_vtab=False\nsqlite3:enable_dbstat_vtab=False\nsqlite3:enable_default_secure_delete=False\nsqlite3:enable_default_vfs=True\nsqlite3:enable_explain_comments=False\nsqlite3:enable_fts3=False\nsqlite3:enable_fts3_parenthesis=False\nsqlite3:enable_fts4=False\nsqlite3:enable_fts5=False\nsqlite3:enable_json1=False\nsqlite3:enable_math_functions=True\nsqlite3:enable_preupdate_hook=False\nsqlite3:enable_rtree=True\nsqlite3:enable_soundex=False\nsqlite3:enable_unlock_notify=True\nsqlite3:fPIC=True\nsqlite3:max_blob_size=None\nsqlite3:max_column=None\nsqlite3:max_variable_number=None\nsqlite3:omit_deprecated=False\nsqlite3:omit_load_extension=False\nsqlite3:shared=False\nsqlite3:threadsafe=1\nsqlite3:use_alloca=False",
"package_id": "07edb47a886c224dfcbe7d7df487e4e73aa2ee6e",
"prev": "0",
"requires": [
"5"
],
"context": "host"
},
"5": {
"ref": "sqlite3/3.40.0",
"options": "build_executable=True\ndisable_gethostuuid=False\nenable_column_metadata=True\nenable_dbpage_vtab=False\nenable_dbstat_vtab=False\nenable_default_secure_delete=False\nenable_default_vfs=True\nenable_explain_comments=False\nenable_fts3=False\nenable_fts3_parenthesis=False\nenable_fts4=False\nenable_fts5=False\nenable_json1=False\nenable_math_functions=True\nenable_preupdate_hook=False\nenable_rtree=True\nenable_soundex=False\nenable_unlock_notify=True\nfPIC=True\nmax_blob_size=None\nmax_column=None\nmax_variable_number=None\nomit_deprecated=False\nomit_load_extension=False\nshared=False\nthreadsafe=1\nuse_alloca=False",
"package_id": "423c3ee42011b37f8a7a6a2c28aa0edfab60ff4d",
"prev": "0",
"context": "host"
},
"6": {
"ref": "libgit2/1.5.0",
"options": "fPIC=True\nshared=False\nthreadsafe=True\nwith_https=openssl\nwith_libssh2=True\nwith_ntlmclient=True\nwith_regex=builtin\nwith_sha1=collisiondetection\nhttp_parser:fPIC=True\nhttp_parser:shared=False\nlibssh2:crypto_backend=openssl\nlibssh2:enable_crypt_none=False\nlibssh2:enable_debug_logging=False\nlibssh2:enable_mac_none=False\nlibssh2:fPIC=True\nlibssh2:shared=False\nlibssh2:with_zlib=True\nopenssl:386=False\nopenssl:enable_weak_ssl_ciphers=False\nopenssl:fPIC=True\nopenssl:no_aria=False\nopenssl:no_asm=False\nopenssl:no_async=False\nopenssl:no_bf=False\nopenssl:no_blake2=False\nopenssl:no_camellia=False\nopenssl:no_cast=False\nopenssl:no_chacha=False\nopenssl:no_cms=False\nopenssl:no_comp=False\nopenssl:no_ct=False\nopenssl:no_deprecated=False\nopenssl:no_des=False\nopenssl:no_dgram=False\nopenssl:no_dh=False\nopenssl:no_dsa=False\nopenssl:no_dso=False\nopenssl:no_ec=False\nopenssl:no_ecdh=False\nopenssl:no_ecdsa=False\nopenssl:no_engine=False\nopenssl:no_filenames=False\nopenssl:no_gost=False\nopenssl:no_hmac=False\nopenssl:no_idea=False\nopenssl:no_md4=False\nopenssl:no_md5=False\nopenssl:no_mdc2=False\nopenssl:no_ocsp=False\nopenssl:no_pinshared=False\nopenssl:no_rc2=False\nopenssl:no_rfc3779=False\nopenssl:no_rmd160=False\nopenssl:no_rsa=False\nopenssl:no_seed=False\nopenssl:no_sha=False\nopenssl:no_sm2=False\nopenssl:no_sm3=False\nopenssl:no_sm4=False\nopenssl:no_sock=False\nopenssl:no_srp=False\nopenssl:no_srtp=False\nopenssl:no_sse2=False\nopenssl:no_ssl=False\nopenssl:no_ssl3=False\nopenssl:no_stdio=False\nopenssl:no_tests=False\nopenssl:no_threads=False\nopenssl:no_tls1=False\nopenssl:no_ts=False\nopenssl:no_whirlpool=False\nopenssl:openssldir=None\nopenssl:shared=False\nzlib:fPIC=True\nzlib:shared=False",
"package_id": "6ed20d33bf0d208a8b0717399dc35c204becaa5a",
"prev": "0",
"requires": [
"7",
"8",
"9",
"10"
],
"context": "host"
},
"7": {
"ref": "zlib/1.2.12",
"options": "fPIC=True\nshared=False",
"package_id": "d13c97721d7bdc192ca6529684f2b79beeae8a7c",
"prev": "0",
"context": "host"
},
"8": {
"ref": "http_parser/2.9.4",
"options": "fPIC=True\nshared=False",
"package_id": "d13c97721d7bdc192ca6529684f2b79beeae8a7c",
"prev": "0",
"context": "host"
},
"9": {
"ref": "libssh2/1.10.0",
"options": "crypto_backend=openssl\nenable_crypt_none=False\nenable_debug_logging=False\nenable_mac_none=False\nfPIC=True\nshared=False\nwith_zlib=True\nopenssl:386=False\nopenssl:enable_weak_ssl_ciphers=False\nopenssl:fPIC=True\nopenssl:no_aria=False\nopenssl:no_asm=False\nopenssl:no_async=False\nopenssl:no_bf=False\nopenssl:no_blake2=False\nopenssl:no_camellia=False\nopenssl:no_cast=False\nopenssl:no_chacha=False\nopenssl:no_cms=False\nopenssl:no_comp=False\nopenssl:no_ct=False\nopenssl:no_deprecated=False\nopenssl:no_des=False\nopenssl:no_dgram=False\nopenssl:no_dh=False\nopenssl:no_dsa=False\nopenssl:no_dso=False\nopenssl:no_ec=False\nopenssl:no_ecdh=False\nopenssl:no_ecdsa=False\nopenssl:no_engine=False\nopenssl:no_filenames=False\nopenssl:no_gost=False\nopenssl:no_hmac=False\nopenssl:no_idea=False\nopenssl:no_md4=False\nopenssl:no_md5=False\nopenssl:no_mdc2=False\nopenssl:no_ocsp=False\nopenssl:no_pinshared=False\nopenssl:no_rc2=False\nopenssl:no_rfc3779=False\nopenssl:no_rmd160=False\nopenssl:no_rsa=False\nopenssl:no_seed=False\nopenssl:no_sha=False\nopenssl:no_sm2=False\nopenssl:no_sm3=False\nopenssl:no_sm4=False\nopenssl:no_sock=False\nopenssl:no_srp=False\nopenssl:no_srtp=False\nopenssl:no_sse2=False\nopenssl:no_ssl=False\nopenssl:no_ssl3=False\nopenssl:no_stdio=False\nopenssl:no_tests=False\nopenssl:no_threads=False\nopenssl:no_tls1=False\nopenssl:no_ts=False\nopenssl:no_whirlpool=False\nopenssl:openssldir=None\nopenssl:shared=False\nzlib:fPIC=True\nzlib:shared=False",
"package_id": "4760125fd09127af98dbf68387bd44e807573faa",
"prev": "0",
"requires": [
"7",
"10"
],
"context": "host"
},
"10": {
"ref": "openssl/1.1.1o",
"options": "386=False\nenable_weak_ssl_ciphers=False\nfPIC=True\nno_aria=False\nno_asm=False\nno_async=False\nno_bf=False\nno_blake2=False\nno_camellia=False\nno_cast=False\nno_chacha=False\nno_cms=False\nno_comp=False\nno_ct=False\nno_deprecated=False\nno_des=False\nno_dgram=False\nno_dh=False\nno_dsa=False\nno_dso=False\nno_ec=False\nno_ecdh=False\nno_ecdsa=False\nno_engine=False\nno_filenames=False\nno_gost=False\nno_hmac=False\nno_idea=False\nno_md4=False\nno_md5=False\nno_mdc2=False\nno_ocsp=False\nno_pinshared=False\nno_rc2=False\nno_rfc3779=False\nno_rmd160=False\nno_rsa=False\nno_seed=False\nno_sha=False\nno_sm2=False\nno_sm3=False\nno_sm4=False\nno_sock=False\nno_srp=False\nno_srtp=False\nno_sse2=False\nno_ssl=False\nno_ssl3=False\nno_stdio=False\nno_tests=False\nno_threads=False\nno_tls1=False\nno_ts=False\nno_whirlpool=False\nopenssldir=None\nshared=False",
"package_id": "d13c97721d7bdc192ca6529684f2b79beeae8a7c",
"prev": "0",
"context": "host"
},
"11": {
"ref": "catch2/3.3.1",
"options": "console_width=80\ndefault_reporter=None\nfPIC=True\nshared=False\nwith_prefix=False",
"package_id": "6b952e07b144105007d6beb6165b6d6b25ff4787",
"prev": "0",
"context": "host"
}
},
"revisions_enabled": false
},
"version": "0.4",
"profile_host": "[settings]\narch=x86_64\narch_build=x86_64\nbuild_type=Debug\ncompiler=gcc\ncompiler.libcxx=libstdc++\ncompiler.version=12\nos=Linux\nos_build=Linux\n[options]\n[build_requires]\n[env]\n"
}

19
conanfile.py

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
from conan import ConanFile
class Recipe(ConanFile):
settings = "os", "compiler", "build_type", "arch"
generators = "CMakeToolchain", "CMakeDeps", "VirtualRunEnv"
def layout(self):
self.folders.generators = "conan"
def requirements(self):
self.requires("fmt/9.1.0")
self.requires("cxxopts/3.0.0")
self.requires("tree-sitter/0.20.6")
self.requires("sqlitecpp/3.2.1")
self.requires("libgit2/1.5.0")
def build_requirements(self):
self.test_requires("catch2/3.3.1")

BIN
database.db

Binary file not shown.

32
docs/Doxyfile.in

@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
# Configuration for Doxygen for use with CMake
# Only options that deviate from the default are included
# To create a new Doxyfile containing all available options, call `doxygen -g`
# Get Project name and version from CMake
PROJECT_NAME = "@PROJECT_NAME@"
PROJECT_NUMBER = "@PROJECT_VERSION@"
# Add sources
INPUT = "@PROJECT_SOURCE_DIR@/README.md" "@PROJECT_SOURCE_DIR@/include" "@PROJECT_SOURCE_DIR@/source" "@PROJECT_SOURCE_DIR@/docs/pages"
EXTRACT_ALL = YES
RECURSIVE = YES
OUTPUT_DIRECTORY = "@DOXYGEN_OUTPUT_DIRECTORY@"
# Use the README as a main page
USE_MDFILE_AS_MAINPAGE = "@PROJECT_SOURCE_DIR@/README.md"
# set relative include paths
FULL_PATH_NAMES = YES
STRIP_FROM_PATH = "@PROJECT_SOURCE_DIR@/include" "@PROJECT_SOURCE_DIR@"
STRIP_FROM_INC_PATH =
# We use m.css to generate the html documentation, so we only need XML output
GENERATE_XML = YES
GENERATE_HTML = NO
GENERATE_LATEX = NO
XML_PROGRAMLISTING = NO
CREATE_SUBDIRS = NO
# Include all directories, files and namespaces in the documentation
# Disable to include only explicitly documented objects
M_SHOW_UNDOCUMENTED = YES

6
docs/conf.py.in

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
DOXYFILE = 'Doxyfile'
LINKS_NAVBAR1 = [
(None, 'pages', [(None, 'about')]),
(None, 'namespaces', []),
]

7
docs/pages/about.dox

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
/**
* @page about About
* @section about-doxygen Doxygen documentation
* This page is auto generated using
* <a href="https://www.doxygen.nl/">Doxygen</a>, making use of some useful
* <a href="https://www.doxygen.nl/manual/commands.html">special commands</a>.
*/

85
source/db.cpp

@ -0,0 +1,85 @@ @@ -0,0 +1,85 @@
#include <SQLiteCpp/Database.h>
#include <SQLiteCpp/Statement.h>
#include <SQLiteCpp/Transaction.h>
#include <fmt/core.h>
#include <fmt/color.h>
#include <fmt/format.h>
#include <memory>
#include "db.hpp"
#include "lib.hpp"
class sqlite_handle : public db_handle {
public:
SQLite::Database database;
sqlite_handle(const std::string &filename) :
database(filename, SQLite::OPEN_READWRITE|SQLite::OPEN_CREATE) {}
void store(std::vector<source_file> records) final;
void init() final;
};
static const std::string query_init = "CREATE TABLE IF NOT EXISTS source_file "
"(id INTEGER PRIMARY KEY, "
"path TEXT, extension TEXT, is_c_source BOOL, is_header BOOL, "
"lines INT, bytes INT, comments INT, comment_lines INT, functions INT, function_lines INT"
");";
static const std::string insert_query = "INSERT INTO source_file "
"(path, extension, is_c_source, is_header, lines, bytes, "
"comments, comment_lines, functions, function_lines) "
"VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?);";
void insert_record(const source_file &r, SQLite::Database &db) {
SQLite::Statement query {db, insert_query};
int rec = 1;
query.bind(rec++, r.full_file_path);
query.bind(rec++, r.file_extension);
query.bind(rec++, static_cast<uint32_t>(r.is_c_source));
query.bind(rec++, static_cast<uint32_t>(r.is_header));
query.bind(rec++, r.lines);
query.bind(rec++, r.bytes);
query.bind(rec++, r.comments);
query.bind(rec++, r.comment_lines);
query.bind(rec++, r.function_defs);
query.bind(rec++, r.function_lines);
try {
query.exec();
} catch (SQLite::Exception &ex) {
fmt::print(fmt::fg(fmt::color::red), "SQLite Exception {}: {}\n", ex.getErrorCode(), ex.getErrorStr());
}
}
std::shared_ptr<db_handle> open_database(const std::string &filename) {
return std::make_shared<sqlite_handle>(filename);
}
void sqlite_handle::init() {
database.exec(query_init);
}
void db_handle::store(std::vector<source_file> records) {
throw std::runtime_error("Not implemented");
}
void sqlite_handle::store(std::vector<source_file> records) {
fmt::print("Storing {} records.\n",records.size());
SQLite::Transaction trans {database};
for (const auto& r : records) {
insert_record(r, database);
}
trans.commit();
}

16
source/db.hpp

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
#include "lib.hpp"
#include <string>
#include <vector>
#include <memory>
class db_handle {
public:
virtual void store(std::vector<source_file> records) = 0;
virtual void init() = 0;
virtual ~db_handle() = default;
};
std::shared_ptr<db_handle> open_database(const std::string &filename);

28
source/langs.h

@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
#ifndef TREE_SITTER_LANGS_H_
#define TREE_SITTER_LANGS_H_
#ifdef __cplusplus
extern "C" {
#endif
#ifndef TREE_SITTER_API_H_
typedef struct TSLanguage TSLanguage;
#endif
const TSLanguage *tree_sitter_c();
const TSLanguage *tree_sitter_cpp();
const TSLanguage *tree_sitter_c_sharp();
const TSLanguage *tree_sitter_go();
const TSLanguage *tree_sitter_java();
const TSLanguage *tree_sitter_javascript();
const TSLanguage *tree_sitter_python();
const TSLanguage *tree_sitter_rust();
const TSLanguage *tree_sitter_typescript();
const TSLanguage *tree_sitter_tsx();
#ifdef __cplusplus
}
#endif
#endif

260
source/lib.cpp

@ -0,0 +1,260 @@ @@ -0,0 +1,260 @@
#include "lib.hpp"
#include <fmt/core.h>
#include <filesystem>
#include <stdexcept>
#include <string>
#include <tree_sitter/api.h>
#include <functional>
#include <fstream>
library::library()
: name {fmt::format("{}", "testts")}
{
}
std::string
read_file(const std::string &filename)
{
std::ifstream file {filename, std::ios::binary};
if (file) {
std::ostringstream os;
os << file.rdbuf();
return os.str();
} else {
throw std::runtime_error(fmt::format("Unable to open file {}", filename));
}
}
source_file::source_file(const source_file& a) {
if (a.tree != nullptr) {
tree = ts_tree_copy(a.tree);
} else {
tree = nullptr;
}
full_source = a.full_source;
full_file_path = a.full_file_path;
file_extension = a.file_extension;
file_name = a.file_name;
is_c_source = a.is_c_source;
is_header = a.is_header;
comment_bytes = a.comment_bytes;
function_bytes = a.function_bytes;
function_defs = a.function_defs;
function_lines = a.function_lines;
lines = a.lines;
bytes = a.bytes;
comments = a.comments;
comment_lines = a.comment_lines;
comment_bytes = a.comment_bytes;
}
source_file::source_file(const std::string &filename, TSParser *parser) {
auto path = std::filesystem::path(filename);
full_file_path = path.string();
file_extension = path.extension();
file_name = path.filename();
is_c_source = path.extension() == ".c";
is_header = path.extension() == ".h";
std::ifstream in(path, std::ifstream::ate | std::ifstream::binary);
bytes = static_cast<uint32_t>(in.tellg());
if (!(is_header || is_c_source)) {
return;
}
full_source = read_file(filename);
tree = ts_parser_parse_string(parser, nullptr, full_source.c_str(),
static_cast<uint32_t>(full_source.size()));
lines = count_lines(full_source);
struct counter {
uint32_t count = 0;
uint32_t bytes = 0;
uint32_t lines = 0;
const std::string_view source_view;
counter(const std::string &source) : source_view (source){}
counter(const std::string_view source) : source_view (source){}
void operator()(TSQueryCursor *cursor, TSQueryMatch *match)
{
count++;
auto this_bytes = static_cast<uint32_t>((ts_node_end_byte(match->captures->node) - ts_node_start_byte(match->captures->node)));
auto text = source_view.substr(ts_node_start_byte(match->captures->node), this_bytes);
lines += static_cast<uint32_t>(count_lines(text));
bytes += this_bytes;
}
};
counter def_counter {full_source};
map_query(tree, "(function_definition) @val", def_counter);
counter comment_counter {full_source};
map_query(tree, "(comment) @val", comment_counter);
comments = comment_counter.count;
comment_bytes = comment_counter.bytes;
comment_lines = comment_counter.lines;
function_defs = def_counter.count;
function_bytes = def_counter.bytes;
function_lines = def_counter.lines;
bytes = full_source.size();
}
void map_query(TSTree *tree,
const std::string &query_text,
auto & on_each) {
uint32_t error {};
TSQueryError error_type {};
TSQuery *query = ts_query_new(tree_sitter_c(), query_text.c_str(),
static_cast<uint32_t>(query_text.size()), &error, &error_type);
if (query == nullptr) {
throw std::runtime_error("Invalid treesitter query.");
}
TSQueryCursor *cursor = ts_query_cursor_new();
ts_query_cursor_exec(cursor, query, ts_tree_root_node(tree));
TSQueryMatch match {};
while (ts_query_cursor_next_match(cursor, &match)) {
on_each(cursor, &match);
}
}
int count_lines(const std::string_view text) {
int lines = 0;
std::size_t found = 0;
do {
lines++;
found = text.find('\n', found+1);
} while (found != std::string::npos);
return lines;
}
int count_lines(const std::string &text) {
return count_lines(std::string_view {text});
}
source_file::~source_file() {
if (tree != nullptr) {
ts_tree_delete(tree);
}
}
source_file::source_file(const std::string &filename)
: source_file(filename, get_new_parser()){}
std::string source_file::as_string() {
return fmt::format(
"{}\n{} bytes, {} lines\ncomments {}: {} lines, {} bytes\nfunctions {}: {} lines, {} bytes",
full_file_path, bytes, lines, comments, comment_lines, comment_bytes, function_defs, function_lines, function_bytes
);
}
TSParser *get_new_parser() {
TSParser *parser = ts_parser_new();
ts_parser_set_language(parser, tree_sitter_c());
return parser;
}
std::string
node_text (const TSNode node, const std::string &text){
auto start = ts_node_start_byte(node);
auto end = ts_node_end_byte(node);
return text.substr(start, end - start);
}
std::string
function::as_string() {
return fmt::format("function {}\n{}\n{}:{}\n{} args, {} lines",
name, signature, source_file_name, start_line, num_args, length_lines);
}
std::vector<function> get_functions(const source_file &s) {
struct counter {
std::vector<function> functions {};
uint32_t count = 0;
uint32_t bytes = 0;
uint32_t lines = 0;
const std::string_view source_view;
const std::string_view source_filename;
counter(const source_file &source)
: source_view (source.full_source), source_filename(source.full_file_path) {}
void operator()(TSQueryCursor *cursor, TSQueryMatch *match)
{
count++;
auto this_bytes = static_cast<uint32_t>((ts_node_end_byte(match->captures->node) - ts_node_start_byte(match->captures->node)));
auto text = source_view.substr(ts_node_start_byte(match->captures->node), this_bytes);
auto this_lines = static_cast<uint32_t>(count_lines(text));
lines += this_lines;
bytes += this_bytes;
auto start_line = count_lines(source_view.substr(0,
ts_node_start_byte(match->captures->node)));
functions.emplace_back((function){
.source_file_name = std::string(source_filename),
.start_line = static_cast<uint32_t>(start_line),
.length_lines = this_lines});
}
};
counter def_counter {s};
map_query(s.tree, "(function_definition) @val", def_counter);
return def_counter.functions;
}
void run_query(const source_file &s, std::string query) {
struct lambda {
int count = 0;
std::string_view source_view;
lambda(const source_file &s) : source_view(s.full_source) {}
void operator()(TSQueryCursor *cursor, TSQueryMatch *match)
{
count++;
for (int j = 0; j < match->capture_count; j++) {
auto this_bytes =
static_cast<uint32_t>((ts_node_end_byte(match->captures[j].node) - ts_node_start_byte(match->captures->node)));
auto text = source_view.substr(ts_node_start_byte(match->captures[j].node), this_bytes);
fmt::print("result {}:\n{}\n", count, text);
}
}
};
struct lambda on_each {s};
try {
map_query(s.tree, query, on_each);
} catch (std::runtime_error &e) {
fmt::print("Error: {}\n", e.what());
}
}

94
source/lib.hpp

@ -0,0 +1,94 @@ @@ -0,0 +1,94 @@
#pragma once
#include <string>
#include <exception>
#include <sstream>
#include <fstream>
#include <chrono>
#include <vector>
#include <tree_sitter/api.h>
extern "C" {
const TSLanguage *tree_sitter_c();
}
/**
* @brief The core implementation of the executable
*
* This class makes up the library part of the executable, which means that the
* main logic is implemented here. This kind of separation makes it easy to
* test the implementation for the executable, because the logic is nicely
* separated from the command-line logic implemented in the main function.
*/
struct library
{
/**
* @brief Simply initializes the name member to the name of the project
*/
library();
std::string name;
};
class source_file {
public:
source_file(const std::string &filename);
source_file(const std::string &filename, TSParser *parser);
source_file(const source_file& a);
~source_file();
std::string full_file_path;
std::string file_extension;
std::string file_name;
std::string full_source;
bool is_c_source = false;
bool is_header = false;
uint32_t lines = 0;
uint32_t bytes = 0;
uint32_t comments = 0;
uint32_t comment_bytes = 0;
uint32_t comment_lines = 0;
uint32_t function_defs = 0;
uint32_t function_bytes = 0;
uint32_t function_lines = 0;
TSTree *tree = nullptr;
std::string as_string();
};
int count_lines(const std::string_view text);
int count_lines(const std::string &text);
TSParser *get_new_parser();
std::string read_file(const std::string &filename);
struct query_lambda {
void operator()(TSQueryCursor *, TSQueryMatch *);
};
void map_query(TSTree *tree, const std::string &query_text, auto &on_each);
struct function {
std::string name;
std::string signature;
uint32_t num_args;
std::string source_file_name;
uint32_t start_line;
uint32_t length_lines;
std::string as_string();
};
std::vector<function> get_functions(const source_file &s);
void run_query(const source_file &s, std::string query);

90
source/main.cpp

@ -0,0 +1,90 @@ @@ -0,0 +1,90 @@
#include <cstddef>
#include <filesystem>
#include <iostream>
#include <memory>
#include <string>
#include <cstdint>
#include <string_view>
#include <optional>
#include <vector>
#include <cxxopts.hpp>
#include <fmt/format.h>
#include <fmt/core.h>
#include <fmt/color.h>
#include "lib.hpp"
#include "db.hpp"
auto main(int argc, char **argv) -> int
{
cxxopts::Options options("testts", "Simple code analyzer using TreeSitter.");
options.add_options()
("d,database", "Specify database filename", cxxopts::value<std::string>()
->default_value("database.db"))
("i,input", "The file or direcotry to analyze", cxxopts::value<std::string>())
;
auto result = options.parse(argc, argv);
if (result.count("input") == 0) {
fmt::print("Must specify input.\n{}", options.help());
return 1;
}
std::string analyze = result["input"].as<std::string>();
auto database = open_database(result["database"].as<std::string>());
database->init();
if (argc > 1) {
fmt::print("Analyzing '{}'\n", analyze);
auto path = std::filesystem::path(analyze);
auto parser = get_new_parser();
int count = 0;
if (std::filesystem::is_directory(path)) {
std::vector<source_file> files {};
for (const std::filesystem::directory_entry& dir_entry :
std::filesystem::recursive_directory_iterator(path))
{
files.emplace_back(dir_entry.path().string(), parser);
//fmt::print("{}\n", files.back().as_string());
if (++count % 500 == 0) {
database->store(files);
files.clear();
}
}
database->store(files);
} else {
auto s = source_file(path.string());
fmt::print("{}\n", s.as_string());
auto funcs = get_functions(s);
for (auto f: funcs) {
fmt::print("{}\n", f.as_string());
}
std::string line {};
while (line != "exit") {
fmt::print("query > ");
std::getline(std::cin, line);
if (line != exit) {
run_query(s, line);
}
}
}
}
return 0;
}

33
test/CMakeLists.txt

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
# Parent project does not export its library target, so this CML implicitly
# depends on being added from it, i.e. the testing is done only from the build
# tree and is not feasible from an install location
project(testtsTests LANGUAGES CXX)
# ---- Dependencies ----
find_package(Catch2 REQUIRED)
find_package(tree-sitter)
find_package(Tree-Sitter CONFIG)
find_package(Tree-Sitter CONFIG REQUIRED C)
target_link_libraries(testts_lib PRIVATE fmt::fmt)
target_link_libraries(testts_lib PRIVATE Tree-Sitter::Tree-Sitter Tree-Sitter::Tree-Sitter-C tree-sitter::tree-sitter)
include(Catch)
# ---- Tests ----
add_executable(testts_test source/testts_test.cpp)
target_link_libraries(
testts_test PRIVATE
testts_lib
Catch2::Catch2WithMain
)
target_compile_features(testts_test PRIVATE cxx_std_20)
catch_discover_tests(testts_test)
# ---- End-of-file commands ----
add_folders(Test)

1
test/source/testts_test.cpp

@ -0,0 +1 @@ @@ -0,0 +1 @@
#include <catch2/catch_test_macros.hpp>

32
tree-sitter-cmake/.github/workflows/build.yml

@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
name: build
on:
push:
branches:
- master
pull_request:
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
max-parallel: 4
matrix:
os:
- windows-latest
- macos-latest
- ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install
uses: symbitic/install-cmake@master
- name: Configure
run: cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug .
- name: Build
run: cmake --build build -j 4

3
tree-sitter-cmake/.gitignore vendored

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
/build/
.DS_Store
/tmp/

121
tree-sitter-cmake/CMakeLists.txt

@ -0,0 +1,121 @@ @@ -0,0 +1,121 @@
cmake_minimum_required(VERSION 3.15 FATAL_ERROR)
include(CMakePackageConfigHelpers)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED TRUE)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
set(CMAKE_BUILD_TYPE_INIT "Release")
if(DEFINED PROJECT_NAME)
set(SUBPROJECT TRUE)
else()
set(SUBPROJECT FALSE)
endif()
project(tree-sitter-cmake)
function(CHECKOUT proj tag)
if (NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/${proj}")
message(STATUS "Cloning ${proj} ${tag}")
execute_process(
COMMAND git clone -b ${tag} https://github.com/tree-sitter/${proj}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
RESULT_VARIABLE ret
OUTPUT_QUIET
ERROR_QUIET
)
if (NOT ret EQUAL 0)
message(ERROR "Error cloning ${proj}")
endif()
endif()
endfunction()
function(add_language lang)
string(TOLOWER "${lang}" lang_str)
if (NOT ARGN)
set(src_dir "tree-sitter-${lang_str}")
else()
set(src_dir "${ARGN}")
endif()
add_library(Tree-Sitter-${lang} STATIC "${CMAKE_CURRENT_BINARY_DIR}/${src_dir}/src/parser.c")
# Make aliases the same as exported lib names. Useful when embedding.
add_library(Tree-Sitter::Tree-Sitter-${lang} ALIAS Tree-Sitter-${lang})
target_include_directories(Tree-Sitter-${lang}
PRIVATE
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/${src_dir}/src>
PUBLIC
$<INSTALL_INTERFACE:include>
)
endfunction()
checkout(tree-sitter v0.20.1)
checkout(tree-sitter-c v0.20.1)
checkout(tree-sitter-c-sharp v0.19.1)
checkout(tree-sitter-cpp v0.20.0)
checkout(tree-sitter-go rust-0.19.1)
checkout(tree-sitter-java v0.19.1)
checkout(tree-sitter-javascript rust-0.20.0)
checkout(tree-sitter-python rust-0.19.1)
checkout(tree-sitter-rust v0.20.0)
checkout(tree-sitter-typescript rust-0.20.0)
add_language(C)
add_language(C-Sharp)
add_language(CPP)
add_language(Go)
add_language(Java)
add_language(JavaScript)
add_language(Python)
add_language(Rust)
add_language(TypeScript tree-sitter-typescript/typescript)
add_language(TSX tree-sitter-typescript/tsx)
add_library(Tree-Sitter "${CMAKE_CURRENT_BINARY_DIR}/tree-sitter/lib/src/lib.c")
target_include_directories(Tree-Sitter
PRIVATE
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/tree-sitter/lib/src>
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/tree-sitter/lib/include>
PUBLIC
$<INSTALL_INTERFACE:include>
)
add_library(Tree-Sitter::Tree-Sitter ALIAS Tree-Sitter)
if(NOT SUBPROJECT)
# Only install when built as top-level project.
if(WIN32)
set(CMAKE_INSTALL_DIR "CMake")
else()
set(CMAKE_INSTALL_DIR "lib/cmake/Tree-Sitter")
endif()
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/tree-sitter/lib/include/tree_sitter/api.h
${CMAKE_CURRENT_BINARY_DIR}/tree-sitter/lib/include/tree_sitter/parser.h
include/tree_sitter/langs.h
DESTINATION include/tree_sitter
)
install(TARGETS Tree-Sitter EXPORT Tree-Sitter DESTINATION lib)
install(EXPORT Tree-Sitter
DESTINATION ${CMAKE_INSTALL_DIR}
NAMESPACE Tree-Sitter::
FILE Tree-Sitter-targets.cmake
)
foreach(lang C C-Sharp CPP Go Java JavaScript Python Rust TypeScript TSX)
install(TARGETS Tree-Sitter-${lang} EXPORT ${lang} DESTINATION lib)
install(EXPORT ${lang}
DESTINATION ${CMAKE_INSTALL_DIR}
NAMESPACE Tree-Sitter::
FILE Tree-Sitter-${lang}-targets.cmake
)
endforeach()
install(FILES tree-sitter-config.cmake DESTINATION ${CMAKE_INSTALL_DIR})
endif()

82
tree-sitter-cmake/README.md

@ -0,0 +1,82 @@ @@ -0,0 +1,82 @@
# tree-sitter-cmake
CMake project for building [Tree-sitter](http://tree-sitter.github.io/tree-sitter/).
## About
Tree-sitter is an excellent project. It provides a reusable interface for parsing many different programming languages.
But because it's aimed primarily for consumption in Node.js, it uses GYP as the build system.
GYP is terrible for modern-day developers needs.
This project aims to provide an integrated way to use Tree-sitter in CMake projects.
Just use this to build and install Tree-sitter, as well as the CMake config files.
As a bonus, it even includes several Tree-sitter languages in library form.
I originally wrote this to make importing Tree-sitter into a project I am
working on. I decided to release this separate in case anyone finds it useful.
## Getting Started
This project includes Tree-sitter and sub-projects as Git Submodules, so make
sure you add the submodules when you checkout the source.
```bash
$ git clone --recurse-submodules https://github.com/Symbitic/tree-sitter-cmake
$ mkdir tree-sitter-cmake/build
$ cd tree-sitter-cmake/build
$ cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release ..
$ cmake --build .
$ sudo cmake --build . --target install
```
The above commands should check out the git submodules this project pulls in.
Otherwise, CMake will check out the git submodules when run.
## Using
```cmake
find_package(Tree-Sitter CONFIG)
# Gives Tree-Sitter::Tree-Sitter
find_package(Tree-Sitter CONFIG REQUIRED CPP Rust)
# Gives Tree-Sitter::Tree-Sitter, Tree-Sitter::Tree-Sitter-CPP, Tree-Sitter::Tree-Sitter-Rust
```
The complete list of targets is:
* `Tree-Sitter::Tree-Sitter`
* `Tree-Sitter::Tree-Sitter-C`
* `Tree-Sitter::Tree-Sitter-CPP`
* `Tree-Sitter::Tree-Sitter-C-Sharp`
* `Tree-Sitter::Tree-Sitter-Go`
* `Tree-Sitter::Tree-Sitter-Java`
* `Tree-Sitter::Tree-Sitter-JavaScript`
* `Tree-Sitter::Tree-Sitter-Python`
* `Tree-Sitter::Tree-Sitter-Rust`
* `Tree-Sitter::Tree-Sitter-TypeScript`
I will probably add more language parsers later.
Also provided is a new header file (`tree_sitter/langs.h`), that contains the
definitions for all the language parsers included.
```c
const TSLanguage *tree_sitter_c();
const TSLanguage *tree_sitter_cpp();
const TSLanguage *tree_sitter_c_sharp();
const TSLanguage *tree_sitter_go();
const TSLanguage *tree_sitter_java();
const TSLanguage *tree_sitter_javascript();
const TSLanguage *tree_sitter_python();
const TSLanguage *tree_sitter_rust();
const TSLanguage *tree_sitter_typescript();
```
## License
This is nothing more than a simple CMake script and some supporting files.
It is released as Public Domain.
This is not endorsed by the Tree-sitter developers. If they contact me and ask
me to remove this I will.

27
tree-sitter-cmake/include/tree_sitter/langs.h

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
#ifndef TREE_SITTER_LANGS_H_
#define TREE_SITTER_LANGS_H_
#ifdef __cplusplus
extern "C" {
#endif
#ifndef TREE_SITTER_API_H_
typedef struct TSLanguage TSLanguage;
#endif
const TSLanguage *tree_sitter_c();
const TSLanguage *tree_sitter_cpp();
const TSLanguage *tree_sitter_c_sharp();
const TSLanguage *tree_sitter_go();
const TSLanguage *tree_sitter_java();
const TSLanguage *tree_sitter_javascript();
const TSLanguage *tree_sitter_python();
const TSLanguage *tree_sitter_rust();
const TSLanguage *tree_sitter_typescript();
const TSLanguage *tree_sitter_tsx();
#ifdef __cplusplus
}
#endif
#endif

30
tree-sitter-cmake/tree-sitter-config.cmake

@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
# TODO: Create VersionConfig file
# TODO: Generate using CMakePackageConfigHelpers to help detect required.
include(${CMAKE_CURRENT_LIST_DIR}/Tree-Sitter-targets.cmake OPTIONAL RESULT_VARIABLE ret)
if(NOT ret)
set(${CMAKE_FIND_PACKAGE_NAME}_FOUND FALSE)
set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE "Tree-Sitter-targets.cmake not found.")
return()
elseif(NOT TARGET Tree-Sitter::Tree-Sitter)
set(${CMAKE_FIND_PACKAGE_NAME}_FOUND FALSE)
set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE "Tree-Sitter::Tree-Sitter target not found.")
return()
endif()
set(_NOTFOUND_MESSAGE)
foreach(lang ${Tree-Sitter_FIND_COMPONENTS})
include(${CMAKE_CURRENT_LIST_DIR}/Tree-Sitter-${lang}-targets.cmake OPTIONAL RESULT_VARIABLE ret)
if(NOT ret)
set(_NOTFOUND_MESSAGE "${_NOTFOUND_MESSAGE}Failed to find Tree-Sitter component \"${lang}\"\n")
elseif(NOT TARGET Tree-Sitter::Tree-Sitter-${lang})
set(_NOTFOUND_MESSAGE "${_NOTFOUND_MESSAGE}Failed to find Tree-Sitter::Tree-Sitter-${lang} target\n")
endif()
endforeach()
if(_NOTFOUND_MESSAGE)
set(${CMAKE_FIND_PACKAGE_NAME}_FOUND FALSE)
set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE "${_NOTFOUND_MESSAGE}")
return()
endif()
Loading…
Cancel
Save