Full Code of cschlosser/doxdocgen for AI

master 28f9c88fa46a cached
54 files
226.3 KB
56.9k tokens
139 symbols
1 requests
Download .txt
Showing preview only (241K chars total). Download the full file or copy to clipboard to get everything.
Repository: cschlosser/doxdocgen
Branch: master
Commit: 28f9c88fa46a
Files: 54
Total size: 226.3 KB

Directory structure:
gitextract_4uo1r0wb/

├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── ISSUE_TEMPLATE.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       ├── cd.yml
│       ├── ci.yml
│       └── codeql-analysis.yml
├── .gitignore
├── .nycrc
├── .vscode/
│   ├── launch.json
│   ├── settings.json
│   └── tasks.json
├── .vscodeignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── package.json
├── src/
│   ├── CodeParserController.ts
│   ├── Common/
│   │   ├── ICodeParser.ts
│   │   └── IDocGen.ts
│   ├── Config.ts
│   ├── DoxygenCompletionItemProvider.ts
│   ├── GitConfig.ts
│   ├── Lang/
│   │   └── Cpp/
│   │       ├── CppArgument.ts
│   │       ├── CppDocGen.ts
│   │       ├── CppParseTree.ts
│   │       ├── CppParser.ts
│   │       └── CppToken.ts
│   ├── extension.ts
│   ├── templatedString.ts
│   ├── test/
│   │   ├── CppTests/
│   │   │   ├── Attributes.test.ts
│   │   │   ├── Con-AndDestructor.test.ts
│   │   │   ├── Config.test.ts
│   │   │   ├── FileDescription.test.ts
│   │   │   ├── FunctionPointer.test.ts
│   │   │   ├── Operators.test.ts
│   │   │   ├── Parameters.test.ts
│   │   │   ├── Preprocessor.test.ts
│   │   │   ├── ReturnTypes.test.ts
│   │   │   ├── SmartText.test.ts
│   │   │   ├── Templates.test.ts
│   │   │   ├── TestSetup.ts
│   │   │   └── TrailingReturns.test.ts
│   │   ├── index.ts
│   │   ├── runTests.ts
│   │   └── tools/
│   │       ├── MockDocument.ts
│   │       ├── MockEditor.ts
│   │       ├── MockLine.ts
│   │       ├── MockPosition.ts
│   │       ├── MockSelection.ts
│   │       └── MockTextEditorEdit.ts
│   └── util.ts
├── tsconfig.json
└── tslint.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''

---

**Describe the bug**
A clear and concise description of what the bug is.

**Code example**

```Cpp
// Put your code here
```

**Expected result**

```Cpp
/**
 * @brief Put the expected comment here
 */
```

**Actual result**

```Cpp
/**
 * @brief Put the actually generated comment here
 */
```

**Screencaps**
If applicable, add screencaps to help explain your problem.

**Your System:**
 - OS: [Windows, macOS, Linux]
 - VS Code Version [e.g. 1.55]
 - Doxdocgen Code Version [e.g. 1.3.1]

**Additional context**
Add any other context about the problem here.


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''

---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.


================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
# Description

Anything you'd like to add

## Code example

```Cpp
// Put your code here
```

### Expected result

```Cpp
/**
 * @brief Put the expected comment here
 */
```

### Actual result

```Cpp
/**
 * @brief Put the actually generated comment here
 */
```


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
# Fix/Feature/Other

## Fix

- [ ] Link to issue. If there is no issue please describe it using at least the issue template

- [ ] Description of the fix

- [ ] Added unit test

## Feature

- [ ] Description of what's added

- [ ] Gif of your feature if appropriate

- [ ] Added gif to README

- [ ] Added unit test

## Other

- [ ] Description


================================================
FILE: .github/workflows/cd.yml
================================================
name: Release

on:
  push:
    branches:
      - master
  pull_request:
    branches:
      - master
  release:
    types:
      - created

jobs:
  pipeline:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Use Node.js
        uses: actions/setup-node@v1
        with:
          node-version: '15.11.0'
      - run: yarn

      - name: Install VSCE
        run: yarn add vsce ovsx -D

      - name: Update $PATH
        run: echo "$PWD/node_modules/.bin/" >> $GITHUB_PATH

      - name: Build vsix
        run: vsce package && ls -alh

      - name: Publish to VS Code Marketplace
        run: vsce publish -p ${VSCE_TOKEN}
        env:
          VSCE_TOKEN: ${{ secrets.VSCE_TOKEN }}
        if: contains(github.ref, 'refs/tags/')

      - name: Publish to Open VSX Registry
        run: ovsx publish
        env:
          OVSX_PAT: ${{ secrets.OPEN_VSX_TOKEN }}
        if: contains(github.ref, 'refs/tags/')

      - name: Upload vsix to GitHub release
        uses: svenstaro/upload-release-action@v2
        with:
          repo_token: ${{ secrets.GITHUB_TOKEN }}
          file: doxdocgen-*.vsix
          tag: ${{ github.ref }}
          overwrite: true
          file_glob: true
        if: contains(github.ref, 'refs/tags/')



================================================
FILE: .github/workflows/ci.yml
================================================
name: CI

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:
  lint:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Use Node.js
        uses: actions/setup-node@v1
        with:
          node-version: '15.11.0'
      - run: yarn

      - name: Lint
        run: yarn lint
  
  build:
    needs: lint
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      
      - name: Use Node.js
        uses: actions/setup-node@v1
        with:
          node-version: '15.11.0'
      - run: yarn

      - name: Compile
        run: yarn compile
  
  test:
    needs: build
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        # disable ubuntu-latest until https://github.com/microsoft/vscode/issues/106569 is fixed
        os: [macos-latest, windows-latest]
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      
      - name: Use Node.js
        uses: actions/setup-node@v1
        with:
          node-version: '15.11.0'
      - run: yarn

      - name: Test
        run: yarn test
        # Run tests only on non-coverage job
        if: matrix.os != 'macOS-latest'
      
      - name: Generate Coverage
        run: yarn cov
        if: matrix.os == 'macOS-latest'

      - name: Publish coverage
        uses: codecov/codecov-action@v1
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
        if: matrix.os == 'macOS-latest'
 
      - name: Upload coverage
        uses: actions/upload-artifact@v2
        with:
          name: cov
          path: coverage
        if: matrix.os == 'macOS-latest'



================================================
FILE: .github/workflows/codeql-analysis.yml
================================================
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"

on:
  push:
    branches: [ master ]
  pull_request:
    # The branches below must be a subset of the branches above
    branches: [ master ]
  schedule:
    - cron: '32 8 * * 3'

jobs:
  analyze:
    name: Analyze
    runs-on: ubuntu-latest
    permissions:
      actions: read
      contents: read
      security-events: write

    strategy:
      fail-fast: false
      matrix:
        language: [ 'javascript' ]
        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
        # Learn more:
        # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed

    steps:
    - name: Checkout repository
      uses: actions/checkout@v2

    # Initializes the CodeQL tools for scanning.
    - name: Initialize CodeQL
      uses: github/codeql-action/init@v1
      with:
        languages: ${{ matrix.language }}
        # If you wish to specify custom queries, you can do so here or in a config file.
        # By default, queries listed here will override any specified in a config file.
        # Prefix the list here with "+" to use these queries and those in the config file.
        # queries: ./path/to/local/query, your-org/your-repo/queries@main

    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
    # If this step fails, then you should remove it and run the build manually (see below)
    - name: Autobuild
      uses: github/codeql-action/autobuild@v1

    # ℹ️ Command-line programs to run using the OS shell.
    # 📚 https://git.io/JvXDl

    # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
    #    and modify them (or add more) to build your code if your project
    #    uses a compiled language

    #- run: |
    #   make bootstrap
    #   make release

    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v1


================================================
FILE: .gitignore
================================================
out
node_modules
.vscode-test/
*.vsix
coverage/
.DS_Store
/npm-debug.log
/.nyc_output/
package-lock.json

================================================
FILE: .nycrc
================================================
{
    "extends": "@istanbuljs/nyc-config-typescript",
    "cache": false,
    "cwd": "./",
    "exclude": [
        "**/**.test.js"
    ],
    "extension": [
        ".ts",
        ".tsx"
    ],
    "hookRequire": true,
    "hookRunInContext": true,
    "hookRunInThisContext": true,
    "instrument": true,
    "reporter": ["json", "text"],
    "recursive": true,
    "require": [
        "ts-node/register",
        "source-map-support/register"
    ],
    "sourceMap": true,
    "all": true,
    "include": [ "src/Lang" ]
}


================================================
FILE: .vscode/launch.json
================================================
// A launch configuration that compiles the extension and then opens it inside a new window
{
    "version": "0.1.0",
    "configurations": [
        {
            "name": "Launch Extension",
            "type": "extensionHost",
            "request": "launch",
            "runtimeExecutable": "${execPath}",
            "args": ["--extensionDevelopmentPath=${workspaceRoot}" ],
            "stopOnEntry": false,
            "sourceMaps": true,
            "outFiles": [ "${workspaceRoot}/out/**/*.js" ],
            "preLaunchTask": "npm: compile"
        },
        {
            "name": "Launch Tests",
            "type": "extensionHost",
            "request": "launch",
            "runtimeExecutable": "${execPath}",
            "args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test", "--disable-extensions" ],
            "stopOnEntry": false,
            "sourceMaps": true,
            "outFiles": [ "${workspaceRoot}/out/test/**/*.js" ],
            "preLaunchTask": "npm: compile"
        }
    ]
}


================================================
FILE: .vscode/settings.json
================================================
// Place your settings in this file to overwrite default and user settings.
{
    "files.exclude": {
        "out": false // set this to true to hide the "out" folder with the compiled JS files
    },
    "search.exclude": {
        "out": true // set this to false to include "out" folder in search results
    }
}

================================================
FILE: .vscode/tasks.json
================================================
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
{
    "version": "2.0.0",
    "tasks": [
        {
            "type": "npm",
            "script": "watch",
            "problemMatcher": "$tsc-watch",
            "isBackground": true,
            "presentation": {
                "reveal": "never"
            },
            "group": {
                "kind": "build",
                "isDefault": true
            }
        }
    ]
}

================================================
FILE: .vscodeignore
================================================
.vscode/**
.vscode-test/**
out/test/**
out/**/*.map
src/**
.gitignore
tsconfig.json
vsc-extension-quickstart.md
.travis.yml
appveyor.yml
coverconfig.json
publish_coverage.sh
.github/**

================================================
FILE: CHANGELOG.md
================================================
# Change Log

## [1.4.0]

### Feature

- Use `vscode.workspace.workspaceFolders[0]` to construct SimpleGit instance (#268)
  Thanks to @lukester1975

### Fix

- Add official VS Code Language identifier for CUDA (#273)
  Thanks to @jobtijhuis

## [1.3.2]

### Fix

- Indent alignment not working in file comments (#206) (#236)
  Thanks to @HO-COOH

- Several typos (#233)
  Thanks to @jogo-

## [1.3.1]

### Fix

- Only suggest Doxygen snippet's when the cursor is in a comment section (#224) (#229)
  Thanks to @HO-COOH

## [1.3.0]

### Feature

- Add `{file}` template, so it can be used in custom tag of both file comments and generic comments (#191) (#221)
  Thanks to @HO-COOH

## [1.2.2]

### Fix

- Fix tags in generic order not expanded (#204) (#217)
  Thanks to @HO-COOH

## [1.2.1]

### Fix

- Fix unexpected return tag on non-functions (#210) (#214)
  Thanks to @HO-COOH

## [1.2.0]

### Feature

- Doxygen command Intellisense support (#211)
  Thanks to @HO-COOH

### Other

- Nicer setting descriptions (#209)
  Thanks to @HO-COOH

## [1.1.0]

### Feature

- Substitute author and email by git config (#186) (#182)
  Thanks to @to-s

### Other

- Update to Typescript 3.8.3

## [1.0.1]

### Fix

- Files without includes don't trigger the autocomplete for file description (#141) (#183)
  Thanks to @to-s

## [1.0.0]

### Revert

- Revert reverting Replace environment variables in templated strings. If no environment variable can be found the name of the variable will be inserted (#110)

### Other

- vsce packaging is forcing yarn even if no yarn config exists. Override this behavior now to use npm.

## [0.8.2]

### Other

- Package with older version of vsce

## [0.8.1]

### Revert

- Replace environment variables in templated strings. If no environment variable can be found the name of the variable will be inserted (#110)

## [0.8.0]

### Feature

- Support for arbitrary tags (#169)

- Replace environment variables in templated strings. If no environment variable can be found the name of the variable will be inserted (#110)

### Fix

- Incorrect parameter name of template (#170)

## [0.7.2]

### Fix

- Could not generate correct file header annotation (again) #164 (#161)

## [0.7.1]

### Fix

- Could not generate correct file header annotation #162 (#161)

## [0.7.0]

### Feature

- Add option to filter keywords (like custom defines) from input line #159 (#152)

### Fix

- Argument type would be placed as the param instead of the argument name #154

  Thanks to @DaanHuinink

- Bug when commenting macros #158 (#142)

## [0.6.0]

### Feature

- Add support for multiline template #140 (#127)
  
  Thanks to @eternalphane

- Enable CUDA language support #148 (#128)
  
  Thanks to @trxcllnt

### Other

- Update dev dependencies

## [0.5.2]

### Fix

- Cannot see params & return value, for global functions (#136)

## [0.5.1]

### Fix

- Ignore `restrict` keyword in C function parameter pointer (#121)

- Parameters not generated when using custom trigger (#119)

- Function argument. Pointer to table is not generated. (#123)

### Other

- Update dev dependencies and increase min VS Code version (#131, #132)

## [0.5.0]

### Feature

- Feature suggest: Indent option (#107)

#### Alignment

This version introduces the possibility to align text elements in config strings at a specified width.

Example:

```json
"doxdocgen.generic.paramTemplate": "@param{indent:10}{param}{indent:30}My Param doc"
```

will turn into

```cpp
/**
 * @param    foo                 My Param doc
 * @param    barIsAlsoAnOption   My Param doc
 */
void bar(int foo, int barIsAlsoAnOption);
```

You can use the `{indent:<number>}` in any templated config option available. Numbers have to be bigger than 0 to have any effect.

Alignment will be done from the start of the config string, like this:

```json
"doxdocgen.generic.paramTemplate": "@param {param}{indent:14}test"
```

```cpp
/**
 * @param        test
 */ 
   |<---------->| // 14
```

## [0.4.3]

### Fix

- Trigger Sequence /** causes extra */ to be generated and the comment block isn't fully generated. (#111)

## [0.4.2]

### Fix

- Invalid argument name for C `enum` types (#102)

## [0.4.1]

### Fix

- Wrong \param value generation. (#91)

## [0.4.0]

### Feature

- Additional file header fields for version, copyright, and custom text (#89)

## [0.3.3]

### Fix

- Doesn't parse CPP member pointer names correctly (#78)

## [0.3.2]

### Fix

- Cannot read property 'text' of undefined (#79)

## [0.3.1]

### Fix

- Don't create a comment while editing an existing comment. (#67)

## [0.3.0]

### Features

- Custom Doxygen tag order for methods (#55)

- Suggest smart text (#57)

  For more details see [README.md](https://github.com/christophschlosser/doxdocgen/tree/0.3.0#smart-text)

- Config keys are now grouped, see [Config update](#config-update) for changes

### Config update

| Old value                             | New value                              | Change   |
|---------------------------------------|----------------------------------------|:--------:|
| doxdocgen.generic.triggerSequence     | doxdocgen.c.triggerSequence            | Category |
| doxdocgen.generic.firstLine           | doxdocgen.c.firstLine                  | Category |
| doxdocgen.generic.commentPrefix       | doxdocgen.c.commentPrefix              | Category |
| doxdocgen.generic.lastLine            | doxdocgen.c.lastLine                   | Category |
|                                       | doxdocgen.c.setterText                 | Added    |
|                                       | doxdocgen.c.getterText                 | Added    |
|                                       | doxdocgen.c.factoryMethodText          | Added    |
| doxdocgen.generic.newLineAfterTParams |                                        | Removed  |
| doxdocgen.generic.newLineAfterBrief   |                                        | Removed  |
| doxdocgen.generic.newLineAfterParams  |                                        | Removed  |
| doxdocgen.generic.tparamTemplate      | doxdocgen.cpp.tparamTemplate           | Category |
|                                       | doxdocgen.cpp.ctorText                 | Added    |
|                                       | doxdocgen.cpp.dtorText                 | Added    |
| doxdocgen.generic.fileTemplate        | doxdocgen.file.fileTemplate            | Category |
| doxdocgen.generic.fileOrder           | doxdocgen.file.fileOrder               | Category |
|                                       | doxdocgen.generic.generateSmartText    | Added    |
|                                       | doxdocgen.generic.splitCasingSmartText | Added    |
|                                       | doxdocgen.generic.order                | Added    |

## [0.2.1]

- Hotfix. Include moment.js as runtime dependency

## [0.2.0]

- Fix #51

- Fix #52

## [0.1.0]

- Fix #40

- Fix #38

- Fix #37

- Fix #36

- Fix #28

- Fix #24

- Implemented support for `noexcept` and `throw`

- Implemented support for `constexpr` and `final`

- Implemented support for unnamed parameters, this broke in version 0.0.7

- Add new config variable that allows a user to disable the true and false return on bool types and make it behave like a normal type

## [0.0.7]

- Fix #31

- Fixed bug where using a different trigger sequence than "*" causes a newline to be added between the comment and the documented entity.

## [0.0.6]

- Improve and fix comment generation for several C++ features (#23) (thanks to @rowanG077 again)

## [0.0.5]

- Extend customization of generated documentation (#15, #16) (thanks to @rowanG077)

- Fix #10

## [0.0.4]

- Fix #9

- Add option to choose if the return type should be included in generated documentation

## [0.0.3]

- Add possibility to set comment start indicator to Qt style

## [0.0.2]

- Add C parser and generator

## [0.0.1]

- Initial release


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2017-2021 Christoph Schlosser

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: README.md
================================================
# Generate Doxygen Comments in VS Code

This VS Code Extensions provides Doxygen Documentation generation on the fly by starting a Doxygen comment block and pressing enter.

[![CI build status](https://img.shields.io/github/workflow/status/cschlosser/doxdocgen/CI/master)](https://github.com/cschlosser/doxdocgen/actions/workflows/ci.yml?query=branch%3Amaster)
[![Release build status](https://img.shields.io/github/workflow/status/cschlosser/doxdocgen/CD?label=Release)](https://github.com/cschlosser/doxdocgen/actions/workflows/cd.yml?query=event%3Arelease++)
[![codecov](https://codecov.io/gh/cschlosser/doxdocgen/branch/master/graph/badge.svg)](https://codecov.io/gh/cschlosser/doxdocgen)
[![IRC chat](https://img.shields.io/badge/irc.oftc.net-%23doxdocgen-brightgreen)](https://webchat.oftc.net/?channels=doxdocgen)

## Table of Contents

- [Generate Doxygen Comments in VS Code](#generate-doxygen-comments-in-vs-code)
  - [Table of Contents](#table-of-contents)
  - [Features](#features)
    - [Alignment](#alignment)
    - [Attributes](#attributes)
    - [Con- and Destructors](#con--and-destructors)
    - [Extensive customization](#extensive-customization)
    - [File descriptions](#file-descriptions)
    - [Function pointers](#function-pointers)
    - [Operators](#operators)
    - [Parameters](#parameters)
    - [Return types](#return-types)
    - [Smart text](#smart-text)
      - [Trailing](#trailing)
    - [Templates](#templates)
    - [Auto-complete doxygen commands](#auto-complete-doxygen-commands)
  - [Config options](#config-options)
  - [Contributors](#contributors)
  - [Known Issues](#known-issues)
  - [What's to come](#whats-to-come)

## Features

### Alignment

![Alignment](images/alignment.gif)

For how this works, see the [CHANGELOG.md](https://github.com/cschlosser/doxdocgen/blob/master/CHANGELOG.md#alignment)

### Attributes

![Attribute](images/attributes.gif)

### Con- and Destructors

![Constructor](images/ctor.gif)
![Destructor](images/dtor.gif)

### Extensive customization

![options](images/options.gif)
![xml options](images/opts-xml.gif)
![order of commands](images/opt-order.gif)

### File descriptions

![file description](images/file.gif)

### Function pointers

![func_ptr](images/function_ptr.gif)

### Operators

![Operator](images/operator.gif)
![Delete Operator](images/op-delete.gif)

### Parameters

![Simple Parameter](images/param_simple.gif)
![Long Parameter](images/long-param.gif)

### Return types

![Bool return val](images/bool.gif)
![Declaration](images/declaration.gif)

### Smart text

![Smart text CTor](images/smartTextCtor.gif)
![Smart text Custom](images/smartTextCustom.gif)
![Smart text Getter](images/smartTextGet.gif)

Supported smart text snippets:

* Constructors

* Destructors

* Getters

* Setters

* Factory methods

Each of them can be configured with its own custom text and you can decide if the addon should attempt to split the name of the method according to its case.

#### Trailing

![Trailing return](images/trailing.gif)

### Templates

![Template method](images/template.gif)
![Template class](images/template-class.gif)

### Auto-complete doxygen commands

![Auto complete doxygen](images/doxygen-auto-complete.gif)


## Config options

```jsonc
{
  // The prefix that is used for each comment line except for first and last.
  "doxdocgen.c.commentPrefix": " * ",

  // Smart text snippet for factory methods/functions.
  "doxdocgen.c.factoryMethodText": "Create a {name} object",

  // The first line of the comment that gets generated. If empty it won't get generated at all.
  "doxdocgen.c.firstLine": "/**",

  // Smart text snippet for getters.
  "doxdocgen.c.getterText": "Get the {name} object",

  // The last line of the comment that gets generated. If empty it won't get generated at all.
  "doxdocgen.c.lastLine": " */",

  // Smart text snippet for setters.
  "doxdocgen.c.setterText": "Set the {name} object",

  // Doxygen comment trigger. This character sequence triggers generation of Doxygen comments.
  "doxdocgen.c.triggerSequence": "/**",

  // Smart text snippet for constructors.
  "doxdocgen.cpp.ctorText": "Construct a new {name} object",

  // Smart text snippet for destructors.
  "doxdocgen.cpp.dtorText": "Destroy the {name} object",

  // The template of the template parameter Doxygen line(s) that are generated. If empty it won't get generated at all.
  "doxdocgen.cpp.tparamTemplate": "@tparam {param} ",

  // File copyright documentation tag.  Array of strings will be converted to one line per element.  Can template {year}.
  "doxdocgen.file.copyrightTag": [
    "@copyright Copyright (c) {year}"
  ],

  // Additional file documentation. One tag per line will be added. Can template `{year}`, `{date}`, `{author}`, `{email}` and `{file}`. You have to specify the prefix.
  "doxdocgen.file.customTag": [],

  // The order to use for the file comment. Values can be used multiple times. Valid values are shown in default setting.
  "doxdocgen.file.fileOrder": [
    "file",
    "author",
    "brief",
    "version",
    "date",
    "empty",
    "copyright",
    "empty",
    "custom"
  ],

  // The template for the file parameter in Doxygen.
  "doxdocgen.file.fileTemplate": "@file {name}",

  // Version number for the file.
  "doxdocgen.file.versionTag": "@version 0.1",

  // Set the e-mail address of the author.  Replaces {email}.
  "doxdocgen.generic.authorEmail": "you@domain.com",

  // Set the name of the author.  Replaces {author}.
  "doxdocgen.generic.authorName": "your name",

  // Set the style of the author tag and your name.  Can template {author} and {email}.
  "doxdocgen.generic.authorTag": "@author {author} ({email})",

  // If this is enabled a bool return value will be split into true and false return param.
  "doxdocgen.generic.boolReturnsTrueFalse": true,

  // The template of the brief Doxygen line that is generated. If empty it won't get generated at all.
  "doxdocgen.generic.briefTemplate": "@brief {text}",

  // The format to use for the date.
  "doxdocgen.generic.dateFormat": "YYYY-MM-DD",

  // The template for the date parameter in Doxygen.
  "doxdocgen.generic.dateTemplate": "@date {date}",

  // Decide if you want to get smart text for certain commands.
  "doxdocgen.generic.generateSmartText": true,

  // Whether include type information at return.
  "doxdocgen.generic.includeTypeAtReturn": true,

  // How many lines the plugin should look for to find the end of the declaration. Please be aware that setting this value too low could improve the speed of comment generation by a very slim margin but the plugin also may not correctly detect all declarations or definitions anymore.
  "doxdocgen.generic.linesToGet": 20,

  // The order to use for the comment generation. Values can be used multiple times. Valid values are shown in default setting.
  "doxdocgen.generic.order": [
    "brief",
    "empty",
    "tparam",
    "param",
    "return",
    "custom",
    "version",
    "author",
    "date",
    "copyright"
  ],

  // Custom tags to be added to the generic order. One tag per line will be added. Can template `{year}`, `{date}`, `{author}`, `{email}` and `{file}`. You have to specify the prefix.
  "doxdocgen.generic.customTags": [],

  // The template of the param Doxygen line(s) that are generated. If empty it won't get generated at all.
  "doxdocgen.generic.paramTemplate": "@param {param} ",

  // The template of the return Doxygen line that is generated. If empty it won't get generated at all.
  "doxdocgen.generic.returnTemplate": "@return {type} ",

  // Decide if the values put into {name} should be split according to their casing.
  "doxdocgen.generic.splitCasingSmartText": true,

  // Array of keywords that should be removed from the input prior to parsing.
  "doxdocgen.generic.filteredKeywords": [],

  // Substitute {author} with git config --get user.name.
  "doxdocgen.generic.useGitUserName": false,

  // Substitute {email} with git config --get user.email.
  "doxdocgen.generic.useGitUserEmail": false,

  // Provide intellisense and snippet for doxygen commands
  "doxdocgen.generic.commandSuggestion": true,

  // Add `\\` in doxygen command suggestion for better readability (need to enable commandSuggestion)
  "doxdocgen.generic.commandSuggestionAddPrefix": false,
}
```

## Contributors

[Christoph Schlosser](https://github.com/cschlosser)

[Rowan Goemans](https://github.com/rowanG077)

## Known Issues

[See open bugs](https://github.com/cschlosser/doxdocgen/labels/bug)

## What's to come

[See open features](https://github.com/cschlosser/doxdocgen/labels/enhancement)


================================================
FILE: package.json
================================================
{
  "name": "doxdocgen",
  "displayName": "Doxygen Documentation Generator",
  "description": "Let me generate Doxygen documentation from your source code for you.",
  "version": "1.4.0",
  "publisher": "cschlosser",
  "engines": {
    "vscode": "^1.55.0"
  },
  "categories": [
    "Other"
  ],
  "badges": [
    {
      "url": "https://img.shields.io/github/workflow/status/cschlosser/doxdocgen/CI/master",
      "href": "https://github.com/cschlosser/doxdocgen/actions/workflows/ci.yml?query=branch%3Amaster+",
      "description": "Continous integration"
    },
    {
      "url": "https://img.shields.io/github/workflow/status/cschlosser/doxdocgen/CD?label=Release",
      "href": "https://github.com/cschlosser/doxdocgen/actions/workflows/cd.yml?query=event%3Arelease++",
      "description": "Release pipeline"
    },
    {
      "url": "https://codecov.io/gh/cschlosser/doxdocgen/branch/master/graph/badge.svg",
      "href": "https://codecov.io/gh/cschlosser/doxdocgen",
      "description": "Code coverage"
    }
  ],
  "activationEvents": [
    "onLanguage:cuda",
    "onLanguage:cuda-cpp",
    "onLanguage:cpp",
    "onLanguage:c"
  ],
  "contributes": {
    "configuration": {
      "type": "object",
      "title": "Doxygen Documentation Generator Settings",
      "properties": {
        "doxdocgen.c.triggerSequence": {
          "description": "Doxygen comment trigger. This character sequence triggers generation of Doxygen comments.",
          "type": "string",
          "default": "/**"
        },
        "doxdocgen.c.firstLine": {
          "description": "The first line of the comment that gets generated. If empty it won't get generated at all.",
          "type": "string",
          "default": "/**"
        },
        "doxdocgen.c.commentPrefix": {
          "description": "The prefix that is used for each comment line except for first and last.",
          "type": "string",
          "default": " * "
        },
        "doxdocgen.c.lastLine": {
          "description": "The last line of the comment that gets generated. If empty it won't get generated at all.",
          "type": "string",
          "default": " */"
        },
        "doxdocgen.c.setterText": {
          "description": "Smart text snippet for setters.",
          "type": "string",
          "default": "Set the {name} object"
        },
        "doxdocgen.c.getterText": {
          "description": "Smart text snippet for getters.",
          "type": "string",
          "default": "Get the {name} object"
        },
        "doxdocgen.c.factoryMethodText": {
          "description": "Smart text snippet for factory methods/functions.",
          "type": "string",
          "default": "Create a {name} object"
        },
        "doxdocgen.cpp.tparamTemplate": {
          "description": "The template of the template parameter Doxygen line(s) that are generated. If empty it won't get generated at all.",
          "type": "string",
          "default": "@tparam {param} "
        },
        "doxdocgen.cpp.ctorText": {
          "description": "Smart text snippet for constructors.",
          "type": "string",
          "default": "Construct a new {name} object"
        },
        "doxdocgen.cpp.dtorText": {
          "description": "Smart text snippet for destructors.",
          "type": "string",
          "default": "Destroy the {name} object"
        },
        "doxdocgen.file.fileTemplate": {
          "description": "The template for the file parameter in Doxygen.",
          "type": "string",
          "default": "@file {name}"
        },
        "doxdocgen.file.copyrightTag": {
          "markdownDescription": "File copyright documentation tag.  Array of strings will be converted to one line per element. Can template `{year}`.",
          "type": [
            "array",
            "string"
          ],
          "default": [
            "@copyright Copyright (c) {year}"
          ]
        },
        "doxdocgen.file.versionTag": {
          "description": "Version number for the file.",
          "type": "string",
          "default": "@version 0.1"
        },
        "doxdocgen.file.customTag": {
          "markdownDescription": "Additional file documentation.  Array of strings will be converted to one line per element. Can template `{year}`, `{date}`, `{author}`, `{email}` and `{file}`. You have to specify the prefix.",
          "type": [
            "array",
            "string"
          ],
          "default": []
        },
        "doxdocgen.file.fileOrder": {
          "markdownDescription": "The order to use for the file comment. Values can be used multiple times. Valid values are `file`, `author`, `brief`, `version`, `date`, `empty`, `copyright` and `custom`.",
          "type": [
            "array",
            "string"
          ],
          "default": [
            "file",
            "author",
            "brief",
            "version",
            "date",
            "empty",
            "copyright",
            "empty",
            "custom"
          ]
        },
        "doxdocgen.generic.includeTypeAtReturn": {
          "description": "Whether include type information at return.",
          "type": "boolean",
          "default": true
        },
        "doxdocgen.generic.boolReturnsTrueFalse": {
          "markdownDescription": "If this is enabled, the documentation for a `bool` return value will be split into `true` and `false` entries.",
          "type": "boolean",
          "default": true
        },
        "doxdocgen.generic.briefTemplate": {
          "description": "The template of the brief Doxygen line that is generated. If empty it won't get generated at all.",
          "type": "string",
          "default": "@brief {text}"
        },
        "doxdocgen.generic.paramTemplate": {
          "description": "The template of the param Doxygen line(s) that are generated. If empty it won't get generated at all.",
          "type": "string",
          "default": "@param {param} "
        },
        "doxdocgen.generic.returnTemplate": {
          "description": "The template of the return Doxygen line that is generated. If empty it won't get generated at all.",
          "type": "string",
          "default": "@return {type} "
        },
        "doxdocgen.generic.linesToGet": {
          "description": "How many lines the plugin should look for to find the end of the declaration. Please be aware that setting this value too low could improve the speed of comment generation by a very slim margin but the plugin also may not correctly detect all declarations or definitions anymore.",
          "type": "number",
          "default": 20
        },
        "doxdocgen.generic.authorName": {
          "markdownDescription": "Set the name of the author.  Replaces `{author}`.",
          "type": "string",
          "default": "your name"
        },
        "doxdocgen.generic.authorEmail": {
          "markdownDescription": "Set the e-mail address of the author.  Replaces `{email}`.",
          "type": "string",
          "default": "you@domain.com"
        },
        "doxdocgen.generic.authorTag": {
          "markdownDescription": "Set the style of the author tag and your name.  Can template `{author}` and `{email}`.",
          "type": "string",
          "default": "@author {author} ({email})"
        },
        "doxdocgen.generic.dateTemplate": {
          "description": "The template for the date parameter in Doxygen.",
          "type": "string",
          "default": "@date {date}"
        },
        "doxdocgen.generic.dateFormat": {
          "description": "The format to use for the date.",
          "type": "string",
          "default": "YYYY-MM-DD"
        },
        "doxdocgen.generic.generateSmartText": {
          "description": "Decide if you want to get smart text for certain commands.",
          "type": "boolean",
          "default": true
        },
        "doxdocgen.generic.splitCasingSmartText": {
          "markdownDescription": "Decide if the values put into `{name}` should be split according to their casing.",
          "type": "boolean",
          "default": true
        },
        "doxdocgen.generic.order": {
          "markdownDescription": "The order to use for the comment generation. Values can be used multiple times. Valid values are `brief`, `empty`, `tparam`, `param`, `return`, `custom`, `author`, `date`, `version` and `copyright`.",
          "type": [
            "array",
            "string"
          ],
          "default": [
            "brief",
            "empty",
            "tparam",
            "param",
            "return",
            "custom"
          ]
        },
        "doxdocgen.generic.customTags": {
          "markdownDescription": "Custom tags to be added to the generic order. One tag per line will be added. Can template `{year}`, `{date}`, `{author}`, `{email}` and `{file}`. You have to specify the prefix.",
          "type": [
            "array",
            "string"
          ],
          "default": []
        },
        "doxdocgen.generic.filteredKeywords": {
          "description": "Array of keywords that should be removed from the input prior to parsing.",
          "type": "array",
          "default": []
        },
        "doxdocgen.generic.useGitUserName": {
          "markdownDescription": "Substitute `{author}` with `git config --get user.name`.",
          "type": "boolean",
          "default": false
        },
        "doxdocgen.generic.useGitUserEmail": {
          "markdownDescription": "Substitute `{email}` with `git config --get user.email`.",
          "type": "boolean",
          "default": false
        },
        "doxdocgen.generic.commandSuggestion": {
          "description": "Provide intellisense and snippet for doxygen commands",
          "type": "boolean",
          "default": true
        },
        "doxdocgen.generic.commandSuggestionAddPrefix": {
          "markdownDescription": "Add `\\` in doxygen command suggestion for better readability (need to enable commandSuggestion)",
          "type": "boolean",
          "default": false
        }
      }
    }
  },
  "icon": "images/icon.png",
  "keywords": [
    "cpp",
    "c++",
    "c",
    "Doxygen"
  ],
  "license": "SEE LICENSE IN LICENSE",
  "main": "./out/extension",
  "repository": {
    "type": "git",
    "url": "https://github.com/cschlosser/doxdocgen.git"
  },
  "bugs": {
    "url": "https://github.com/cschlosser/doxdocgen/labels/bug"
  },
  "scripts": {
    "vscode:prepublish": "yarn compile",
    "compile": "tsc -p ./",
    "watch": "tsc -watch -p ./",
    "test": "yarn compile && node ./out/test/runTests.js",
    "cov": "yarn clean && nyc yarn test",
    "lint": "tslint -c tslint.json 'src/**/*.ts'",
    "clean": "rm -rf coverage .nyc_output out"
  },
  "dependencies": {
    "env-var": "^4.1.0",
    "moment": "^2.29.4",
    "opn": "^5.2.0",
    "simple-git": "^3.5.0"
  },
  "devDependencies": {
    "@istanbuljs/nyc-config-typescript": "0.1.3",
    "@types/mocha": "^5.2.7",
    "@types/node": "12.7.1",
    "@types/vscode": "^1.55.0",
    "decache": "^4.5.1",
    "glob": "^7.1.6",
    "handlebars": "^4.7.3",
    "minimist": ">=1.2.6",
    "mocha": "^9.1.3",
    "nyc": "12.0.1",
    "remap-istanbul": "^0.13.0",
    "source-map-support": "^0.5.19",
    "ts-node": "^9.1.1",
    "tslint": "^5.20.0",
    "typescript": "^4.2.4",
    "vscode-test": "^1.5.2",
    "yargs-parser": ">=13.1.2"
  }
}


================================================
FILE: src/CodeParserController.ts
================================================
import {
    Disposable,
    Position,
    Range,
    TextDocumentContentChangeEvent,
    TextEditor,
    TextLine,
    window,
    workspace,
} from "vscode";
import CodeParser from "./Common/ICodeParser";
import { Config } from "./Config";
import GitConfig from "./GitConfig";
import CppParser from "./Lang/Cpp/CppParser";
import { inComment } from "./util";
/**
 *
 * Checks if the event matches the specified guidelines and if a parser exists for this language
 *
 * @export
 * @class CodeParserController
 */
export default class CodeParserController {
    private disposable: Disposable;
    private cfg: Config;
    private gitConfig: GitConfig;

    /**
     * Creates an instance of CodeParserController
     *
     * @memberOf CodeParserController
     */
    public constructor() {
        const subscriptions: Disposable[] = [];
        this.gitConfig = new GitConfig();

        // Hand off the event to the parser if a valid parser is found
        workspace.onDidChangeTextDocument((event) => {
            const activeEditor: TextEditor = window.activeTextEditor;
            if (activeEditor && event.document === activeEditor.document) {
                this.cfg = Config.ImportFromSettings();
                this.onEvent(activeEditor, event.contentChanges[0]);
            }
        }, this, subscriptions);

        this.disposable = Disposable.from(...subscriptions);
    }

    /**
     *
     * Disposes of the subscriptions
     *
     * @memberOf CodeParserController
     */
    public dispose() {
        this.disposable.dispose();
    }

    /***************************************************************************
                                    Implementation
     ***************************************************************************/

    private check(activeEditor: TextEditor, event: TextDocumentContentChangeEvent): boolean {
        if (activeEditor === undefined || activeEditor == null ||
            event === undefined || event.text == null) {
            return false;
        }
        const activeSelection: Position = activeEditor.selection.active;
        const activeLine: TextLine = activeEditor.document.lineAt(activeSelection.line);
        const activeChar: string = activeLine.text.charAt(activeSelection.character);
        const startsWith: boolean = event.text.startsWith("\n") || event.text.startsWith("\r\n");

        // Check if enter was pressed. Note the !
        if (!((activeChar === "") && startsWith)) {
            return false;
        }

        // Check if currently in a comment block
        if (inComment(activeEditor, activeSelection.line)) {
            return false;
        }

        // Do not trigger when there's whitespace after the trigger sequence
        // tslint:disable-next-line:max-line-length
        const seq = "[\\s]*(" + this.cfg.C.triggerSequence.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&") + ")$";
        const match: RegExpMatchArray = activeLine.text.match(seq);

        if (match !== null) {
            const cont: string = match[1];
            return this.cfg.C.triggerSequence === cont;
        } else {
            return false;
        }
    }

    private onEvent(activeEditor: TextEditor, event: TextDocumentContentChangeEvent) {
        if (!this.check(activeEditor, event)) {
            return null;
        }

        const lang: string = activeEditor.document.languageId;
        let parser: CodeParser;

        switch (lang) {
            case "c":
            case "cpp":
            case "cuda":
            case "cuda-cpp":
                parser = new CppParser(this.cfg);
                break;
            default:
                // tslint:disable-next-line:no-console
                console.log("No comments can be generated for language: " + lang);
                return null;
        }

        const currentPos: Position = window.activeTextEditor.selection.active;
        const startReplace: Position = new Position(
            currentPos.line,
            currentPos.character - this.cfg.C.triggerSequence.length,
        );

        const nextLineText: string = window.activeTextEditor.document.lineAt(startReplace.line + 1).text;
        const endReplace = new Position(currentPos.line + 1, nextLineText.length);

        parser.Parse(activeEditor).GenerateDoc(new Range(startReplace, endReplace), this.gitConfig);
    }
}


================================================
FILE: src/Common/ICodeParser.ts
================================================
import { TextEditor } from "vscode";
import { IDocGen } from "./IDocGen";

export default interface ICodeParser {
    /**
     * @param  {TextEditor} activeEditor The open active Editor where the event came from
     */
    Parse(activeEditor: TextEditor): IDocGen;
}


================================================
FILE: src/Common/IDocGen.ts
================================================
import { Range } from "vscode";
import GitConfig from "../GitConfig";

export interface IDocGen {
    /**
     * @brief Generate documentation string and write it to the active editor
     * @param {Range} rangeToReplace Range to replace with the generated comment.
     */
    GenerateDoc(rangeToReplace: Range, gitConfig: GitConfig);
}


================================================
FILE: src/Config.ts
================================================
import { workspace } from "vscode";
// tslint:disable:max-classes-per-file
// tslint:disable:max-line-length

class C {
    public static getConfiguration() {
        return workspace.getConfiguration("doxdocgen.c");
    }

    public triggerSequence: string = "/**";
    public firstLine: string = "/**";
    public commentPrefix: string = " * ";
    public lastLine: string = " */";
    public getterText: string = "Get the {name} object";
    public setterText: string = "Set the {name} object";
    public factoryMethodText: string = "Create a {name} object";
}

class Cpp {
    public static getConfiguration() {
        return workspace.getConfiguration("doxdocgen.cpp");
    }

    public tparamTemplate: string = "@tparam {param} ";
    public ctorText: string = "Construct a new {name} object";
    public dtorText: string = "Destroy the {name} object";
}

class File {
    public static getConfiguration() {
        return workspace.getConfiguration("doxdocgen.file");
    }

    public fileTemplate: string = "@file {name}";
    public copyrightTag: string[] = ["@copyright Copyright (c) {year}"];
    public versionTag: string = "@version 0.1";
    public customTag: string[] = [];
    public fileOrder: string[] = ["brief", "empty", "file", "author", "date"];
}

class Generic {
    public static getConfiguration() {
        return workspace.getConfiguration("doxdocgen.generic");
    }

    public includeTypeAtReturn: boolean = true;
    public boolReturnsTrueFalse: boolean = true;
    public briefTemplate: string = "@brief {text}";
    public paramTemplate: string = "@param {param} ";
    public returnTemplate: string = "@return {type} ";
    public linesToGet: number = 20;
    public authorName: string = "your name";
    public authorEmail: string = "you@domain.com";
    public authorTag: string = "@author {author} ({email})";
    public dateTemplate: string = "@date {date}";
    public dateFormat: string = "YYYY-MM-DD";
    public generateSmartText: boolean = true;
    public splitCasingSmartText: boolean = true;
    public order: string[] = ["brief", "empty", "tparam", "param", "return"];
    public customTags: string[] = [];
    public filteredKeywords: string[] = [];
    public useGitUserName: boolean = false;
    public useGitUserEmail: boolean = false;
}

export class Config {
    public static ImportFromSettings(): Config {
        const values: Config = new Config();

        values.C.triggerSequence = C.getConfiguration().get<string>("triggerSequence", values.C.triggerSequence);
        values.C.firstLine = C.getConfiguration().get<string>("firstLine", values.C.firstLine);
        values.C.commentPrefix = C.getConfiguration().get<string>("commentPrefix", values.C.commentPrefix);
        values.C.lastLine = C.getConfiguration().get<string>("lastLine", values.C.lastLine);
        values.C.getterText = C.getConfiguration().get<string>("getterText", values.C.getterText);
        values.C.setterText = C.getConfiguration().get<string>("setterText", values.C.setterText);
        values.C.factoryMethodText = C.getConfiguration().get<string>("factoryMethodText", values.C.factoryMethodText);

        values.Cpp.tparamTemplate = Cpp.getConfiguration().get<string>("tparamTemplate", values.Cpp.tparamTemplate);
        values.Cpp.ctorText = Cpp.getConfiguration().get<string>("ctorText", values.Cpp.ctorText);
        values.Cpp.dtorText = Cpp.getConfiguration().get<string>("dtorText", values.Cpp.dtorText);

        values.File.fileTemplate = File.getConfiguration().get<string>("fileTemplate", values.File.fileTemplate);
        values.File.versionTag = File.getConfiguration().get<string>("versionTag", values.File.versionTag);
        values.File.copyrightTag = File.getConfiguration().get<string[]>("copyrightTag", values.File.copyrightTag);
        values.File.customTag = File.getConfiguration().get<string[]>("customTag", values.File.customTag);
        values.File.fileOrder = File.getConfiguration().get<string[]>("fileOrder", values.File.fileOrder);

        values.Generic.includeTypeAtReturn = Generic.getConfiguration().get<boolean>("includeTypeAtReturn", values.Generic.includeTypeAtReturn);
        values.Generic.boolReturnsTrueFalse = Generic.getConfiguration().get<boolean>("boolReturnsTrueFalse", values.Generic.boolReturnsTrueFalse);
        values.Generic.briefTemplate = Generic.getConfiguration().get<string>("briefTemplate", values.Generic.briefTemplate);
        values.Generic.paramTemplate = Generic.getConfiguration().get<string>("paramTemplate", values.Generic.paramTemplate);
        values.Generic.returnTemplate = Generic.getConfiguration().get<string>("returnTemplate", values.Generic.returnTemplate);
        values.Generic.linesToGet = Generic.getConfiguration().get<number>("linesToGet", values.Generic.linesToGet);
        values.Generic.authorTag = Generic.getConfiguration().get<string>("authorTag", values.Generic.authorTag);
        values.Generic.authorName = Generic.getConfiguration().get<string>("authorName", values.Generic.authorName);
        values.Generic.authorEmail = Generic.getConfiguration().get<string>("authorEmail", values.Generic.authorEmail);
        values.Generic.dateTemplate = Generic.getConfiguration().get<string>("dateTemplate", values.Generic.dateTemplate);
        values.Generic.dateFormat = Generic.getConfiguration().get<string>("dateFormat", values.Generic.dateFormat);
        values.Generic.generateSmartText = Generic.getConfiguration().get<boolean>("generateSmartText", values.Generic.generateSmartText);
        values.Generic.splitCasingSmartText = Generic.getConfiguration().get<boolean>("splitCasingSmartText", values.Generic.splitCasingSmartText);
        values.Generic.order = Generic.getConfiguration().get<string[]>("order", values.Generic.order);
        values.Generic.customTags = Generic.getConfiguration().get<string[]>("customTags", values.Generic.customTags);
        values.Generic.filteredKeywords = Generic.getConfiguration().get<string[]>("filteredKeywords", values.Generic.filteredKeywords);
        values.Generic.useGitUserName = Generic.getConfiguration().get<boolean>("useGitUserName", values.Generic.useGitUserName);
        values.Generic.useGitUserEmail = Generic.getConfiguration().get<boolean>("useGitUserEmail", values.Generic.useGitUserEmail);

        return values;
    }

    public readonly paramTemplateReplace: string = "{param}";
    public readonly typeTemplateReplace: string = "{type}";
    public readonly nameTemplateReplace: string = "{name}";
    public readonly authorTemplateReplace: string = "{author}";
    public readonly emailTemplateReplace: string = "{email}";
    public readonly dateTemplateReplace: string = "{date}";
    public readonly yearTemplateReplace: string = "{year}";
    public readonly textTemplateReplace: string = "{text}";

    public C: C;
    public Cpp: Cpp;
    public File: File;
    public Generic: Generic;

    constructor() {
        this.C = new C();
        this.Cpp = new Cpp();
        this.File = new File();
        this.Generic = new Generic();
    }
}


================================================
FILE: src/DoxygenCompletionItemProvider.ts
================================================
import * as vscode from "vscode";
import { inComment } from "./util";

// tslint:disable:max-line-length

/*https://github.com/cschlosser/doxdocgen/issues/30 */
export default class DoxygenCompletionItemProvider implements vscode.CompletionItemProvider {
    /**
     * commands are a tuple of <command, snippet, documentation>
     */
    public static readonly commands: Array<[string, string, string]> = [
        /*Special commands */
        ["a", "${1:word}", "Display `<word>` in italics"],
        ["arg", "${1:item-description}", "Generate a simple, non-nested list of arguments"],
        ["b", "${1:word}", "Display `<word>` in bold"],
        ["c", "${1:word}", "Display `<word>` using a typewriter font"],
        ["code", "{.${1:language-id}}\n${2:code}\n@endcode", "Starts a block of code"], // TODO: match end block symbol with trigger character
        ["copydoc", "${1:link-object}", "Copy a documentation block from the object specified by `<link-object>` and paste it at the location of the command. The link object can point to a member (of a class, file or group), a class, a namespace, a group, a page, or a file. If the member is overloaded, you should specify the argument types explicitly"],
        ["copybrief", "${1:link-object}", "Work in a similar way as `@copydoc` but will only copy the brief description, not the detailed documentation"],
        ["copydetails", "${1:link-object}", "Work in a similar way as `@copydoc` but will only copy the detailed documentation, not the brief description"],
        ["docbookonly", "$1\n@enddocbookonly", "Start a block of text that only will be verbatim included in the generated DocBook documentation and tagged with `<docbookonly>` in the generated XML output"],
        ["emoji", ":${1:name}:", "Produce an emoji character given its name"],
        ["startuml", "${1:file} \"${2:caption}\" ${3:size_indication}=${4:size}\n@enduml", "Start a text fragment which should contain a valid description of a PlantUML diagram"],
        ["e", "${1:word}", "Display `<word>` in italics"],
        ["em", "${1:word}", "Display `<word>` in italics"],
        ["endcode", "", "End a block of code"],
        ["enddocbookonly", "", "End a block of text that was started with `@docbookonly` command"],
        ["enduml", "", "End a block that was started with `@startuml`"],
        ["endhtmlonly", "", "End a block of text that was started with a `@htmlonly` command"],
        ["endlatexonly", "", "End a block of text that was started with a `@latexonly` command"],
        ["endmanonly", "", "End a block of text that was started with a `@manonly` command"],
        ["endxmlonly", "", "End a block of text that was started with a `@xmlonly` command"],
        ["htmlonly", "$1\n@endhtmlonly", "Start a block of text that only will be verbatim included in the generated HTML documentation and tagged with `<htmlonly>` in the generated XML output"],
        ["image", "${1|html,latex,docbook,rtf|} ${2:file} ${3:caption} ${4:${5:size_inidication}=${6:size}}", "Insert an image into the documentation"],
        ["latexonly", "$1\n@endlatexonly", "Start a block of text that only will be verbatim included in the generated LATEX documentation and tagged with `<latexonly>` in the generated XML output"],
        ["manonly", "$1\n@manonly", "Start a block of text that only will be verbatim included in the generated MAN documentation and tagged with `<manonly>` in the generated XML output"],
        ["li", "${1:item-description}", "Generate a simple, non-nested list of arguments"],
        ["n", "", "Force a new line"],
        ["verbatim", "$1\n@verbatim", "Start a block of text that will be verbatim included in the documentation"],
        ["xmlonly", "$1\n@endxmlonly", "Start a block of text that will be verbatim included in the documentation"],
        /*Section indicators */
        ["attention", "${1:attention text}", "Start a paragraph where a message that needs attention may be entered. The paragraph will be indented. The text of the paragraph has no special internal structure. All visual enhancement commands may be used inside the paragraph. Multiple adjacent `@attention` commands will be joined into a single paragraph. The `@attention` command ends when a blank line or some other sectioning command is encountered."],
        ["author", "${1:author}", "Starts a paragraph where one or more author names may be entered. The paragraph will be indented. The text of the paragraph has no special internal structure. All visual enhancement commands may be used inside the paragraph. Multiple adjacent `@author` commands will be joined into a single paragraph. Each author description will start a new line. Alternatively, one `@author` command may mention several authors. The `@author` command ends when a blank line or some other sectioning command is encountered."],
        ["authors", "${1:list of authors}", "Equivalent to `@author`"],
        ["brief", "${1:brief description}", "Starts a paragraph that serves as a brief description. For classes and files the brief description will be used in lists and at the start of the documentation page. For class and file members, the brief description will be placed at the declaration of the member and prepended to the detailed description. A brief description may span several lines (although it is advised to keep it brief!). A brief description ends when a blank line or another sectioning command is encountered. If multiple `@brief` commands are present they will be joined."],
        ["bug", "${1:bug description}", "Starts a paragraph where one or more bugs may be reported. The paragraph will be indented. The text of the paragraph has no special internal structure. All visual enhancement commands may be used inside the paragraph. Multiple adjacent `@bug` commands will be joined into a single paragraph. Each bug description will start on a new line. Alternatively, one `@bug` command may mention several bugs. The `@bug` command ends when a blank line or some other sectioning command is encountered."],
        ["cond", "${1:section label}", "Starts a conditional section that ends with a corresponding `@endcond` command, which is typically found in another comment block"],
        ["copyright", "${1:copyright description}", "Starts a paragraph where the copyright of an entity can be described. This paragraph will be indented."],
        ["date", "${1:date description}", "Starts a paragraph where one or more dates may be entered. The paragraph will be indented. The text of the paragraph has no special internal structure. All visual enhancement commands may be used inside the paragraph. Multiple adjacent `@date` commands will be joined into a single paragraph. Each date description will start on a new line. Alternatively, one `@date` command may mention several dates. The `@date` command ends when a blank line or some other sectioning command is encountered."],
        ["deprecated", "${1:description}", "Starts a paragraph indicating that this documentation block belongs to a deprecated entity"],
        ["details", "${1:description}", "Just like `@brief` starts a brief description, `@details` starts the detailed description. You can also start a new paragraph (blank line) then the `@details` command is not needed."],
        ["noop", "", "All the text, including the command, till the end of the line is ignored"],
        ["else", "", "Starts a conditional section if the previous conditional section was not enabled. The previous section should have been started with a `@if`, `@ifnot`, or `@elseif` command."],
        ["elseif", "${1:section-label}", "Starts a conditional documentation section if the previous section was not enabled. A conditional section is disabled by default. To enable it you must put the section-label after the ENABLED_SECTIONS tag in the configuration file. The section label can be a logical expression build of section names, round brackets, && (AND), || (OR) and ! (NOT). Conditional blocks can be nested. A nested section is only enabled if all enclosing sections are enabled as well."],
        ["endcond", "", "Ends a conditional section that was started by `@cond`."],
        ["endif", "", "Ends a conditional section that was started by `@if` or `@ifnot` For each `@if` or `@ifnot` one and only one matching `@endif` must follow."],
        ["exception", "${1:exception-object} ${2:exception description}", "Starts an exception description for an exception object with name `<exception-object>`. Followed by a description of the exception. The existence of the exception object is not checked. The text of the paragraph has no special internal structure. All visual enhancement commands may be used inside the paragraph. Multiple adjacent @exception commands will be joined into a single paragraph. Each exception description will start on a new line. The `@exception` description ends when a blank line or some other sectioning command is encountered"],
        ["if", "${1:section-label}\n$2\n@endif", "Starts a conditional documentation section. The section ends with a matching `@endif` command. A conditional section is disabled by default. To enable it you must put the section-label after the ENABLED_SECTIONS tag in the configuration file. The section label can be a logical expression build of section names, round brackets, && (AND), || (OR) and!(NOT). If you use an expression you need to wrap it in round brackets, i.e. `@cond(!LABEL1 && LABEL2)`."],
        ["ifnot", "${1:section-label}", "Starts a conditional documentation section. The section ends with a matching `@endif` command. This conditional section is enabled by default. To disable it you must put the section-label after the ENABLED_SECTIONS tag in the configuration file. The section label can be a logical expression build of section names, round brackets, && (AND), || (OR) and ! (NOT)."],
        ["invariant", "${1:description of invariant}", "Starts a paragraph where the invariant of an entity can be described. The paragraph will be indented. The text of the paragraph has no special internal structure. All visual enhancement commands may be used inside the paragraph. Multiple adjacent `@invariant` commands will be joined into a single paragraph. Each invariant description will start on a new line. Alternatively, one `@invariant` command may mention several invariants. The `@invariant` command ends when a blank line or some other sectioning command is encountered."],
        ["note", "${1:text}", "Starts a paragraph where a note can be entered. The paragraph will be indented. The text of the paragraph has no special internal structure. All visual enhancement commands may be used inside the paragraph. Multiple adjacent `@note` commands will be joined into a single paragraph. Each note description will start on a new line. Alternatively, one `@note` command may mention several notes. The `@note` command ends when a blank line or some other sectioning command is encountered"],
        ["par", "${1:paragraph title}\n$2", "If a paragraph title is given this command starts a paragraph with a user defined heading. The heading extends until the end of the line. The paragraph following the command will be indented. If no paragraph title is given this command will start a new paragraph. The text of the paragraph has no special internal structure. All visual enhancement commands may be used inside the paragraph. The `@par` command ends when a blank line or some other sectioning command is encountered."],
        ["param", "${1:parameter-name} ${2:description}", "Starts a parameter description for a function parameter with name `<parameter-name>`, followed by a description of the parameter. The existence of the parameter is checked and a warning is given if the documentation of this (or any other) parameter is missing or not present in the function declaration or definition. Multiple adjacent `@param` commands will be joined into a single paragraph. Each parameter description will start on a new line. The `@param` description ends when a blank line or some other sectioning command is encountered. Note that you can also document multiple parameters with a single `@param` command using a comma separated list."],
        ["parblock", "\n$1\n@endparblock", "For commands that expect a single paragraph as argument (such as `@par`, `@param` and `@warning`), the `@parblock` command allows to start a description that covers multiple paragraphs, which then ends with `@endparblock`."],
        ["endparblock", "", "This ends a block of paragraphs started with `@parblock`."],
        ["tparam", "${1:template-parameter-name} ${2:description}", "Starts a template parameter for a class or function template parameter with name `<template-parameter-name>`, followed by a description of the template parameter."],
        ["post", "${1:description of the postcondition}", "Starts a paragraph where the postcondition of an entity can be described. The paragraph will be indented. The text of the paragraph has no special internal structure. All visual enhancement commands may be used inside the paragraph. Multiple adjacent `@post` commands will be joined into a single paragraph. Each postcondition will start on a new line. Alternatively, one `@post` command may mention several postconditions. The `@post` command ends when a blank line or some other sectioning command is encountered."],
        ["pre", "${1:description of the precondition}", "Starts a paragraph where the precondition of an entity can be described. The paragraph will be indented. The text of the paragraph has no special internal structure. All visual enhancement commands may be used inside the paragraph. Multiple adjacent `@pre` commands will be joined into a single paragraph. Each precondition will start on a new line. Alternatively, one `@pre` command may mention several preconditions. The `@pre` command ends when a blank line or some other sectioning command is encountered."],
        ["remark", "${1:remark text}", "Starts a paragraph where one or more remarks may be entered. The paragraph will be indented. The text of the paragraph has no special internal structure. All visual enhancement commands may be used inside the paragraph. Multiple adjacent `@remark` commands will be joined into a single paragraph. Each remark will start on a new line. Alternatively, one `@remark` command may mention several remarks. The `@remark` command ends when a blank line or some other sectioning command is encountered."],
        ["remarks", "${1:remark text}", "Equivalent to `@remark`"],
        ["result", "${1:description of the result value}", "Equivalent to `@return`"],
        ["return", "${1:description of the return value}", "Starts a return value description for a function. The text of the paragraph has no special internal structure. All visual enhancement commands may be used inside the paragraph. Multiple adjacent `@return` commands will be joined into a single paragraph. The `@return` description ends when a blank line or some other sectioning command is encountered"],
        ["returns", "${1:description of the return value}", "Equivalent to `@return`"],
        ["retval", "${1:return value} ${2:description}", "Starts a description for a function's return value with name `<return value>`, followed by a description of the return value. The text of the paragraph that forms the description has no special internal structure. All visual enhancement commands may be used inside the paragraph. Multiple adjacent `@retval` commands will be joined into a single paragraph. Each return value description will start on a new line. The `@retval` description ends when a blank line or some other sectioning command is encountered."],
        ["sa", "${1:reference}", "Starts a paragraph where one or more cross-references to classes, functions, methods, variables, files or URL may be specified. Two names joined by either `::` or `#` are understood as referring to a class and one of its members. One of several overloaded methods or constructors may be selected by including a parenthesized list of argument types after the method name."],
        ["see", "${1:reference}", "Equivalent to `@sa`"],
        ["short", "${1:short description", "Equivalent to `@brief`"],
        ["since", "${1:text}", "This command can be used to specify since when (version or time) an entity is available. The paragraph that follows `@since` does not have any special internal structure. All visual enhancement commands may be used inside the paragraph. The `@since` description ends when a blank line or some other sectioning command is encountered."],
        ["test", "${1:paragraph describing a test case", "This command can be used to specify since when (version or time) an entity is available. The paragraph that follows `@since` does not have any special internal structure. All visual enhancement commands may be used inside the paragraph. The `@since` description ends when a blank line or some other sectioning command is encountered."],
        ["throw", "${1:exception-object} ${2:exception description}", "Equivalent to `@exception`"],
        ["throws", "${1:exception-object} ${2:exception description}", "Equivalent to `@exception`"],
        ["todo", "${1:paragraph describing what is to be done}", "Starts a paragraph where a `TODO` item is described. The description will also add an item to a separate `TODO` list. The two instances of the description will be cross-referenced. Each item in the `TODO` list will be preceded by a header that indicates the origin of the item."],
        ["version", "${1:version number}", "Starts a paragraph where one or more version strings may be entered. The paragraph will be indented. The text of the paragraph has no special internal structure. All visual enhancement commands may be used inside the paragraph. Multiple adjacent `@version` commands will be joined into a single paragraph. Each version description will start on a new line. Alternatively, one `@version` command may mention several version strings. The `@version` command ends when a blank line or some other sectioning command is encountered."],
        ["warning", "${1:warning message}", "Starts a paragraph where one or more warning messages may be entered. The paragraph will be indented. The text of the paragraph has no special internal structure. All visual enhancement commands may be used inside the paragraph. Multiple adjacent `@warning` commands will be joined into a single paragraph. Each warning description will start on a new line. Alternatively, one `@warning` command may mention several warnings. The `@warning` command ends when a blank line or some other sectioning command is encountered."],
        ["xrefitem", "${1:key} \"${2:heading}\" \"${3:list title}\" ${4:text}", "This command is a generalization of commands such as `@todo` and `@bug`. It can be used to create user-defined text sections which are automatically cross-referenced between the place of occurrence and a related page, which will be generated. On the related page all sections of the same type will be collected."],
        /*Commands to create links */
        ["addindex", "${1:text}", "This command adds `text` to the LATEX, DocBook and RTF index."],
        ["anchor", "${1:word}", "This command places an invisible, named anchor into the documentation to which you can refer with the `@ref` command."],
        ["cite", "${1:label}", "Adds a bibliographic reference in the text and in the list of bibliographic references. The `<label>` must be a valid BibTeX label that can be found in one of the `.bib` files listed in `CITE_BIB_FILES`. For the LATEX output the formatting of the reference in the text can be configured with `LATEX_BIB_STYLE`. For other output formats a fixed representation is used. Note that using this command requires the bibtex tool to be present in the search path."],
        ["endlink", "", "This command ends a link that is started with the `@link` command."],
        ["link", "${1:link-object} @endlink", "The links that are automatically generated by doxygen always have the name of the object they point to as link-text. The `@link` command can be used to create a link to an object (a file, class, or member) with a user specified link-text. The link command should end with an `@endlink` command. All text between the `@link` and `@endlink` commands serves as text for a link to the `<link-object>` specified as the first argument of `@link`."],
        ["ref", "${1:name} \"${2:text}\"", "Creates a reference to a named section, subsection, page or anchor. For HTML documentation the reference command will generate a link to the section. For a section or subsection the title of the section will be used as the text of the link. For an anchor the optional text between quotes will be used or `<name>` if no text is specified. For LATEX documentation the reference command will generate a section number for sections or the text followed by a page number if `<name>` refers to an anchor."],
        ["refitem", "${1:name}", "Just like the `@ref` command, this command creates a reference to a named section, but this reference appears in a list that is started by `@secreflist` and ends with `@endsecreflist`."],
        ["secreflist", "$1\n@endsecreflist", "Starts an index list of item, created with `@refitem` that each link to a named section."],
        ["endsecreflist", "", "End an index list started with `@secreflist`."],
        ["subpage", "${1:name} \"${2:text}\"", "This command can be used to create a hierarchy of pages."],
        ["tableofcontents", "{${1|HTML,LaTeX,XML,DocBook|}:${2:level}}", "Creates a table of contents at the top of a page, listing all sections and subsections in the page."],
        ["section", "${1:section-name} ${2:section title}", "Creates a section with name `<section-name>`."],
        ["subsection", "${1:subsection-name} ${2:subsection title}", "Creates a subsection with name `<subsection-name>`."],
        ["subsubsection", "${1:subsubsection-name} ${2:subsubsection title}", "Creates a subsubsection with name `<subsubsection-name>`."],
        ["paragraph", "${1:paragraph-name} ${2:paragraph title}", "Creates a named paragraph with name `<paragraph-name>`."],
        /*Commands for displaying examples */
        ["include", "{${1|lineno,doc|}} ${2:file-name}", "This command can be used to include a source file as a block of code. Using the `@include` command is equivalent to inserting the file into the documentation block and surrounding it with `@code` and `@endcode` commands."],
        ["line", "${1:pattern}", "This command searches line by line through the example that was last included using `@include` or `@dontinclude` until it finds a non-blank line. If that line contains the specified pattern, it is written to the output."],
        ["skip", "${1:pattern}", "This command searches line by line through the example that was last included using `@include` or `@dontinclude` until it finds a line that contains the specified pattern."],
        ["skipline", "${1:pattern}", "This command searches line by line through the example that was last included using `@include` or `@dontinclude` until it finds a line that contains the specified pattern. It then writes the line to the output."],
        ["snippet", "{${1|lineno,doc|}} ${2:file-name} ${3:block_id}", "Where the `@include` command can be used to include a complete file as source code, this command can be used to quote only a fragment of a source file. In case this is used as `<file-name>` the current file is taken as file to take the snippet from."],
        ["until", "${1:pattern}", "This command writes all lines of the example that was last included using `@include` or `@dontinclude` to the output, until it finds a line containing the specified pattern. The line containing the pattern will be written as well."],
        ["verbinclude", "${1:file-name}", "This command includes the contents of the file `<file-name>` verbatim in the documentation. The command is equivalent to pasting the contents of the file in the documentation and placing `@verbatim` and `@endverbatim` commands around it."],
        ["htmlinclude", "${1:[block]} ${2:file-name}", "This command includes the contents of the file `<file-name>` as is in the HTML documentation and tagged with `<htmlonly>` in the generated XML output. The command is equivalent to pasting the contents of the file in the documentation and placing `@htmlonly` and `@endhtmlonly` commands around it."],
        ["latexinclude", "${1:file-name}", "This command includes the contents of the file `<file-name>` as is in the LaTeX documentation and tagged with `<latexonly>` in the generated XML output. The command is equivalent to pasting the contents of the file in the documentation and placing `@latexonly` and `@endlatexonly` commands around it."],
        ["rtfinclude", "${1:file-name}", "This command includes the contents of the file `<file-name>` as is in the RTF documentation and tagged with `<rtfonly>` in the generated XML output. The command is equivalent to pasting the contents of the file in the documentation and placing `@rtfonly` and `@endrtfonly` commands around it."],
        ["maninclude", "${1:file-name}", "This command includes the contents of the file `<file-name>` as is in the MAN documentation and tagged with `<manonly>` in the generated XML output. The command is equivalent to pasting the contents of the file in the documentation and placing `@manonly` and `@endmanonly` commands around it."],
        ["docbookinclude", "${1:file-name}", "This command includes the contents of the file `<file-name>` as is in the DocBook documentation and tagged with `<docbookonly>` in the generated XML output. The command is equivalent to pasting the contents of the file in the documentation and placing `@docbookonly` and `@enddocbookonly` commands around it."],
        ["xmlinclude", "${1:file-name}", "This command includes the contents of the file `<file-name>` as is in the XML documentation. The command is equivalent to pasting the contents of the file in the documentation and placing `@xmlonly` and `@endxmlonly` commands around it."],
    ];
    public static completionItems = (() => {
        const items: vscode.CompletionItem[] = [];
        const addPrefix = vscode.workspace.getConfiguration("doxdocgen.generic").get<boolean>("commandSuggestionAddPrefix");
        for (const item of DoxygenCompletionItemProvider.commands) {
            // TODO: Only \ can be used, @ as label prefix causes unexpected filtering
            const newItem = new vscode.CompletionItem(addPrefix ? `\\${item[0]}` : item[0]);
            newItem.documentation = new vscode.MarkdownString(item[2]);
            newItem.insertText = new vscode.SnippetString(`${item[0]} ${item[1]}`);
            newItem.kind = vscode.CompletionItemKind.Snippet;
            items.push(newItem);
        }
        return items;
    })();

    public trigger: string = "";
    public indentSpace: number;
    public provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext) {
        if (inComment(vscode.window.activeTextEditor, position.line + 1)) {
            this.trigger = context.triggerCharacter;
            this.indentSpace = position.character;
            return DoxygenCompletionItemProvider.completionItems;
        }
        return [];
    }

    public resolveCompletionItem(item: vscode.CompletionItem, token: vscode.CancellationToken) {
        let insertion = (item.insertText as vscode.SnippetString).value;
        if (this.trigger === "\\") {
            insertion = insertion.replace("@", "\\");
        }
        const indentPrefix = "\n".concat("* ");
        insertion = insertion.replace(/\n/g, indentPrefix);

        /*insert an empty line if the snippet is multi-line, so * can be auto-completed */
        if (insertion.includes("\n")) {
            insertion = insertion.concat(indentPrefix);
        }

        const newItem = new vscode.CompletionItem(item.label, item.kind);
        newItem.documentation = item.documentation;
        newItem.insertText = new vscode.SnippetString(insertion);
        return newItem;
    }
}


================================================
FILE: src/GitConfig.ts
================================================
import simpleGit, { ConfigValues, SimpleGit } from "simple-git";
import { workspace } from "vscode";

export default class GitConfig {
    private gitConfig: ConfigValues;

    public constructor() {
        let git: SimpleGit;
        try {
            git = simpleGit(workspace.workspaceFolders?.[0].uri.fsPath);
        } catch (error) {
            git = simpleGit();
        }

        git.listConfig().then((result) => {
            this.gitConfig = result.all;
        });
    }

    /** git config --get user.name */
    get UserName(): string {
        try {
            return this.gitConfig["user.name"].toString();
        } catch (error) {
            return "";
        }
    }

    /** git config --get user.email */
    get UserEmail(): string {
        try {
            return this.gitConfig["user.email"].toString();
        } catch (error) {
            return "";
        }
    }
}


================================================
FILE: src/Lang/Cpp/CppArgument.ts
================================================
import { CppParseTree } from "./CppParseTree";

export class CppArgument {
    public name: string = null;
    public type: CppParseTree =  new CppParseTree();
}


================================================
FILE: src/Lang/Cpp/CppDocGen.ts
================================================
import * as moment from "moment";
import { Position, Range, Selection, TextEditor } from "vscode";
import { IDocGen } from "../../Common/IDocGen";
import { Config } from "../../Config";
import GitConfig from "../../GitConfig";
import * as templates from "../../templatedString";
import { getIndentation } from "../../util";
import { CppArgument } from "./CppArgument";
import * as CppParser from "./CppParser";
import { CppToken, CppTokenType } from "./CppToken";

export enum SpecialCase {
    none,
    constructor,
    destructor,
    getter,
    setter,
    factoryMethod,
}

export enum CommentType {
    method,
    file,
}

export enum CasingType {
    Pascal,
    camel,
    snake,
    SCREAMING_SNAKE,
    UPPER,
    uncertain,
}

export class CppDocGen implements IDocGen {
    protected activeEditor: TextEditor;

    protected readonly cfg: Config;

    protected func: CppArgument;
    protected templateParams: string[];
    protected params: CppArgument[];

    protected specialCase: SpecialCase;
    protected commentType: CommentType;
    protected casingType: CasingType;

    protected smartTextLength: number;

    protected vscodeAutoGeneratedComment: boolean;

    private gitConfig;

    /**
     * @param  {TextEditor} actEdit Active editor window
     * @param  {Position} cursorPosition Where the cursor of the user currently is
     * @param  {string[]} templateParams The template parameters of the declaration.
     * @param  {CppArgument} func The type and name of the function to generate doxygen.
     *                          Doesn't contain anything if it is not a function.
     * @param  {CppArgument[]} params The parameters of the function. Doesn't contain anything if it is not a function.
     * @param  {boolean} vscodeAutoGeneratedComment Set this to true if VS Code inserted an autogenerated comment closer
     *                                              on the next line after the comment.
     */
    public constructor(
        actEdit: TextEditor,
        cursorPosition: Position,
        cfg: Config,
        templateParams: string[],
        func: CppArgument,
        params: CppArgument[],
        specialCase: SpecialCase,
        commentType: CommentType,
        casingType: CasingType,
        vscodeAutoGeneratedComment: boolean,
    ) {
        this.activeEditor = actEdit;
        this.cfg = cfg;
        this.templateParams = templateParams;
        this.func = func;
        this.params = params;
        this.specialCase = specialCase;
        this.commentType = commentType;
        this.smartTextLength = 0;
        this.casingType = casingType;
        this.vscodeAutoGeneratedComment = vscodeAutoGeneratedComment;
    }

    /**
     * @inheritdoc
     */
    public GenerateDoc(rangeToReplace: Range, gitConfig: GitConfig) {
        let comment: string = "";
        this.gitConfig = gitConfig;
        if (this.commentType === CommentType.file) {
            comment = this.generateFileDescription();
        } else if (this.commentType === CommentType.method) {
            comment = this.generateComment();
        }

        // overwrite any autogenerated comment closer
        let modifiedRangeToReplace = rangeToReplace;
        if (this.vscodeAutoGeneratedComment) {
            const newPos: Position = new Position(
                modifiedRangeToReplace.end.line + 1,
                modifiedRangeToReplace.end.character,
            );
            modifiedRangeToReplace = new Range(rangeToReplace.start, newPos);
        }

        this.activeEditor.edit((editBuilder) => {
            editBuilder.replace(modifiedRangeToReplace, comment); // Insert the comment
        });

        // Set cursor to first DoxyGen command.
        this.moveCursurToFirstDoxyCommand(
            comment,
            modifiedRangeToReplace.start.line,
            modifiedRangeToReplace.start.character,
        );
    }

    /***************************************************************************
                                    Implementation
     ***************************************************************************/

    protected getSmartText(): string {
        if (!this.cfg.Generic.generateSmartText) {
            return "";
        }
        let val: string = "";
        let text: string = "";
        switch (this.specialCase) {
            case SpecialCase.constructor: {
                if (this.func.name === null) {
                    return "";
                } else {
                    const ctorText = this.func.name.trim();
                    this.casingType = CppParser.default.checkCasing(ctorText, 0);
                    val = this.splitCasing(ctorText).trim();
                    text = this.cfg.Cpp.ctorText;
                    break;
                }
            }
            case SpecialCase.destructor: {
                if (this.func.name === null) {
                    return "";
                } else {
                    const dtorText = this.func.name.replace("~", "").trim();
                    this.casingType = CppParser.default.checkCasing(dtorText, 0);
                    val = this.splitCasing(dtorText).trim();
                    text = this.cfg.Cpp.dtorText;
                    break;
                }
            }
            case SpecialCase.getter: {
                val = this.splitCasing(this.func.name.trim()).trim().substr(3).trim();
                text = this.cfg.C.getterText;
                break;
            }
            case SpecialCase.setter: {
                val = this.splitCasing(this.func.name.trim()).trim().substr(3).trim();
                text = this.cfg.C.setterText;
                break;
            }
            case SpecialCase.factoryMethod: {
                val = this.splitCasing(this.func.name.trim()).trim().substr(6).trim();
                text = this.cfg.C.factoryMethodText;
                break;
            }
            case SpecialCase.none:
            default: {
                return "";
            }
        }
        const str = templates.getTemplatedString(text, { toReplace: this.cfg.nameTemplateReplace, with: val });
        this.smartTextLength = str.length;
        return str;
    }

    protected generateBrief(lines: string[]) {
        lines.push(
            ...templates.getTemplatedString(
                this.cfg.Generic.briefTemplate,
                { toReplace: this.cfg.textTemplateReplace, with: this.getSmartText() },
            ).split("\n"),
        );
    }

    protected generateReturnParams(): string[] {

        const params: string[] = [];

        // Check if return type is a pointer
        const ptrReturnIndex = this.func.type.nodes
            .findIndex((n) => n instanceof CppToken && n.type === CppTokenType.Pointer);

        // Special case for void functions.
        const voidReturnIndex = this.func.type.nodes
            .findIndex((n) => n instanceof CppToken && n.type === CppTokenType.Symbol && n.value === "void");

        // Special case for bool return type.
        const boolReturnIndex: number = this.func.type.nodes
            .findIndex((n) => n instanceof CppToken && n.type === CppTokenType.Symbol && n.value === "bool");

        if (boolReturnIndex !== -1 && this.cfg.Generic.boolReturnsTrueFalse === true) {
            params.push("true");
            params.push("false");
        } else if (voidReturnIndex !== -1 && ptrReturnIndex !== -1) {
            params.push(this.cfg.Generic.includeTypeAtReturn === true ? this.func.type.Yield() : "");
        } else if (voidReturnIndex === -1 && this.func.type.nodes.length > 0) {
            params.push(this.cfg.Generic.includeTypeAtReturn === true ? this.func.type.Yield() : "");
        } else {
            if (this.cfg.Generic.includeTypeAtReturn === false) {
                return [];
            }
        }
        if (this.cfg.Generic.includeTypeAtReturn === false) {
            return [""];
        }

        return params;
    }

    protected generateAuthorTag(lines: string[]) {
        if (this.cfg.Generic.authorTag.trim().length !== 0) {
            const authorInfo = this.getAuthorInfo();
            // Allow substitution of {author} and {email} only
            lines.push(
                ...templates.getMultiTemplatedString(
                    this.cfg.Generic.authorTag,
                    [
                        { toReplace: this.cfg.authorTemplateReplace, with: authorInfo.authorName },
                        { toReplace: this.cfg.emailTemplateReplace, with: authorInfo.authorEmail },
                    ],
                ).split("\n"),
            );
        }
    }

    protected generateFilenameFromTemplate(lines: string[]) {
        if (this.cfg.File.fileTemplate.trim().length !== 0) {
            templates.generateFromTemplate(
                lines,
                this.cfg.nameTemplateReplace,
                this.cfg.File.fileTemplate,
                [this.activeEditor.document.fileName.replace(/^.*[\\\/]/, "")],
            );
        }
    }

    protected generateVersionTag(lines: string[]) {
        if (this.cfg.File.versionTag.trim().length !== 0) {
            lines.push(...templates.getIndentedTemplate(this.cfg.File.versionTag).split("\n"));
        }
    }

    protected generateCopyrightTag(lines: string[]) {
        // This currently only supports year substitution
        this.cfg.File.copyrightTag.forEach((element) => {
            templates.generateFromTemplate(
                lines,
                this.cfg.yearTemplateReplace,
                element,
                [moment().format("YYYY")],
            );
        });
    }

    /**
     * Generate those tags shared between File comment and function comment
     */
    protected generateCommonTag(lines: string[], tags: string) {
        switch (tags) {
            case "brief": {
                this.insertBrief(lines);
                break;
            }
            case "empty": {
                lines.push("");
                break;
            }
            case "version": {
                this.generateVersionTag(lines);
                break;
            }
            case "author": {
                this.generateAuthorTag(lines);
                break;
            }
            case "date": {
                this.generateDateFromTemplate(lines);
                break;
            }
            case "copyright": {
                this.generateCopyrightTag(lines);
                break;
            }
            default: {
                break;
            }
        }
    }

    protected generateCustomTag(lines: string[], target = CommentType.file) {
        let dateFormat: string = "YYYY-MM-DD"; // Default to ISO standard if not defined
        if ( this.cfg.Generic.dateFormat.trim().length !== 0) {
            dateFormat = this.cfg.Generic.dateFormat; // Overwrite with user format
        }

        // Have to check this setting, otherwise {author} and {email} will get incorrect result
        // if useGitUserName and useGitUserEmail is used
        const authorInfo = this.getAuthorInfo();

        const targetTagArray = target === CommentType.file ? this.cfg.File.customTag : this.cfg.Generic.customTags;
        // For each line of the customTag
        targetTagArray.forEach((element) => {
            if (element !== "custom") { // Prevent recursive expansion
                // Allow any of date, year, author, email to be replaced
                lines.push(
                    ...templates.getMultiTemplatedString(
                        element,
                        [
                            { toReplace: this.cfg.authorTemplateReplace, with: authorInfo.authorName },
                            { toReplace: this.cfg.emailTemplateReplace, with: authorInfo.authorEmail },
                            { toReplace: this.cfg.dateTemplateReplace, with: moment().format(dateFormat) },
                            { toReplace: this.cfg.yearTemplateReplace, with: moment().format("YYYY") },
                            { toReplace: "{file}", with: this.activeEditor.document.fileName.replace(/^.*[\\\/]/, "")},
                        ],
                    ).split("\n"),
                );
            }
        });
    }

    protected generateDateFromTemplate(lines: string[]) {
        if (this.cfg.Generic.dateTemplate.trim().length !== 0 &&
            this.cfg.Generic.dateFormat.trim().length !== 0) {
            templates.generateFromTemplate(
                lines,
                this.cfg.dateTemplateReplace,
                this.cfg.Generic.dateTemplate,
                [moment().format(this.cfg.Generic.dateFormat)],
            );
        }
    }

    protected insertFirstLine(lines: string[]) {
        if (this.cfg.C.firstLine.trim().length !== 0) {
            lines.unshift(this.cfg.C.firstLine);
        }
    }

    protected insertBrief(lines: string[]) {
        if (this.cfg.Generic.briefTemplate.trim().length !== 0) {
            this.generateBrief(lines);
        }
    }

    protected insertLastLine(lines: string[]) {
        if (this.cfg.C.lastLine.trim().length !== 0) {
            lines.push(this.cfg.C.lastLine);
        }
    }

    protected generateFileDescription(): string {
        let lines: string[] = [];

        this.cfg.File.fileOrder.forEach((element) => {
            switch (element) {
                case "file": {
                    this.generateFilenameFromTemplate(lines);
                    break;
                }
                case "custom": {
                    this.generateCustomTag(lines, CommentType.file);
                    break;
                }
                default: {
                    this.generateCommonTag(lines, element);
                }
            }
        });

        lines = lines.map((line) => `${this.cfg.C.commentPrefix}${line}`);
        this.insertFirstLine(lines);
        this.insertLastLine(lines);

        return lines.join("\n");
    }

    protected generateComment(): string {
        let lines: string[] = [];

        this.cfg.Generic.order.forEach((element) => {
            switch (element) {
                case "tparam": {
                    if (this.cfg.Cpp.tparamTemplate.trim().length !== 0 && this.templateParams.length > 0) {
                        templates.generateFromTemplate(
                            lines,
                            this.cfg.paramTemplateReplace,
                            this.cfg.Cpp.tparamTemplate,
                            this.templateParams,
                        );
                    }
                    break;
                }
                case "param": {
                    if (this.cfg.Generic.paramTemplate.trim().length !== 0 && this.params.length > 0) {
                        const paramNames: string[] = this.params.map((p) => p.name);
                        templates.generateFromTemplate(
                            lines,
                            this.cfg.paramTemplateReplace,
                            this.cfg.Generic.paramTemplate,
                            paramNames,
                        );
                    }
                    break;
                }
                case "return": {
                    if (this.cfg.Generic.returnTemplate.trim().length !== 0 && this.func.type !== null) {
                        const returnParams = this.generateReturnParams();
                        templates.generateFromTemplate(
                            lines,
                            this.cfg.typeTemplateReplace,
                            this.cfg.Generic.returnTemplate,
                            returnParams,
                        );
                    }
                    break;
                }
                case "custom": {
                    this.generateCustomTag(lines, CommentType.method);
                    break;
                }
                default: {
                    this.generateCommonTag(lines, element);
                }
            }
        });

        lines = lines.map((line) => `${this.cfg.C.commentPrefix}${line}`);
        this.insertFirstLine(lines);
        this.insertLastLine(lines);

        return lines.join(`\n${getIndentation(this.activeEditor)}`);
    }

    protected moveCursurToFirstDoxyCommand(comment: string, baseLine: number, baseCharacter) {
        // Find first offset of a new line in the comment. Since that's when the line where the first param starts.
        let line: number = baseLine;
        let character: number = comment.indexOf("\n");

        // If a first line is included find the 2nd line with a newline.
        if (this.cfg.C.firstLine.trim().length !== 0) {
            line++;
            const oldCharacter: number = character;
            character = comment.indexOf("\n", oldCharacter + 1) - oldCharacter;
        }

        // If newline is not found means no first param was found so Set to base line before the newline.
        if (character < 0) {
            line = baseLine;
            character = baseCharacter;
        }
        const to: Position = new Position(line, character);
        this.activeEditor.selection = new Selection(to, to);
    }

    protected splitCasing(text: string): string {
        if (!this.cfg.Generic.splitCasingSmartText) {
            return text;
        }
        let txt = text;
        let vals: string[] = [];
        switch (this.casingType) {
            case CasingType.SCREAMING_SNAKE: {
                txt = txt.toLowerCase();
            }
            case CasingType.snake: {
                vals = txt.split("_");
                break;
            }
            case CasingType.Pascal: {
                txt = txt.replace(/([A-Z0-9])/g, " $1");
                vals.push(txt);
                break;
            }
            case CasingType.camel: {
                txt = txt.replace(/([a-zA-Z0-9])(?=[A-Z])/g, "$1 ");
                vals.push(txt);
                break;
            }
            case CasingType.UPPER:
            case CasingType.uncertain:
            default: {
                return text;
            }
        }

        return vals.join(" ");
    }

    /**
     * Get author info, possibly using info from git config
     */
    private getAuthorInfo() {
        let authorName: string = this.cfg.Generic.authorName;
        let authorEmail: string = this.cfg.Generic.authorEmail;

        // Check if set to use the git username
        if (this.cfg.Generic.useGitUserName === true) {
            authorName = this.gitConfig.UserName;
        }

        // Check if set to use the git email
        if (this.cfg.Generic.useGitUserEmail === true) {
            authorEmail = this.gitConfig.UserEmail;
        }
        return {
            authorEmail,
            authorName,
        };
    }
}


================================================
FILE: src/Lang/Cpp/CppParseTree.ts
================================================
import { CppToken, CppTokenType } from "./CppToken";

export class CppParseTree {

    /**
     * Create a tree from CppTokens. This consumes the CppTokens.
     * @param CppTokens The CppTokens to create a tree for.
     * @param inNested If currently allready nesting.
     */
    public static CreateTree(CppTokens: CppToken[], inNested: boolean = false): CppParseTree {
        const tree: CppParseTree = new CppParseTree();

        while (CppTokens.length > 0) {
            const token: CppToken = CppTokens.shift();
            switch (token.type) {
                case CppTokenType.OpenParenthesis:
                    tree.nodes.push(this.CreateTree(CppTokens, true));
                    break;
                case CppTokenType.CloseParenthesis:
                    if (inNested === false) {
                        throw new Error("Unmatched closing parenthesis.");
                    }
                    return tree;
                default:
                    tree.nodes.push(token);
                    break;
            }
        }

        if (inNested === true) {
            throw new Error("No match found for an opening parenthesis.");
        }

        return tree;
    }

    public nodes: Array<CppToken | CppParseTree> = [];

    /**
     * Compact empty branches. Example ((foo))(((bar))) will become (foo)(bar)
     * @param tree The CppParseTree to compact. Defaults to the current tree.
     */
    public Compact(tree: CppParseTree = this): CppParseTree {
        const newTree: CppParseTree = new CppParseTree();
        newTree.nodes = tree.nodes.map((n) => n);
        const isNotCompact = (n) => {
            return n instanceof CppParseTree
                && n.nodes.length === 1 && n.nodes[0] instanceof CppParseTree;
        };

        // Compact current level of nodes to the maximum amount.
        while (newTree.nodes.some((n) => isNotCompact(n))) {
            newTree.nodes = newTree.nodes
                .map((n) =>  n instanceof CppParseTree && isNotCompact(n) ? n.nodes[0] : n);
        }

        // Compact all nested CppParseTrees.
        newTree.nodes = newTree.nodes
            .map((n) => n instanceof CppParseTree ? this.Compact(n) : n);

        return newTree;
    }

    /**
     * Copy CppParseTree.
     * @param tree The CppParseTree to compact. Defaults to the current tree.
     */
    public Copy(tree: CppParseTree = this): CppParseTree {
        const newTree: CppParseTree = new CppParseTree();
        newTree.nodes = tree.nodes
            .map((n) => n instanceof CppToken ? n : this.Copy(n));
        return newTree;
    }

    /**
     * Create string from the CppParseTree which is a representation of the original code.
     * @param tree The CppParseTree to compact. Defaults to the current tree.
     */
    public Yield(tree: CppParseTree = this): string {
        let code: string = "";

        for (const node of tree.nodes) {
            if (node instanceof CppParseTree) {
                code += "(" + this.Yield(node) + ")";
                continue;
            }

            switch (node.type) {
                case CppTokenType.Symbol:
                    code += code === "" ? node.value : " " + node.value;
                    break;
                case CppTokenType.Pointer:
                    code += node.value;
                    break;
                case CppTokenType.Reference:
                    code += node.value;
                    break;
                case CppTokenType.ArraySubscript:
                    code += node.value;
                    break;
                case CppTokenType.CurlyBlock:
                    code += node.value;
                    break;
                case CppTokenType.Assignment:
                    code += " " + node.value;
                    break;
                case CppTokenType.Comma:
                    code += node.value;
                    break;
                case CppTokenType.Arrow:
                    code += " " + node.value;
                    break;
                case CppTokenType.Ellipsis:
                    code += node.value;
                    break;
                case CppTokenType.Attribute:
                    code += code === "" ? node.value : " " + node.value;
                    break;
            }
        }

        return code;
    }
}


================================================
FILE: src/Lang/Cpp/CppParser.ts
================================================
import { Position, TextDocumentContentChangeEvent, TextEditor, TextLine, workspace } from "vscode";
import ICodeParser from "../../Common/ICodeParser";
import { IDocGen } from "../../Common/IDocGen";
import { Config } from "../../Config";
import { CppArgument } from "./CppArgument";
import { CasingType, CommentType, CppDocGen, SpecialCase } from "./CppDocGen";
import { CppParseTree } from "./CppParseTree";
import { CppToken, CppTokenType } from "./CppToken";

/**
 *
 * Parses C code for methods and signatures
 *
 * @export
 * @class CParser
 * @implements {ICodeParser}
 */
export default class CppParser implements ICodeParser {
    /**
     * Get the casing of a specified text
     *
     * @private
     * @param {string} name Text to check
     * @param {number} validateFrom Check if only a substr is the same casing as the whole string.
     *                              Set to 0 to disable check.
     * @returns {CasingType} Detected type of casing
     *
     * @memberOf CppParser
     */
    public static checkCasing(name: string, validateFrom: number): CasingType {
        let containsUnderscores = name.indexOf("_") !== -1;
        if (containsUnderscores) {
            containsUnderscores = name.indexOf("_") !== name.length - 1; // last character _ may be Google style
        }
        // first letter upper case
        let methodCasing: CasingType;
        let match = name.match("^([_|\\d|]*[A-Z]).+");
        if (match !== null) {
            match = name.match("^([A-Z|_|\\d]{2,})");
            if (match !== null) {
                methodCasing = containsUnderscores ? CasingType.SCREAMING_SNAKE : CasingType.UPPER;
            } else {
                methodCasing = containsUnderscores ? CasingType.uncertain : CasingType.Pascal;
            }
        } else {
            methodCasing = containsUnderscores ? CasingType.snake : CasingType.camel;
        }

        if (validateFrom > 0 && methodCasing !== CasingType.uncertain) {
            // validate after
            switch (methodCasing) {
                case CasingType.SCREAMING_SNAKE: {
                    // Take the leading _ after removing the characters into consideration
                    const testCasing = this.checkCasing(name.substr(validateFrom + 1), 0);
                    // screaming or upper
                    if (testCasing !== CasingType.SCREAMING_SNAKE && testCasing !== CasingType.UPPER) {
                        methodCasing = CasingType.uncertain;
                    }
                    break;
                }
                case CasingType.snake: {
                    // Take the leading _ after removing the characters into consideration
                    const textCheck = name.substr(validateFrom + 1);
                    const testCasing = this.checkCasing(textCheck, 0);
                    // snake
                    if (testCasing !== CasingType.snake) {
                        if (textCheck.match("([a-z\\d]+)") === null) {
                            methodCasing = CasingType.uncertain;
                        }
                    }
                    break;
                }
                case CasingType.Pascal:
                case CasingType.camel: {
                    const testCasing = this.checkCasing(name.substr(validateFrom), 0);
                    // pascal
                    if (testCasing !== CasingType.Pascal) {
                        methodCasing = CasingType.uncertain;
                    }
                    break;
                }
                case CasingType.UPPER: {
                    const testCasing = this.checkCasing(name.substr(validateFrom), 0);
                    // upper
                    if (name.substr(validateFrom).match("([A-Z\\d]+)") === null) {
                        methodCasing = CasingType.uncertain;
                    }
                    break;
                }
                default: {
                    break; // No op
                }
            }
        }

        return methodCasing;
    }

    protected activeEditor: TextEditor;
    protected activeSelection: Position;
    protected readonly cfg: Config;

    private typeKeywords: string[];
    private stripKeywords: string[];
    private keywords: string[];
    private attributes: string[];
    private lexerVocabulary;

    private specialCase: SpecialCase;
    private commentType: CommentType;

    private casingType: CasingType;

    private vscodeAutoGeneratedComment: boolean;

    constructor(cfg: Config) {
        this.cfg = cfg;

        this.typeKeywords = [
            "constexpr",
            "const",
            "struct",
            "enum",
            "restrict",
        ];

        this.stripKeywords = [
            "final",
            "static",
            "inline",
            "friend",
            "virtual",
            "extern",
            "explicit",
            "class",
            "override",
            "typename",
        ];

        this.attributes = [
            "noexcept",
            "throw",
            "alignas",
        ];

        // Non type keywords will be stripped from the final return type.
        this.keywords = this.typeKeywords.concat(this.stripKeywords);
        // Remove keywords that should be filtered
        this.keywords = this.keywords.concat(this.cfg.Generic.filteredKeywords);

        this.lexerVocabulary = {
            ArraySubscript: (x: string): string => (x.match("^\\[[^\\[]*?\\]") || [])[0],
            Arrow: (x: string): string => (x.match("^->") || [])[0],
            Assignment: (x: string): string => {
                if (!x.match("^=")) {
                    return undefined;
                }

                const nesters: Map<string, string> = new Map<string, string>([
                    ["<", ">"], ["(", ")"], ["{", "}"], ["[", "]"],
                ]);

                for (let i = 0; i < x.length; i++) {
                    const v = nesters.get(x[i]);
                    if (v !== undefined) {
                        const startEndOffset: number[] = this.GetSubExprStartEnd(x, i, x[i], v);
                        if (startEndOffset[1] === 0) {
                            return undefined;
                        }
                        i = startEndOffset[1] - 1;
                    } else if (x[i] === "\"" || x[i] === "'") {
                        // Check if raw literal. Since those may have unescaped characters
                        // but require ()
                        if (x[i - 1] !== "R") {
                            // Skip to next end of the string or char literal.
                            let found: boolean = false;
                            for (let j = i + 1; j < x.length; j++) {
                                if (x[j] === x[i] && x[j - 1] !== "\\") {
                                    found = true;
                                    i = j;
                                    break;
                                }
                            }
                            if (!found) {
                                return undefined;
                            }
                        } else {
                            const startEndOffset: number[] = this.GetSubExprStartEnd(x, i, "(", ")");
                            if (startEndOffset[1] === 0) {
                                return undefined;
                            }
                            i = startEndOffset[1];
                        }
                    } else if (x[i] === "," || x[i] === ")") {
                        return x.slice(0, i);
                    }
                }

                return x;
            },
            Attribute: (x: string): string => {
                const attribute: string = (x.match("^\\[\\[[^\\[]*?\\]\\]") || [])[0];
                if (attribute !== undefined) {
                    return attribute;
                }

                const foundIndex: number = this.attributes
                    .findIndex((n: string) => x.startsWith(n) === true);

                if (foundIndex === -1) {
                    return undefined;
                }

                if (x.slice(this.attributes[foundIndex].length).trim().startsWith("(") === false) {
                    return x.slice(0, this.attributes[foundIndex].length);
                }

                const startEndOffset: number[] = this.GetSubExprStartEnd(x, 0, "(", ")");
                return startEndOffset[1] === 0 ? undefined : x.slice(0, startEndOffset[1]);
            },
            CloseParenthesis: (x: string): string => (x.match("^\\)") || [])[0],
            Comma: (x: string): string => (x.match("^,") || [])[0],
            CommentBlock: (x: string): string => {
                if (x.startsWith("/*") === false) {
                    return undefined;
                }

                let closeOffset: number = x.indexOf("*/");
                closeOffset = closeOffset === -1 ? x.length : closeOffset + 2;
                return x.slice(0, closeOffset);
            },
            CommentLine: (x: string): string => {
                if (x.startsWith("//") === false) {
                    return undefined;
                }

                let closeOffset: number = x.indexOf("\n");
                closeOffset = closeOffset === -1 ? x.length : closeOffset + 1;
                return x.slice(0, closeOffset);
            },
            CurlyBlock: (x: string): string => {
                if (x.startsWith("{") === false) {
                    return undefined;
                }
                const startEndOffset: number[] = this.GetSubExprStartEnd(x, 0, "{", "}");
                return startEndOffset[1] === 0 ? undefined : x.slice(0, startEndOffset[1]);
            },
            Ellipsis: (x: string): string => (x.match("^\\.\\.\\.") || [])[0],
            OpenParenthesis: (x: string): string => (x.match("^\\(") || [])[0],
            Pointer: (x: string): string => (x.match("^\\*") || [])[0],
            Reference: (x: string): string => (x.match("^&") || [])[0],
            Symbol: (x: string): string => {
                // Handle specifiers
                const specifierFound: number = this.attributes
                    .findIndex((n: string) => x.startsWith(n) === true);

                if (specifierFound !== -1) {
                    return undefined;
                }

                // Handle decltype special cases.
                if (x.startsWith("decltype") === true) {
                    const startEndOffset: number[] = this.GetSubExprStartEnd(x, 0, "(", ")");
                    return startEndOffset[1] === 0 ? undefined : x.slice(0, startEndOffset[1]);
                }

                // Special case group up the fundamental types with the modifiers.
                // tslint:disable-next-line:max-line-length
                let reMatch: string = (x.match("^(unsigned|signed|short|long|int|char|double|float)(\\s*(unsigned|signed|short|long|int|char|double|float)\\s)+(?!a-z|A-Z|:|_|\\d)") || [])[0];
                if (reMatch !== undefined) {
                    return reMatch.trim();
                }

                // Regex to handle a part of all symbols and includes all symbol special cases.
                // This is run in a loop because template parts of a symbol can't be parsed using regex.
                // Also check if it doesn't start with a number since those are always literals
                // tslint:disable-next-line:max-line-length
                const symbolRegex: string = "^([a-z|A-Z|:|_|~|\\d]*operator\\s*(\"\"_[a-z|A-Z|_|\\d]+|>>=|<<=|->\\*|\\+=|-=|\\*=|\\/=|%=|\\^=|&=|\\|=|<<|>>|==|!=|<=|->|>=|&&|\\|\\||\\+\\+|--|\\+|-|\\*|\\/|%|\\^|&|\||~|!|=|<|>|,|\\[\\s*\\]|\\(\\s*\\)|(new|delete)\\s*(\\[\\s*\\]){0,1}){0,1}|[a-z|A-Z|:|_|~|\\d]+)";

                reMatch = (x.match(symbolRegex) || [])[0];
                if (reMatch === undefined || x.match(/^\d/)) {
                    return undefined;
                }

                let symbol: string = reMatch;
                while (true) {
                    if (x.slice(symbol.length).trim().startsWith("<") === true) {
                        const offsets: number[] = this.GetSubExprStartEnd(x, symbol.length, "<", ">");
                        if (offsets[1] === 0) {
                            return undefined;
                        }
                        symbol = x.slice(0, offsets[1]);
                    }

                    reMatch = (x.slice(symbol.length).match(symbolRegex) || [])[0];
                    if (reMatch === undefined) {
                        break;
                    }

                    symbol += reMatch;
                }

                return symbol.replace(/\s+$/, "");
            },
        };

        this.specialCase = SpecialCase.none;
        this.commentType = CommentType.method;
        this.vscodeAutoGeneratedComment = false;
    }

    /**
     * @inheritdoc
     */
    public Parse(activeEdit: TextEditor): IDocGen {
        this.activeEditor = activeEdit;
        this.activeSelection = this.activeEditor.selection.active;

        let line: string = "";
        try {
            line = this.getLogicalLine();
        } catch (err) {
            // console.dir(err);
        }
        const templateArgs: string[] = [];
        let args: [CppArgument, CppArgument[]] = [new CppArgument(), []];

        if (activeEdit.selection.active.line === 0 && line.length === 0) { // head of file
            this.commentType = CommentType.file;
        } else { // method
            // template parsing is simpler by using heuristics rather then CppTokenizing first.
            while (line.startsWith("template")) {
                const template: string = this.GetTemplate(line);

                templateArgs.push.apply(templateArgs, this.GetArgsFromTemplate(template));

                line = line.slice(template.length, line.length + 1).trim();
            }

            try {
                args = this.GetReturnAndArgs(line);
            } catch (err) {
                // console.dir(err);
            }
        }

        if (args[0].name !== null) {
            const methodName = args[0].name;

            if (methodName.toLowerCase().startsWith("get")) {
                this.casingType = CppParser.checkCasing(methodName, 3);
                if (this.casingType !== CasingType.uncertain) {
                    this.specialCase = SpecialCase.getter;
                }
            } else if (methodName.toLowerCase().startsWith("set")) {
                this.casingType = CppParser.checkCasing(methodName, 3);
                if (this.casingType !== CasingType.uncertain) {
                    this.specialCase = SpecialCase.setter;
                }
            } else if (methodName.toLowerCase().startsWith("create")) {
                this.casingType = CppParser.checkCasing(methodName, 6);
                if (this.casingType !== CasingType.uncertain) {
                    this.specialCase = SpecialCase.factoryMethod;
                }
            }
        }

        return new CppDocGen(
            this.activeEditor,
            this.activeSelection,
            this.cfg,
            templateArgs,
            args[0],
            args[1],
            this.specialCase,
            this.commentType,
            this.casingType,
            this.vscodeAutoGeneratedComment,
        );
    }

    /***************************************************************************
                                    Implementation
     ***************************************************************************/
    private getLogicalLine(): string {
        let logicalLine: string = "";

        let nextLine: Position = new Position(this.activeSelection.line + 1, this.activeSelection.character);

        let nextLineTxt: string = this.activeEditor.document.lineAt(nextLine.line).text.trim();

        // VSCode may enter a * on itself, we don't want that in our method
        if (nextLineTxt === "*") {
            nextLineTxt = "";
        }

        let currentNest: number = 0;
        logicalLine = nextLineTxt;

        // Get method end line
        let linesToGet: number = this.cfg.Generic.linesToGet;
        while (linesToGet-- > 0) { // Check for end of expression.
            nextLine = new Position(nextLine.line + 1, nextLine.character);
            nextLineTxt = this.activeEditor.document.lineAt(nextLine.line).text.trim();
            let finalSlice = -1;

            // Check if method has finished if curly brace is opened while
            // nesting is occuring.
            for (let i: number = 0; i < nextLineTxt.length; i++) {
                if (nextLineTxt[i] === "(") {
                    currentNest++;
                } else if (nextLineTxt[i] === ")") {
                    currentNest--;
                } else if (nextLineTxt[i] === "{" && currentNest === 0) {
                    finalSlice = i;
                    break;
                } else if ((nextLineTxt[i] === ";"
                    || (nextLineTxt[i] === ":" && nextLineTxt[i - 1] !== ":" && nextLineTxt[i + 1] !== ":"))
                    && currentNest === 0) {
                    finalSlice = i;
                    break;
                }
            }

            // Head of file probably
            if (nextLineTxt.startsWith("#include")) {
                this.commentType = CommentType.file;
                return "";
            } else if (nextLineTxt.startsWith("#") && ! nextLineTxt.startsWith("#define")) {
                return "";
            } else if (nextLine.line === 2) { // Check if there where two empty lines trailing the file
                if (this.activeEditor.document.lineAt(0).text === this.cfg.C.firstLine
                    && (
                        this.activeEditor.document.lineAt(1).text === this.cfg.C.commentPrefix
                        || this.activeEditor.document.lineAt(1).text.trim() === ""
                     ) && this.activeEditor.document.lineAt(2).text.trim() === "") {
                        this.commentType = CommentType.file;
                        return "";
                }
            }

            if (this.isVsCodeAutoComplete(nextLineTxt) === true) {
                // Can only be true if it's in the next line
                this.vscodeAutoGeneratedComment = linesToGet === (this.cfg.Generic.linesToGet - 1);
            } else {
                logicalLine += "\n";
                if (finalSlice >= 0) {
                    logicalLine += nextLineTxt.slice(0, finalSlice);
                } else {
                    logicalLine += nextLineTxt;
                }
                logicalLine = logicalLine.replace(/\*\//g, "");
            }

            if (finalSlice >= 0) {
                return logicalLine.replace(/^\s+|\s+$/g, "");
            }
        }

        throw new Error("More than " + linesToGet + " lines were read from editor and no end of expression was found.");
    }

    private Tokenize(expression: string): CppToken[] {
        const CppTokens: CppToken[] = [];
        expression = expression.replace(/^\s+|\s+$/g, "");

        while (expression.length !== 0) {
            const matches: CppToken[] = Object.keys(this.lexerVocabulary)
                .map((k): CppToken => new CppToken(CppTokenType[k], this.lexerVocabulary[k](expression)))
                .filter((t) => t.value !== undefined);

            if (matches.length === 0) {
                throw new Error("Next CppToken couldn\'t be determined: " + expression);
            } else if (matches.length > 1) {
                throw new Error("Multiple matches for next CppToken: " + expression);
            }

            CppTokens.push(matches[0]);
            expression = expression.slice(matches[0].value.length, expression.length).replace(/^\s+|\s+$/g, "");
        }

        return CppTokens;
    }

    private GetReturnAndArgs(line: string): [CppArgument, CppArgument[]] {
        if (this.GetArgumentFromCastOperator(line) !== null) {
            const opFunc = new CppArgument();
            opFunc.name = this.GetArgumentFromCastOperator(line)[1].trim();
            opFunc.type.nodes.push(new CppToken(CppTokenType.Symbol, opFunc.name));
            return [opFunc, []];
        }
        // CppTokenize rest of expression and remove comment CppTokens;
        const CppTokens: CppToken[] = this.Tokenize(line)
            .filter((t) => t.type !== CppTokenType.CommentBlock)
            .filter((t) => t.type !== CppTokenType.CommentLine);

        // Create hierarchical tree based on the parenthesis.
        const tree: CppParseTree = CppParseTree.CreateTree(CppTokens).Compact();

        // return argument.
        const func = this.GetArgument(tree);
        // check if it is a constructor or descructor since these have no name.
        // Also reverse the assignment of type and name.
        if (func.name === null) {
            if (func.type.nodes.length !== 1) {
                throw new Error("Too many symbols found for constructor/descructor.");
            } else if (func.type.nodes[0] instanceof CppParseTree) {
                throw new Error("One node found with just a CppParseTree. Malformed input.");
            }

            if (line.includes("~")) {
                this.specialCase = SpecialCase.destructor;
            } else {
                this.specialCase = SpecialCase.constructor;
            }

            func.name = (func.type.nodes[0] as CppToken).value;
            func.type.nodes = [];
        }

        // Get arguments list as a CppParseTree and create arguments from them.
        const params = this.GetArgumentList(tree)
            .map((a) => this.GetArgument(a));

        return [func, params];
    }

    private RemoveUnusedTokens(tree: CppParseTree): CppParseTree {
        tree = tree.Copy();

        // First slice of everything after assignment since that will not be used.
        const assignmentIndex = tree.nodes
            .findIndex((n) => n instanceof CppToken && n.type === CppTokenType.Assignment);
        if (assignmentIndex !== -1) {
            tree.nodes = tree.nodes.slice(0, assignmentIndex);
        }

        // Specifiers aren't needed so remove them.
        tree.nodes = tree.nodes
            .filter((n) => n instanceof CppParseTree || (n instanceof CppToken && n.type !== CppTokenType.Attribute));

        return tree;
    }

    private GetArgumentList(tree: CppParseTree): CppParseTree[] {
        const args: CppParseTree[] = [];

        tree = this.RemoveUnusedTokens(tree);

        let cursor: CppParseTree = tree;
        while (this.IsFuncPtr(cursor.nodes) === true) {
            cursor = cursor.nodes.find((n) => n instanceof CppParseTree) as CppParseTree;
        }

        const argTree: CppParseTree = cursor.nodes.find((n) => n instanceof CppParseTree) as CppParseTree;
        if (argTree === undefined) {
            throw new Error("Function arguments not found.");
        }

        // Split the argument tree on commas
        let arg: CppParseTree = new CppParseTree();
        for (const node of argTree.nodes) {
            if (node instanceof CppToken && node.type === CppTokenType.Comma) {
                args.push(arg);
                arg = new CppParseTree();
            } else {
                arg.nodes.push(node);
            }
        }

        if (arg.nodes.length > 0) {
            args.push(arg);
        }

        return args;
    }

    private IsFuncPtr(nodes: Array<CppToken | CppParseTree>) {
        return nodes.filter((n) => n instanceof CppParseTree).length === 2;
    }

    private IsArrayPtr(nodes: Array<CppToken | CppParseTree>) {
        if (nodes.filter((n) => n instanceof CppParseTree).length === 1) {
            const treeIdx = nodes.findIndex((n) => n instanceof CppParseTree);
            if (treeIdx !== -1) {
                const nextElem = nodes[treeIdx + 1];
                if (nextElem instanceof CppToken) {
                    const match = nextElem.value.match(/^\[[0-9]*\]$/g);
                    if (match !== undefined && match !== null) {
                        return match.length > 0;
                    }
                }
            }
        }
        return false;
    }

    private StripNonTypeNodes(tree: CppParseTree) {
        tree.nodes = tree.nodes
            // All strippable keywords.
            .filter((n) => {
                return !(n instanceof CppToken
                    && n.type === CppTokenType.Symbol
                    && this.stripKeywords.find((k) => k === n.value) !== undefined);
            });
    }

    private GetArgumentFromCastOperator(line: string) {
        const copy = line;
        return copy.match("[explicit|\\s]*\\s*operator\\s*([a-zA-Z].*)\\(\\).*");
    }

    private GetArgumentFromTrailingReturn(tree: CppParseTree, startTrailingReturn: number): CppArgument {
        const argument: CppArgument = new CppArgument();

        // Find index of auto prior to the first CppParseTree.
        // If auto is not found something is going wrong since trailing return
        // requires auto.
        let autoIndex: number = -1;
        for (let i: number = 0; i < tree.nodes.length; i++) {
            const node = tree.nodes[i];
            if (node instanceof CppParseTree) {
                break;
            }
            if (node.type === CppTokenType.Symbol && node.value === "auto") {
                autoIndex = i;
                break;
            }
        }

        if (autoIndex === -1) {
            throw new Error("Function declaration has trailing return but type is not auto.");
        }

        // Get symbol between auto and CppParseTree which is the argument name. It also may not be a keyword.
        for (let i: number = autoIndex + 1; i < tree.nodes.length; i++) {
            const node = tree.nodes[i];
            if (node instanceof CppParseTree) {
                break;
            }
            if (node.type === CppTokenType.Symbol && this.keywords.find((k) => k === node.value) === undefined) {
                argument.name = node.value;
                break;
            }
        }

        argument.type.nodes = tree.nodes.slice(startTrailingReturn + 1, tree.nodes.length);
        this.StripNonTypeNodes(argument.type);

        return argument;
    }

    private GetArgumentFromFuncPtr(tree: CppParseTree): CppArgument {
        const argument: CppArgument = new CppArgument();

        argument.type = tree;

        let cursor: CppParseTree = tree;

        while (this.IsFuncPtr(cursor.nodes) === true || this.IsArrayPtr(cursor.nodes) === true) {
            cursor = cursor.nodes.find((n) => n instanceof CppParseTree) as CppParseTree;
        }

        // Remove CppParseTree. This can be if it is a function declaration.
        const argumentsIndex = cursor.nodes.findIndex((n) => n instanceof CppParseTree);
        if (argumentsIndex !== -1) {
            cursor.nodes.splice(argumentsIndex, 1);
        }

        // Find first symbol that is the argument name.
        // Remove it from the tree and set the name to the argument name
        for (let i: number = 0; i < cursor.nodes.length; i++) {
            const node = cursor.nodes[i];
            if (node instanceof CppParseTree) {
                continue;
            }

            if (node.type === CppTokenType.Symbol && this.keywords.find((k) => k === node.value) === undefined) {
                argument.name = node.value;
                cursor.nodes.splice(i, 1);
            }
        }

        this.StripNonTypeNodes(argument.type);
        return argument;
    }

    private GetDefaultArgument(tree: CppParseTree): CppArgument {
        const argument: CppArgument = new CppArgument();

        for (const node of tree.nodes) {
            if (node instanceof CppParseTree) {
                break;
            }
            const symbolCount = argument.type.nodes
                .filter((n) => n instanceof CppToken)
                .map((n) => n as CppToken)
                .filter((n) => n.type === CppTokenType.Symbol)
                .filter((n) => this.keywords.find((k) => k === n.value) === undefined)
                .length;

            if (node.type === CppTokenType.Symbol
                && this.keywords.find((k) => k === node.value) === undefined
            ) {
                if (symbolCount === 1) {
                    argument.name = node.value;
                    continue;
                } else if (symbolCount > 1) {
                    throw new Error("Too many non keyword symbols.");
                }
            }

            argument.type.nodes.push(node);
        }

        this.StripNonTypeNodes(argument.type);
        return argument;
    }

    private GetArgument(tree: CppParseTree): CppArgument {
        // Copy tree structure leave original untouched.
        const copy = this.RemoveUnusedTokens(tree);

        // Special case with only ellipsis. C style variadic arguments
        if (copy.nodes.length === 1) {
            const node = copy.nodes[0];
            if (node instanceof CppToken && node.type === CppTokenType.Ellipsis) {
                const argument: CppArgument = new CppArgument();
                argument.name = node.value;
                return argument;
            }
        }

        // Check if it is has a trailing return.
        const startTrailingReturn: number = copy.nodes
            .findIndex((t) => t instanceof CppToken ? t.type === CppTokenType.Arrow : false);

        // Special case trailing return.
        if (startTrailingReturn !== -1) {
            return this.GetArgumentFromTrailingReturn(copy, startTrailingReturn);
        }

        // Handle function pointers
        if (this.IsFuncPtr(copy.nodes) === true) {
            return this.GetArgumentFromFuncPtr(copy);
        }

        if (this.IsArrayPtr(copy.nodes) === true) {
            return this.GetArgumentFromFuncPtr(copy);
        }

        // Handle member pointers
        for (let token: number = 0; token < copy.nodes.length - 1; token++) {
            const firstToken: CppToken = copy.nodes[token] as CppToken;
            const secondToken: CppToken = copy.nodes[token + 1] as CppToken;

            if (firstToken.type === CppTokenType.Symbol && secondToken.type === CppTokenType.Pointer &&
                firstToken.value.endsWith("::")) {
                    firstToken.type = CppTokenType.MemberPointer;
            }
        }

        return this.GetDefaultArgument(copy);
    }

    private GetSubExprStartEnd(expression: string, startSearch: number, openExpr: string, closeExpr: string): number[] {
        let openExprOffset: number = -1;
        let nestedCount: number = 0;
        for (let i: number = startSearch; i < expression.length; i++) {
            if (expression[i] === openExpr && openExprOffset === -1) {
                openExprOffset = i;
            }

            if (expression[i] === openExpr) {
                nestedCount++;
            } else if (expression[i] === closeExpr && nestedCount > 0) {
                nestedCount--;
            }

            if (expression[i] === closeExpr && nestedCount === 0 && openExprOffset !== -1) {
                return [openExprOffset, i + 1];
            }
        }

        return [0, 0];
    }

    private GetTemplate(expression: string): string {
        if (expression.startsWith("template") === false) {
            return "";
        }

        let startTemplateOffset: number = -1;
        for (let i: number = "template".length; i < expression.length; i++) {
            if (expression[i] === "<") {
                startTemplateOffset = i;
                break;
            } else if (expression[i] !== " ") {
                return "";
            }
        }

        if (startTemplateOffset === -1) {
            return "";
        }

        const [start, end] = this.GetSubExprStartEnd(expression, startTemplateOffset, "<", ">");
        return expression.slice(0, end);
    }

    private GetArgsFromTemplate(template: string): string[] {
        const args: string[] = [];
        if (template === "") {
            return args;
        }

        // Remove <> and add a comma to the end to remove edge case.
        template = template.slice(template.indexOf("<") + 1, template.lastIndexOf(">")).replace(/^\s+|\s+$/g, "") + ",";

        // Remove = and everything to the right until a , comes up
        template = template.replace(/(\W*=\W*\S*\,)/gm, ",");

        const nestedCounts: { [key: string]: number; } = {
            "(": 0,
            "<": 0,
            "{": 0,
        };

        let lastSeparator: number = 0;
        for (let i: number = 0; i < template.length; i++) {
            const notInSubExpr: boolean = nestedCounts["<"] === 0
                && nestedCounts["("] === 0
                && nestedCounts["{"] === 0;

            if (notInSubExpr === true && template[i] === ",") {
                args.push(template.slice(lastSeparator + 1, i).replace(/^\s+|\s+$/g, ""));
            } else if (notInSubExpr === true && (template[i] === " " || template[i] === ".")) {
                lastSeparator = i;
            }

            if (template[i] === "(") {
                nestedCounts["("]++;
            } else if (template[i] === ")" && nestedCounts["("] > 0) {
                nestedCounts["("]--;
            } else if (template[i] === "<") {
                nestedCounts["<"]++;
            } else if (template[i] === ">" && nestedCounts["<"] > 0) {
                nestedCounts["<"]--;
            } else if (template[i] === "{") {
                nestedCounts["{"]++;
            } else if (template[i] === "}" && nestedCounts["{"] > 0) {
                nestedCounts["{"]--;
            }
        }

        return args;
    }

    private isVsCodeAutoComplete(line: string): boolean {
        switch (line) {
            case "*/":
                this.vscodeAutoGeneratedComment = true;
                return true;
            default:
                return false;
        }
    }
}


================================================
FILE: src/Lang/Cpp/CppToken.ts
================================================
export enum CppTokenType {
    Symbol,
    Pointer,
    Reference,
    ArraySubscript,
    OpenParenthesis,
    CloseParenthesis,
    CurlyBlock,
    Assignment,
    Comma,
    Arrow,
    CommentBlock,
    CommentLine,
    Ellipsis,
    Attribute,
    MemberPointer,
}

export class CppToken {
    public type: CppTokenType;
    public value: string;

    constructor(type: CppTokenType, value: string) {
        this.type = type;
        this.value = value;
    }
}


================================================
FILE: src/extension.ts
================================================
"use strict";
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from "vscode";
import CodeParserController from "./CodeParserController";
import DoxygenCompletionItemProvider from "./DoxygenCompletionItemProvider";

enum Version {
    CURRENT = "1.4.0",
    PREVIOUS = "1.3.2",
    KEY = "doxdocgen_version",
}

// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
export function activate(context: vscode.ExtensionContext) {
    const parser = new CodeParserController();

    context.subscriptions.push(parser);

    const version = context.globalState.get<string>(Version.KEY);
    if (version === undefined) {
        context.globalState.update(Version.KEY, Version.CURRENT);
    } else if (version !== Version.CURRENT) {
        context.globalState.update(Version.KEY, Version.CURRENT);
    }

    /*register doxygen commands intellisense */
    if (vscode.workspace.getConfiguration("doxdocgen.generic").get<boolean>("commandSuggestion")) {
        // tslint:disable-next-line: max-line-length
        vscode.languages.registerCompletionItemProvider({ language: "cpp", scheme: "file" }, new DoxygenCompletionItemProvider(), "@", "\\");
    }

    // After the CompletionItemProvider is registered, it cannot be unregistered
    // Check the settings everytime when it is triggered would be inefficient
    // So just prompt the user to restart to take effect
    vscode.workspace.onDidChangeConfiguration((event) => {
        if (event.affectsConfiguration("doxdocgen.generic.commandSuggestion")) {
            vscode.window.showWarningMessage("Please restart vscode to apply the changes!");
        }
    });
}


================================================
FILE: src/templatedString.ts
================================================
import { getEnvVars } from "./util";

/**
 * Represent a templated variable in string
 */
export interface ITemplate {
    toReplace: string;  // The template to be replaced in string
    with: string;       // The value to replace with
}

export function getIndentedTemplate(replace: string): string {
    if (replace === "") {
        return "";
    }
    const snippets = replace.split(/({indent:\d+})/);

    let indentedString: string = "";
    let indentWidth: number = 0;

    // tslint:disable-next-line:prefer-for-of
    snippets.forEach((element) => {
        if (element.match(/{indent:\d+}/)) {
            const indents = parseInt(element.match(/{indent:(\d+)}/)[1], 10);
            indentWidth = indents;
            const numSpaces = Math.max(indentWidth - indentedString.length, 0);
            indentedString += " ".repeat(numSpaces);
        } else {
            // just some text
            indentedString += element;
        }
    });

    return indentedString;
}

/**
 * Expand variable template in the string
 * @param original the original string
 * @param template variable template to be expanded
 * @returns new string with expanded template
 */
export function getTemplatedString(original: string, template: ITemplate): string {
    const replacedTemplate = original.replace(template.toReplace, template.with);
    const replacedWithEnv = getEnvVars(replacedTemplate);
    return getIndentedTemplate(replacedWithEnv);
}

/**
 * Generate lines of doxygen comments from template
 * @param lines Arrays to store lines of comments
 * @param replace Variable template to be expanded
 * @param template Original string that contains variable templates
 * @param templateWith Arrays of values to replace in the original template string
 */
export function generateFromTemplate(
    lines: string[],
    replace: string,
    template: string,
    templateWith: string[],
) {
    templateWith.forEach((element: string) => {
        // Ignore null values
        if (element !== null) {
            lines.push(...getTemplatedString(template, { toReplace: replace, with: element }).split("\n"));
        }
    });
}

/**
 * Expand multiple variable templates in the string
 * @param original string containing multiple variables to be expanded
 * @param templates variable templates to be expanded
 * @returns new string with expanded templates
 */
export function getMultiTemplatedString(
    original: string,
    templates: ITemplate[],
): string {
    // For each replace entry, attempt to replace it with the corresponding param in the template
    for (const template of templates) {
        original = original.replace(template.toReplace, template.with);
    }
    return getEnvVars(getIndentedTemplate(original));
}


================================================
FILE: src/test/CppTests/Attributes.test.ts
================================================
//
// Note: This example test is leveraging the Mocha test framework.
// Please refer to their documentation on https://mochajs.org/ for help.
//

// The module 'assert' provides assertion methods from node
import * as assert from "assert";

// You can import and use all API from the 'vscode' module
// as well as import your extension to test it
import * as vscode from "vscode";
import TestSetup from "./TestSetup";

// Defines a Mocha test suite to group tests of similar kind together
suite("C++ - Attributes Tests", () => {
    const testSetup: TestSetup = new TestSetup("void foo();");

    // Tests
    test("Attributes on parameter", () => {
        const result = testSetup.SetLine("int foo([[maybe_unused]] int a, [[maybe_unused]]double& b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n * @param b \n * @return int \n */");
    });

    test("Attributes on function", () => {
        const result = testSetup.SetLine("[[nodiscard]] int foo(int a, double& b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n * @param b \n * @return int \n */");
    });

    test("Multiple attributes on parameter", () => {
        const result = testSetup.SetLine("int foo([[maybe_unused]] [[carries_dependency]]"
            + " int a, double& b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n * @param b \n * @return int \n */");
    });

    test("Multiple attributes on function", () => {
        const result = testSetup.SetLine("[[nodiscard]][[deprecated(\"old\")]] int foo(int a, double& b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n * @param b \n * @return int \n */");
    });

    test("Specifier and attribute on class", () => {
        const result = testSetup.SetLine("class [[nodiscard]] alignas(8) Matrix {").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n */");
    });

    test("Specifier and attribute on struct", () => {
        const result = testSetup.SetLine("struct [[nodiscard]] alignas(8) Matrix {").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n */");
    });

    test("Noexcept on function", () => {
        let result = testSetup.SetLine("constexpr int foo(int a, double& b) noexcept(true);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n * @param b \n * @return constexpr int \n */");

        result = testSetup.SetLine("constexpr int foo(int a, double& b) noexcept;").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n * @param b \n * @return constexpr int \n */");
    });

    test("Throw on function", () => {
        const result = testSetup.SetLine("constexpr int foo(int a, double& b) throw(std::except);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n * @param b \n * @return constexpr int \n */");
    });

    test("Newline in function", () => {
        const result = testSetup.SetLines(["static void ResetActionState( BOOL sendNAK )", "{"]).GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param sendNAK \n */");
    });
});


================================================
FILE: src/test/CppTests/Con-AndDestructor.test.ts
================================================
//
// Note: This example test is leveraging the Mocha test framework.
// Please refer to their documentation on https://mochajs.org/ for help.
//

// The module 'assert' provides assertion methods from node
import * as assert from "assert";

// You can import and use all API from the 'vscode' module
// as well as import your extension to test it
import * as vscode from "vscode";
import TestSetup from "./TestSetup";

// Defines a Mocha test suite to group tests of similar kind together
suite("C++ - Con- and Destructor Tests", () => {
    const testSetup: TestSetup = new TestSetup("void foo();");

    testSetup.cfg.Generic.generateSmartText = false;

    // Tests
    test("Normal Constructor", () => {
        const result = testSetup.SetLine("Foo(int a);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n */");
    });

    test("Constructor with initializer list", () => {
        const result = testSetup.SetLine("Foo(int a) : m_a(a) {").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n */");
    });

    test("Explicit Constructor", () => {
        const result = testSetup.SetLine("explicit Foo(int a);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n */");
    });

    test("Deleted Constructor", () => {
        const result = testSetup.SetLine("Foo(int a) = delete;").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n */");
    });

    test("Default Constructor", () => {
        const result = testSetup.SetLine("Foo() = default;").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n */");
    });

    test("Destructor", () => {
        const result = testSetup.SetLine("~Foo();").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n */");
    });

    test("Virtual Destructor", () => {
        const result = testSetup.SetLine("virtual ~Foo();").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n */");
    });

    test("Deleted Destructor", () => {
        const result = testSetup.SetLine("virtual ~Foo() = 0;").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n */");
    });

    test("Default Destructor", () => {
        const result = testSetup.SetLine("~Foo() = default;").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n */");
    });
});


================================================
FILE: src/test/CppTests/Config.test.ts
================================================
//
// Note: This example test is leveraging the Mocha test framework.
// Please refer to their documentation on https://mochajs.org/ for help.
//

// The module 'assert' provides assertion methods from node
import * as assert from "assert";

// You can import and use all API from the 'vscode' module
// as well as import your extension to test it
import * as moment from "moment";
import * as vscode from "vscode";
import { Config } from "../../Config";
import GitConfig from "../../GitConfig";
import TestSetup from "./TestSetup";

// Defines a Mocha test suite to group tests of similar kind together
suite("C++ - Configuration Tests", () => {
    const testSetup: TestSetup = new TestSetup("void foo();");

    // Tests
    test("Default config", () => {
        testSetup.cfg = new Config();
        const result = testSetup.SetLine("template<typename T> bool foo(T a);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @tparam T \n * @param a \n * "
            + "@return true \n * @return false \n */");
    });

    test("Comment order", () => {
        testSetup.cfg = new Config();
        testSetup.cfg.Generic.order = ["brief", "param", "tparam", "return"];
        const result = testSetup.SetLine("template<typename T> bool foo(T a);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * @param a \n * @tparam T \n * "
            + "@return true \n * @return false \n */");
    });

    test("Non existing order param", () => {
        testSetup.cfg = new Config();
        testSetup.cfg.Generic.order = ["breif"];
        const result = testSetup.SetLine("template<typename T> bool foo(T a);").GetResult();
        assert.strictEqual(result, "/**\n */");
    });

    test("Modified template", () => {
        testSetup.cfg = new Config();

        testSetup.cfg.C.firstLine = "";
        testSetup.cfg.C.lastLine = "";
        testSetup.cfg.C.commentPrefix = "/// ";
        testSetup.cfg.Generic.briefTemplate = "\\brief ";
        testSetup.cfg.Generic.paramTemplate = "\\param {param} ";
        testSetup.cfg.Cpp.tparamTemplate = "\\tparam {param} ";
        testSetup.cfg.Generic.returnTemplate = "\\return {type} ";

        const result = testSetup.SetLine("template<typename T> bool foo(T a);").GetResult();
        assert.strictEqual(result, "/// \\brief \n/// \n/// \\tparam T \n/// \\param a \n"
            + "/// \\return true \n/// \\return false ");

    });

    test("Disable true false on bool", () => {
        testSetup.cfg = new Config();
        testSetup.cfg.Generic.boolReturnsTrueFalse = false;
        const result = testSetup.SetLine("template<typename T> bool foo(T a);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @tparam T \n * @param a \n"
            + " * @return bool \n */");
    });

    test("Disable including return type on function", () => {
        testSetup.cfg = new Config();
        testSetup.cfg.Generic.includeTypeAtReturn = false;

        const result = testSetup.SetLine("template<typename T> bool foo(T a);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @tparam T \n * @param a \n"
            + " * @return  \n */");
    });

    test("Disable including return type on non-function", () => {
        testSetup.cfg = new Config();
        testSetup.cfg.Generic.includeTypeAtReturn = false;

        const result = testSetup.SetLine("bool b;").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n */");
    });

    test("Newlines after params and tparams but not after brief", () => {
        testSetup.cfg = new Config();
        testSetup.cfg.Generic.order = ["brief", "tparam", "empty", "param", "empty", "return"];

        const result = testSetup.SetLine("template<typename T> bool foo(T a);").GetResult();
        testSetup.cfg.Generic.order = ["brief", "empty", "tparam", "param", "return"]; // reset to default
        assert.strictEqual(result, "/**\n * @brief \n * @tparam T \n * \n * @param a \n * \n"
            + " * @return true \n * @return false \n */");
    });

    test("Function comment indentation test", () => {
        testSetup.cfg = new Config();
        let result = testSetup.SetLine("\ttemplate<typename T> bool foo(T a);").GetResult();
        assert.strictEqual(result, "/**\n\t * @brief \n\t * \n\t * @tparam T \n\t * @param a \n\t * "
            + "@return true \n\t * @return false \n\t */");

        result = testSetup.SetLine("          template<typename T> bool foo(T a);").GetResult();
        assert.strictEqual(result, "/**\n           * @brief \n           * \n           * @tparam T "
            + "\n           * @param a \n           * "
            + "@return true \n           * @return false \n           */");
    });

    test("File comment indentation test", () => {
        testSetup.cfg.File.fileOrder = [
            "file",
            "author",
            "brief",
            "version",
            "date",
            "copyright",
        ];
        testSetup.cfg.File.fileTemplate = "@file{indent:10}{name}";
        testSetup.cfg.Generic.authorTag = "@author{indent:10}{author}";
        testSetup.cfg.Generic.briefTemplate = "@brief{indent:10}Thing";
        testSetup.cfg.File.versionTag = "@version{indent:10}0.1";
        testSetup.cfg.Generic.dateTemplate = "@date{indent:10}date";
        testSetup.cfg.File.copyrightTag = ["@copyright{indent:10}Copyright(c)"];
        const result = testSetup.SetLine("").GetResult();
        assert.strictEqual(result, `/**
 * @file     MockDocument.h
 * @author   your name
 * @brief    Thing
 * @version  0.1
 * @date     date
 * @copyrightCopyright(c)
 */`);
    });

    test("Lines to get test", () => {
        testSetup.cfg = new Config();
        testSetup.cfg.Generic.linesToGet = 2;
        const positiveResult = testSetup.SetLines(["template<typename T> bool \n", "foo(T a);"]).GetResult();
        assert.strictEqual(positiveResult, "/**\n * @brief \n * \n * @tparam T \n * @param a \n * "
            + "@return true \n * @return false \n */");
        testSetup.cfg.Generic.linesToGet = 0;
        testSetup.firstLine = 1;
        const negativeResult = testSetup.SetLines(["template<typename T> bool \n", "foo(T a);"]).GetResult();
        assert.strictEqual(negativeResult, "/**\n * @brief \n * \n */");
    });

    test("File description order test", () => {
        testSetup.cfg = new Config();
        testSetup.firstLine = 0;
        testSetup.cfg.File.fileOrder = ["brief", "author", "date", "file"];
        const result = testSetup.SetLine("").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * @author your name (you@domain.com)\n" +
            " * @date " + moment().format("YYYY-MM-DD") + "\n * @file MockDocument.h\n */");
    });

    test("Custom smart text Ctor", () => {
        testSetup.cfg.Cpp.ctorText = "Test {name}";
        const result = testSetup.SetLine("Foo();").GetResult();
        assert.strictEqual(result, "/**\n * @brief Test Foo\n * \n */");
    });

    test("Custom smart text Dtor", () => {
        testSetup.cfg.Cpp.dtorText = "Test {name}";
        const result = testSetup.SetLine("~Foo();").GetResult();
        assert.strictEqual(result, "/**\n * @brief Test Foo\n * \n */");
    });

    test("Custom smart text getter", () => {
        testSetup.cfg.C.getterText = "Test {name}";
        const result = testSetup.SetLine("int getFoo();").GetResult();
        assert.strictEqual(result, "/**\n * @brief Test Foo\n * \n * @return int \n */");
    });

    test("Custom smart text setter", () => {
        testSetup.cfg.C.setterText = "Test {name}";
        const result = testSetup.SetLine("void setFoo(int foo);").GetResult();
        assert.strictEqual(result, "/**\n * @brief Test Foo\n * \n * @param foo \n */");
    });

    test("Custom smart text factory method", () => {
        testSetup.cfg.C.factoryMethodText = "Test {name}";
        const result = testSetup.SetLine("int createFoo();").GetResult();
        assert.strictEqual(result, "/**\n * @brief Test Foo\n * \n * @return int \n */");
    });

    test("Don't split casing for smart text", () => {
        testSetup.cfg.C.factoryMethodText = "Test {name}";
        testSetup.cfg.Generic.splitCasingSmartText = false;
        const result = testSetup.SetLine("int createFooObject();").GetResult();
        assert.strictEqual(result, "/**\n * @brief Test FooObject\n * \n * @return int \n */");
    });

    test("Remove inserted '*/' from line", () => {
        const result = testSetup.SetLines(["*/", "int foo();"]).GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @return int \n */");
    });

    test("Single alignment", () => {
        testSetup.cfg.Generic.paramTemplate = "@param{indent:10}{param}";
        testSetup.cfg.Cpp.tparamTemplate = "@tparam{indent:10}{param}";
        testSetup.cfg.Generic.returnTemplate = "@return{indent:10}{type}";
        testSetup.cfg.Generic.briefTemplate = "@brief{indent:10}Brief";

        const result = testSetup.SetLines(["template<typename T>", "int foo(std::string bar, T foobar);"]).GetResult();
        // tslint:disable-next-line:max-line-length
        assert.strictEqual(result, "/**\n * @brief    Brief\n * \n * @tparam   T\n * @param    bar\n * @param    foobar\n * @return   int\n */");
    });

    test("Multi alignment", () => {
        testSetup.cfg.Generic.paramTemplate = "@param{indent:10}{param}{indent:30}Parameters everywhere";
        testSetup.cfg.Cpp.tparamTemplate = "@tparam{indent:10}{param}{indent:30}I'm a template";
        testSetup.cfg.Generic.returnTemplate = "@return{indent:10}{type}{indent:30}Returns stuff";
        testSetup.cfg.Generic.briefTemplate = "@brief{indent:30}Short desc";

        const result = testSetup.SetLines(["template<typename T>", "int foo(std::string bar, T foobar);"]).GetResult();
        // tslint:disable-next-line:max-line-length
        assert.equal(result, "/**\n * @brief                        Short desc\n * \n * @tparam   T                   I'm a template\n * @param    bar                 Parameters everywhere\n * @param    foobar              Parameters everywhere\n * @return   int                 Returns stuff\n */");
    });

    test("Negative alignment tests", () => {
        testSetup.cfg.Generic.returnTemplate = "";

        const result = testSetup.SetLines(["template<typename T>", "int foo(std::string bar, T foobar);"]).GetResult();
        // tslint:disable-next-line:max-line-length
        assert.equal(result, "/**\n * @brief                        Short desc\n * \n * @tparam   T                   I'm a template\n * @param    bar                 Parameters everywhere\n * @param    foobar              Parameters everywhere\n */");
    });

    test("Multiline template", () => {
        testSetup.cfg.C.commentPrefix = "/// ";
        testSetup.cfg.C.firstLine = "";
        testSetup.cfg.C.lastLine = "";
        testSetup.cfg.Generic.briefTemplate = "<summary>\n{text}\n</summary>";
        testSetup.cfg.Generic.paramTemplate = "<param name=\"{param}\">\n</param>";
        testSetup.cfg.Generic.returnTemplate = "<returns>\n</returns>";
        const result = testSetup.SetLine("    int foo(bool a);").GetResult();
        // tslint:disable:no-trailing-whitespace
        assert.strictEqual(result, 
            result, `/// <summary>
    /// 
    /// </summary>
    /// 
    /// <param name="a">
    /// </param>
    /// <returns>
    /// </returns>`,
        );
        // tslint:enable:no-trailing-whitespace
    });

    test("Macro define in funtion", () => {
        testSetup.cfg = new Config();
        testSetup.cfg.Generic.filteredKeywords = ["MOCKABLE"];
        // tslint:disable-next-line:max-line-length
        const result = testSetup.SetLine("MOCKABLE void processNetworkStatusReset( const common_n::NetworkCommands_s *networkstatus );").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param networkstatus \n */");
    });

    test("Custom tag", () => {
        testSetup.cfg = new Config();
        testSetup.cfg.Generic.order = ["custom"];
        testSetup.cfg.Generic.customTags = ["@note"];
        const result = testSetup.SetLine("void foo();").GetResult();
        assert.strictEqual(result, "/**\n * @note\n */");
    });

    test("Custom tag expansion in function", () => {
        testSetup.cfg = new Config();
        testSetup.cfg.Generic.order = ["custom"];
        testSetup.cfg.Generic.customTags = [
            "@author {author}",
            "@date {date}",
            "@note {email}",
            "@file {file}",
        ];
        const result = testSetup.SetLine("void foo();").GetResult();
        const date = moment().format("YYYY-MM-DD");
        assert.strictEqual(result, `/**\n * @author your name\n * @date ${date}\n * @note you@domain.com\n` +
        ` * @file MockDocument.h\n */`);
    });

    test("Env variable", () => {
        testSetup.cfg = new Config();
        testSetup.cfg.Generic.order = ["custom"];
        if (process.platform === "win32") {
            testSetup.cfg.Generic.customTags = ["@author ${env:USERNAME}"];
            const res = testSetup.SetLine("void foo();").GetResult();
            // USERNAME env var is different for everybody
            assert.notStrictEqual("/**\n * @author USERNAME\n */", res);
        } else {
            testSetup.cfg.Generic.customTags = ["@author ${env:USER}"];
            const res = testSetup.SetLine("void foo();").GetResult();
            // USER env var is different for everybody
            assert.notStrictEqual("/**\n * @author USER\n */", res);
        }

        testSetup.cfg.Generic.customTags = ["@author ${env:MY_VARIABLE}"];
        const result = testSetup.SetLine("void foo();").GetResult();
        assert.strictEqual(result, "/**\n * @author MY_VARIABLE\n */");
    });

    test("Use git user.name as author", () => {
        testSetup.cfg = new Config();
        testSetup.cfg.Generic.useGitUserName = true;
        const result = testSetup.SetLine("").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @file MockDocument.h\n * @author " +
            testSetup.gitConfig.UserName +
            " (you@domain.com)\n * @date " + moment().format("YYYY-MM-DD") + "\n */");
    });

    test("Use git user.email as email", () => {
        testSetup.cfg = new Config();
        testSetup.cfg.Generic.useGitUserEmail = true;
        const result = testSetup.SetLine("").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @file MockDocument.h\n * @author your name (" +
            testSetup.gitConfig.UserEmail +
            ")\n * @date " + moment().format("YYYY-MM-DD") + "\n */");
    });

    test("Substitute author and email by git config", () => {
        testSetup.cfg = new Config();
        testSetup.cfg.Generic.useGitUserName = true;
        testSetup.cfg.Generic.useGitUserEmail = true;
        const result = testSetup.SetLine("").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @file MockDocument.h\n * @author " +
            testSetup.gitConfig.UserName + " (" + testSetup.gitConfig.UserEmail +
            ")\n * @date " + moment().format("YYYY-MM-DD") + "\n */");
    });

});


================================================
FILE: src/test/CppTests/FileDescription.test.ts
================================================
//
// Note: This example test is leveraging the Mocha test framework.
// Please refer to their documentation on https://mochajs.org/ for help.
//

// The module 'assert' provides assertion methods from node
import * as assert from "assert";

// You can import and use all API from the 'vscode' module
// as well as import your extension to test it
import * as moment from "moment";
import * as vscode from "vscode";
import TestSetup from "./TestSetup";

// Defines a Mocha test suite to group tests of similar kind together
suite("File Description Tests", () => {
    const testSetup: TestSetup = new TestSetup("void foo();");
    testSetup.cfg.File.fileOrder = ["brief", "empty", "file", "author", "date"];
    const date = moment().format("YYYY-MM-DD");
    const year = moment().format("YYYY");

    // Tests
    test("#include on next line", () => {
        const result = testSetup.SetLine("#include <iostream>").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @file MockDocument.h\n * @author your name (you@domain.com)\n" +
            " * @date " + date + "\n */");
    });

    test("#pragma on next line", () => {
        const result = testSetup.SetLine("#pragma Foo").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @file MockDocument.h\n * @author your name (you@domain.com)\n" +
            " * @date " + date + "\n */");
    });

    test("On first line of document", () => {
        const result = testSetup.SetLine("").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @file MockDocument.h\n * @author your name (you@domain.com)\n" +
            " * @date " + date + "\n */");
    });

    test("File description in first line", () => {
        const result = testSetup.SetLines([
            "/**",
            "",
            "",
            "struct T {",
            "   int i;",
            "};",
            ], false).GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @file MockDocument.h\n * @author your name (you@domain.com)\n" +
            " * @date " + date + "\n */");
    });

    test("Don't generate non existing commands", () => {
        testSetup.cfg.File.fileOrder = ["dates"];
        const result = testSetup.SetLine("").GetResult();
        assert.strictEqual(result, "/**\n */");
    });

    test("version block", () => {
        testSetup.cfg.File.fileOrder = ["version"];
        const result = testSetup.SetLine("").GetResult();
        assert.strictEqual(result, "/**\n * @version 0.1\n */");
    });

    test("Copyright block", () => {
        testSetup.cfg.File.fileOrder = ["copyright"];
        const result = testSetup.SetLine("").GetResult();
        assert.strictEqual(result, "/**\n * @copyright Copyright (c) " + year + "\n */");
    });

    test("version block", () => {
        testSetup.cfg.File.fileOrder = ["version"];
        const result = testSetup.SetLine("").GetResult();
        assert.strictEqual(result, "/**\n * @version 0.1\n */");
    });

    test("custom block", () => {
        testSetup.cfg.File.fileOrder = ["custom"];
        testSetup.cfg.File.customTag = ["First Line", "{year} Year Line", "{date} Date Line",
                                        "{author} Author Line", "{email} Email Line", "{file} File Line"];
        const result = testSetup.SetLine("").GetResult();
        assert.strictEqual(result, "/**\n * First Line\n * " + year + " Year Line\n * " + date + " Date Line\n" +
            " * your name Author Line\n * you@domain.com Email Line\n" + " * MockDocument.h File Line\n */");
    });
});


================================================
FILE: src/test/CppTests/FunctionPointer.test.ts
================================================
//
// Note: This example test is leveraging the Mocha test framework.
// Please refer to their documentation on https://mochajs.org/ for help.
//

// The module 'assert' provides assertion methods from node
import * as assert from "assert";

// You can import and use all API from the 'vscode' module
// as well as import your extension to test it
import * as vscode from "vscode";
import TestSetup from "./TestSetup";

// Defines a Mocha test suite to group tests of similar kind together
suite("C++ - Function pointer Tests", () => {
    const testSetup: TestSetup = new TestSetup("void foo();");

    // Tests
    test("Function pointer return", () => {
        const result = testSetup.SetLine("int (*idputs(int a, int b))(char *);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n * @param b \n * @return int(*)(char*) \n */");
    });

    test("Nested function pointer return", () => {
        const result = testSetup.SetLine("int (*(*(*foo(int a, int b))(int))(double))(float);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n * @param b \n"
            + " * @return int(*(*(*)(int))(double))(float) \n */");
    });

    test("Function pointer parameter", () => {
        const result = testSetup.SetLine("int foo(int (*puts)(const char *));").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param puts \n * @return int \n */");
    });

    test("Struct function pointer return with struct FP parameters and keywords", () => {
        const result = testSetup.SetLine("const struct foo (*idputs(int (*puts)(const char *), const"
            + " struct test(*str)(int *, const struct test(*str2))))(const char *);").GetResult();

        assert.strictEqual(result, "/**\n * @brief \n * \n * @param puts \n * @param str "
            + "\n * @return const struct foo(*)(const char*) \n */");
    });

    test("Memberpointer in function pointer", () => {
        const result = testSetup.SetLine("void foo(void (SomeClass::* func)());").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param func \n */");
    });

    test("Arraypointer", () => {
        const result = testSetup.SetLine("void some_function(int (*table)[]);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param table \n */");
    });
});


================================================
FILE: src/test/CppTests/Operators.test.ts
================================================
//
// Note: This example test is leveraging the Mocha test framework.
// Please refer to their documentation on https://mochajs.org/ for help.
//

// The module 'assert' provides assertion methods from node
import * as assert from "assert";

// You can import and use all API from the 'vscode' module
// as well as import your extension to test it
import * as vscode from "vscode";
import TestSetup from "./TestSetup";

// Defines a Mocha test suite to group tests of similar kind together
suite("C++ - Operators Tests", () => {
    const testSetup: TestSetup = new TestSetup("void foo();");

    // Tests
    test("+ operator", () => {
        const result = testSetup.SetLine("T operator +(const T& lhs, const T2& rhs);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param lhs \n * @param rhs \n * @return T \n */");
    });

    test("- operator", () => {
        const result = testSetup.SetLine("T operator- (const T& lhs, const T2& rhs);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param lhs \n * @param rhs \n * @return T \n */");
    });

    test("* operator with params", () => {
        const result = testSetup.SetLine("T operator*(const T& lhs, const T2& rhs);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param lhs \n * @param rhs \n * @return T \n */");
    });

    test("/ operator", () => {
        const result = testSetup.SetLine("T operator/(const T& lhs, const T2& rhs);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param lhs \n * @param rhs \n * @return T \n */");
    });

    test("% operator", () => {
        const result = testSetup.SetLine("T operator%(const T& lhs, const T2& rhs);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param lhs \n * @param rhs \n * @return T \n */");
    });

    test("^ operator", () => {
        const result = testSetup.SetLine("T operator^(const T& lhs, const T2& rhs);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param lhs \n * @param rhs \n * @return T \n */");
    });

    test("& operator with params", () => {
        const result = testSetup.SetLine("T operator&(const T& lhs, const T2& rhs);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param lhs \n * @param rhs \n * @return T \n */");
    });

    test("| operator", () => {
        const result = testSetup.SetLine("T operator|(const T& lhs, const T2& rhs);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param lhs \n * @param rhs \n * @return T \n */");
    });

    test("~ operator", () => {
        const result = testSetup.SetLine("T operator~(const T& t);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param t \n * @return T \n */");
    });

    test("<< operator", () => {
        const result = testSetup.SetLine("T operator<<(const T& lhs, const T2& rhs);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param lhs \n * @param rhs \n * @return T \n */");
    });

    test(">> operator", () => {
        const result = testSetup.SetLine("T operator>>(const T& lhs, const T2& rhs);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param lhs \n * @param rhs \n * @return T \n */");
    });

    test("! operator", () => {
        const result = testSetup.SetLine("bool operator!(const T& t);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param t \n * @return true \n * @return false \n */");
    });

    test("&& operator", () => {
        const result = testSetup.SetLine("bool operator&&(const T& lhs, const T2& rhs);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param lhs \n * @param rhs \n * @return true \n"
            + " * @return false \n */");
    });

    test("|| operator", () => {
        const result = testSetup.SetLine("bool operator||(const T& lhs, const T2& rhs);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param lhs \n * @param rhs \n * @return true \n"
            + " * @return false \n */");
    });

    test("!= operator", () => {
        const result = testSetup.SetLine("bool operator!=(const T& lhs, const T2& rhs);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param lhs \n * @param rhs \n * @return true \n"
            + " * @return false \n */");
    });

    test("== operator", () => {
        const result = testSetup.SetLine("bool operator==(const T& lhs, const T2& rhs);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param lhs \n * @param rhs \n * @return true \n"
            + " * @return false \n */");
    });

    test("<= operator", () => {
        const result = testSetup.SetLine("bool operator<=(const T& lhs, const T2& rhs);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param lhs \n * @param rhs \n * @return true \n"
            + " * @return false \n */");
    });

    test(">= operator", () => {
        const result = testSetup.SetLine("bool operator>=(const T& lhs, const T2& rhs);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param lhs \n * @param rhs \n * @return true \n"
            + " * @return false \n */");
    });

    test("< operator", () => {
        const result = testSetup.SetLine("bool operator<(const T& lhs, const T2& rhs);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param lhs \n * @param rhs \n * @return true \n"
            + " * @return false \n */");
    });

    test("> operator", () => {
        const result = testSetup.SetLine("bool operator>(const T& lhs, const T2& rhs);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param lhs \n * @param rhs \n * @return true \n"
            + " * @return false \n */");
    });

    test("= operator", () => {
        const result = testSetup.SetLine("T& operator=(const T& t);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param t \n * @return T& \n */");
    });

    test("+= operator", () => {
        const result = testSetup.SetLine("T& operator+=(const T& t);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param t \n * @return T& \n */");
    });

    test("-= operator", () => {
        const result = testSetup.SetLine("T& operator-=(const T& t);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param t \n * @return T& \n */");
    });

    test("*= operator", () => {
        const result = testSetup.SetLine("T& operator*=(const T& t);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param t \n * @return T& \n */");
    });

    test("/= operator", () => {
        const result = testSetup.SetLine("T& operator/=(const T& t);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param t \n * @return T& \n */");
    });

    test("%= operator", () => {
        const result = testSetup.SetLine("T& operator%=(const T& t);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param t \n * @return T& \n */");
    });

    test("^= operator", () => {
        const result = testSetup.SetLine("T& operator^=(const T& t);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param t \n * @return T& \n */");
    });

    test("&= operator", () => {
        const result = testSetup.SetLine("T& operator&=(const T& t);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param t \n * @return T& \n */");
    });

    test("|= operator", () => {
        const result = testSetup.SetLine("T& operator|=(const T& t);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param t \n * @return T& \n */");
    });

    test(">>= operator", () => {
        const result = testSetup.SetLine("T& operator>>=(const T& t);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param t \n * @return T& \n */");
    });

    test("<<= operator", () => {
        const result = testSetup.SetLine("T& operator<<=(const T& t);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param t \n * @return T& \n */");
    });

    test("prefix ++ operator", () => {
        const result = testSetup.SetLine("T& operator++();").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @return T& \n */");
    });

    test("prefix -- operator", () => {
        const result = testSetup.SetLine("T& operator--();").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @return T& \n */");
    });

    test("postfix ++ operator", () => {
        const result = testSetup.SetLine("T operator++(int);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @return T \n */");
    });

    test("postfix -- operator", () => {
        const result = testSetup.SetLine("T operator--(int);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @return T \n */");
    });

    test("() operator", () => {
        let result = testSetup.SetLine("R operator()(const T& a, const T2& b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n * @param b \n * @return R \n */");

        result = testSetup.SetLine("R operator( )(const T& a, const T2& b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n * @param b \n * @return R \n */");
    });

    test(", operator", () => {
        const result = testSetup.SetLine("T2& operator , (const T2& t);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param t \n * @return T2& \n */");
    });

    test("* operator without params", () => {
        const result = testSetup.SetLine("R& operator*();").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @return R& \n */");
    });

    test("& operator without params", () => {
        const result = testSetup.SetLine("R* operator&();").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @return R* \n */");
    });

    test("->* operator", () => {
        const result = testSetup.SetLine("R& operator->*();").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @return R& \n */");
    });

    test("-> operator", () => {
        const result = testSetup.SetLine("R* operator->();").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @return R* \n */");
    });

    test("[] operator", () => {
        let result = testSetup.SetLine("R operator[](S b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param b \n * @return R \n */");

        result = testSetup.SetLine("R operator[ ](S b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param b \n * @return R \n */");
    });

    test("new operator", () => {
        const result = testSetup.SetLine("void* operator new ( std::size_t count );").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param count \n * @return void* \n */");
    });

    test("new[] operator", () => {
        let result = testSetup.SetLine("void* operator new[]( std::size_t count, std::align_val_t al);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param count \n * @param al \n * @return void* \n */");

        result = testSetup.SetLine("void* operator new[ ]( std::size_t count, std::align_val_t al);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param count \n * @param al \n * @return void* \n */");
    });

    test("delete operator", () => {
        const result = testSetup.SetLine("void operator delete(void* ptr, std::size_t sz, std::align_val_t al);")
            .GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param ptr \n * @param sz \n * @param al \n */");
    });

    test("delete[] operator", () => {
        let result = testSetup.SetLine("void operator delete[] (void* ptr);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param ptr \n */");

        result = testSetup.SetLine("void operator delete [ ] (void* ptr);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param ptr \n */");
    });

    test("user literal operator", () => {
        let result = testSetup.SetLine("long double operator\"\"_My_C00l_Conversion(const char * str);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param str \n * @return long double \n */");

        result = testSetup.SetLine("long double operator \"\"_My_C00l_Conversion(const char * str);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param str \n * @return long double \n */");

        result = testSetup.SetLine("long double operator\"\"_My_C00l_Conversion (const char * str);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param str \n * @return long double \n */");
    });

    test("Implicit conversion operator", () => {
        const result = testSetup.SetLine("operator int() const;").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @return int \n */");
    });

    test("Explicit conversion operator", () => {
        const result = testSetup.SetLine("explicit operator int() const;").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @return int \n */");
    });

    test("conversion operator to struct", () => {
        const result = testSetup.SetLine("explicit operator struct foo() const;").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @return struct foo \n */");
    });

    test("conversion operator to struct pointer", () => {
        const result = testSetup.SetLine("explicit operator struct foo*() const;").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @return struct foo* \n */");
    });
});


================================================
FILE: src/test/CppTests/Parameters.test.ts
================================================
//
// Note: This example test is leveraging the Mocha test framework.
// Please refer to their documentation on https://mochajs.org/ for help.
//

// The module 'assert' provides assertion methods from node
import * as assert from "assert";

// You can import and use all API from the 'vscode' module
// as well as import your extension to test it
import * as vscode from "vscode";
import TestSetup from "./TestSetup";

// Defines a Mocha test suite to group tests of similar kind together
suite("C++ - Parameters Tests", () => {

    const testSetup: TestSetup = new TestSetup("void foo();");

    // Tests
    test("No parameters", () => {
        const result = testSetup.SetLine("void foo();").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n */");
    });

    test("Single parameter", () => {
        const result = testSetup.SetLine("void foo(int a);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n */");
    });

    test("Multiple parameters", () => {
        const result = testSetup.SetLine("void foo(int a, int b, int c);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n * @param b \n * @param c \n */");
    });

    test("Parameters with numbers in them", () => {
        const result = testSetup.SetLine("void foo(int a1, int b23);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n * @param b23 \n */");
    });

    test("Reference parameter", () => {
        const result = testSetup.SetLine("void foo(int& a);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n */");
    });

    test("Reference parameter with unsigned interger qualifier", () => {
        const result = testSetup.SetLine("void foo(unsigned int& a);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n */");
    });

    test("Reference parameter with const qualifier", () => {
        const result = testSetup.SetLine("void foo(const int& a);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n */");
    });

    test("Reference parameter with const and interger qualifier", () => {
        const result = testSetup.SetLine("void foo(const unsigned int& a);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n */");
    });

    test("Pointer parameter", () => {
        const result = testSetup.SetLine("void foo(int* a);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n */");
    });

    test("Const parameter", () => {
        let result = testSetup.SetLine("void foo(const int a1);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n */");

        result = testSetup.SetLine("void foo(int const a1);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n */");
    });

    test("Struct parameter", () => {
        const result = testSetup.SetLine("void foo(int a1, int b23);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n * @param b23 \n */");
    });

    test("Template parameter", () => {
        const result = testSetup.SetLine("void foo(Matrix<T, N, M> mat);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param mat \n */");
    });

    test("Enum parameter", () => {
        const result = testSetup.SetLine("void foo(enum foo bar);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param bar \n */");
    });

    test("Const parameter with const pointer to const pointer", () => {
        let result = testSetup.SetLine("void foo(const int * const * const a1);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n */");

        result = testSetup.SetLine("void foo(int const * const * const a1);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n */");
    });

    test("Fundamental return type with modifiers", () => {
        let result = testSetup.SetLine("void foo(unsigned int a);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n */");

        result = testSetup.SetLine("void foo(unsigned short int a);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n */");

        result = testSetup.SetLine("void foo(signed short a);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n */");

        result = testSetup.SetLine("void foo(long a);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n */");

        result = testSetup.SetLine("void foo(unsigned long long int a);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n */");

        result = testSetup.SetLine("void foo(signed a);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n */");

        result = testSetup.SetLine("void foo(unsigned a);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n */");

        result = testSetup.SetLine("void foo(unsigned char a);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n */");

        result = testSetup.SetLine("void foo(long double a);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a \n */");

        result = testSetup.SetLine("void foo(long unsigned unsigned_a);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param unsigned_a \n */");
    });

    test("Parameter type in namespace", () => {
        const result = testSetup.SetLine("void foo(MyNamespace::Foo a1);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n */");
    });

    test("Parameter template type in namespace", () => {
        const result = testSetup.SetLine("void foo(Math::Matrix<A, B, C> mat);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param mat \n */");
    });

    test("Parameter template type within templated namespace", () => {
        const result = testSetup.SetLine("void foo(Matrix<A, B, C>::Matrix<A, B, C> mat);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param mat \n */");
    });

    test("Parameter type in nested namespacee", () => {
        const result = testSetup.SetLine("void foo( Math::LA::Matrix<A, B, C> mat);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param mat \n */");
    });

    test("Parameter with default char literal", () => {
        let result = testSetup.SetLine("void foo(char a1 = 'a', int b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n * @param b \n */");

        result = testSetup.SetLine("void foo(char a1 = u'b', int b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n * @param b \n */");

        result = testSetup.SetLine("void foo(char a1 = u8'b', int b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n * @param b \n */");

        result = testSetup.SetLine("void foo(char a1 = U'a', int b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n * @param b \n */");

        result = testSetup.SetLine("void foo(char a1 = l',', int b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n * @param b \n */");

        result = testSetup.SetLine("void foo(int a1 = 'ab', int b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n * @param b \n */");
    });

    test("Parameter with default string literal", () => {
        let result = testSetup.SetLine("void foo(std::string a1 = \"bar\", int b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n * @param b \n */");

        result = testSetup.SetLine("void foo(std::string a1 = u\"bar, test\", int b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n * @param b \n */");

        result = testSetup.SetLine("void foo(std::string a1 = u8\"bar\", int b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n * @param b \n */");

        result = testSetup.SetLine("void foo(std::string a1 = U\"bar\", int b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n * @param b \n */");

        result = testSetup.SetLine("void foo(std::string a1 = l\"bar\", int b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n * @param b \n */");

        result = testSetup.SetLine("void foo(std::string a1 = R\"(bar\\t\\\")\", int b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n * @param b \n */");
    });

    test("Parameter with default integer literal", () => {
        let result = testSetup.SetLine("void foo(int a1 = 1337, int b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n * @param b \n */");

        result = testSetup.SetLine("void foo(int a1 = 01337, int b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n * @param b \n */");

        result = testSetup.SetLine("void foo(int a1 = 1337, int b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n * @param b \n */");

        result = testSetup.SetLine("void foo(int a1 = 0x0FAB, int b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n * @param b \n */");

        result = testSetup.SetLine("void foo(int a1 = 0X0FAB, int b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n * @param b \n */");

        result = testSetup.SetLine("void foo(std::uint8_t a1 = 0b110011, int b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n * @param b \n */");

        result = testSetup.SetLine("void foo(std::byte a1 = 0B11111, int b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n * @param b \n */");

        result = testSetup.SetLine("void foo(long a1 = 1337l, int b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n * @param b \n */");

        result = testSetup.SetLine("void foo(long long a1 = 1337ll, int b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n * @param b \n */");

        result = testSetup.SetLine("void foo(unsigned long long a1 = 1337ull, int b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n * @param b \n */");

        result = testSetup.SetLine("void foo(long a1 = 1337L, int b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n * @param b \n */");

        result = testSetup.SetLine("void foo(long long a1 = 1337LL, int b);").GetResult();
        assert.strictEqual(result, "/**\n * @brief \n * \n * @param a1 \n * @param b \n */");

        result = testSetup.SetLine("void foo(unsigned long long a1 = 1337ULL, int b);").GetResult();
        assert.strictEqual(result, "/**\n * @b
Download .txt
gitextract_4uo1r0wb/

├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── ISSUE_TEMPLATE.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       ├── cd.yml
│       ├── ci.yml
│       └── codeql-analysis.yml
├── .gitignore
├── .nycrc
├── .vscode/
│   ├── launch.json
│   ├── settings.json
│   └── tasks.json
├── .vscodeignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── package.json
├── src/
│   ├── CodeParserController.ts
│   ├── Common/
│   │   ├── ICodeParser.ts
│   │   └── IDocGen.ts
│   ├── Config.ts
│   ├── DoxygenCompletionItemProvider.ts
│   ├── GitConfig.ts
│   ├── Lang/
│   │   └── Cpp/
│   │       ├── CppArgument.ts
│   │       ├── CppDocGen.ts
│   │       ├── CppParseTree.ts
│   │       ├── CppParser.ts
│   │       └── CppToken.ts
│   ├── extension.ts
│   ├── templatedString.ts
│   ├── test/
│   │   ├── CppTests/
│   │   │   ├── Attributes.test.ts
│   │   │   ├── Con-AndDestructor.test.ts
│   │   │   ├── Config.test.ts
│   │   │   ├── FileDescription.test.ts
│   │   │   ├── FunctionPointer.test.ts
│   │   │   ├── Operators.test.ts
│   │   │   ├── Parameters.test.ts
│   │   │   ├── Preprocessor.test.ts
│   │   │   ├── ReturnTypes.test.ts
│   │   │   ├── SmartText.test.ts
│   │   │   ├── Templates.test.ts
│   │   │   ├── TestSetup.ts
│   │   │   └── TrailingReturns.test.ts
│   │   ├── index.ts
│   │   ├── runTests.ts
│   │   └── tools/
│   │       ├── MockDocument.ts
│   │       ├── MockEditor.ts
│   │       ├── MockLine.ts
│   │       ├── MockPosition.ts
│   │       ├── MockSelection.ts
│   │       └── MockTextEditorEdit.ts
│   └── util.ts
├── tsconfig.json
└── tslint.json
Download .txt
SYMBOL INDEX (139 symbols across 23 files)

FILE: src/CodeParserController.ts
  class CodeParserController (line 23) | class CodeParserController {
    method constructor (line 33) | public constructor() {
    method dispose (line 55) | public dispose() {
    method check (line 63) | private check(activeEditor: TextEditor, event: TextDocumentContentChan...
    method onEvent (line 96) | private onEvent(activeEditor: TextEditor, event: TextDocumentContentCh...

FILE: src/Common/ICodeParser.ts
  type ICodeParser (line 4) | interface ICodeParser {

FILE: src/Common/IDocGen.ts
  type IDocGen (line 4) | interface IDocGen {

FILE: src/Config.ts
  class C (line 5) | class C {
    method getConfiguration (line 6) | public static getConfiguration() {
  class Cpp (line 19) | class Cpp {
    method getConfiguration (line 20) | public static getConfiguration() {
  class File (line 29) | class File {
    method getConfiguration (line 30) | public static getConfiguration() {
  class Generic (line 41) | class Generic {
    method getConfiguration (line 42) | public static getConfiguration() {
  class Config (line 66) | class Config {
    method ImportFromSettings (line 67) | public static ImportFromSettings(): Config {
    method constructor (line 124) | constructor() {

FILE: src/DoxygenCompletionItemProvider.ts
  class DoxygenCompletionItemProvider (line 7) | class DoxygenCompletionItemProvider implements vscode.CompletionItemProv...
    method provideCompletionItems (line 133) | public provideCompletionItems(document: vscode.TextDocument, position:...
    method resolveCompletionItem (line 142) | public resolveCompletionItem(item: vscode.CompletionItem, token: vscod...

FILE: src/GitConfig.ts
  class GitConfig (line 4) | class GitConfig {
    method constructor (line 7) | public constructor() {
    method UserName (line 21) | get UserName(): string {
    method UserEmail (line 30) | get UserEmail(): string {

FILE: src/Lang/Cpp/CppArgument.ts
  class CppArgument (line 3) | class CppArgument {

FILE: src/Lang/Cpp/CppDocGen.ts
  type SpecialCase (line 12) | enum SpecialCase {
  type CommentType (line 21) | enum CommentType {
  type CasingType (line 26) | enum CasingType {
  class CppDocGen (line 35) | class CppDocGen implements IDocGen {
    method constructor (line 64) | public constructor(
    method GenerateDoc (line 91) | public GenerateDoc(rangeToReplace: Range, gitConfig: GitConfig) {
    method getSmartText (line 126) | protected getSmartText(): string {
    method generateBrief (line 180) | protected generateBrief(lines: string[]) {
    method generateReturnParams (line 189) | protected generateReturnParams(): string[] {
    method generateAuthorTag (line 224) | protected generateAuthorTag(lines: string[]) {
    method generateFilenameFromTemplate (line 240) | protected generateFilenameFromTemplate(lines: string[]) {
    method generateVersionTag (line 251) | protected generateVersionTag(lines: string[]) {
    method generateCopyrightTag (line 257) | protected generateCopyrightTag(lines: string[]) {
    method generateCommonTag (line 272) | protected generateCommonTag(lines: string[], tags: string) {
    method generateCustomTag (line 304) | protected generateCustomTag(lines: string[], target = CommentType.file) {
    method generateDateFromTemplate (line 335) | protected generateDateFromTemplate(lines: string[]) {
    method insertFirstLine (line 347) | protected insertFirstLine(lines: string[]) {
    method insertBrief (line 353) | protected insertBrief(lines: string[]) {
    method insertLastLine (line 359) | protected insertLastLine(lines: string[]) {
    method generateFileDescription (line 365) | protected generateFileDescription(): string {
    method generateComment (line 391) | protected generateComment(): string {
    method moveCursurToFirstDoxyCommand (line 448) | protected moveCursurToFirstDoxyCommand(comment: string, baseLine: numb...
    method splitCasing (line 469) | protected splitCasing(text: string): string {
    method getAuthorInfo (line 506) | private getAuthorInfo() {

FILE: src/Lang/Cpp/CppParseTree.ts
  class CppParseTree (line 3) | class CppParseTree {
    method CreateTree (line 10) | public static CreateTree(CppTokens: CppToken[], inNested: boolean = fa...
    method Compact (line 43) | public Compact(tree: CppParseTree = this): CppParseTree {
    method Copy (line 68) | public Copy(tree: CppParseTree = this): CppParseTree {
    method Yield (line 79) | public Yield(tree: CppParseTree = this): string {

FILE: src/Lang/Cpp/CppParser.ts
  class CppParser (line 18) | class CppParser implements ICodeParser {
    method checkCasing (line 30) | public static checkCasing(name: string, validateFrom: number): CasingT...
    method constructor (line 116) | constructor(cfg: Config) {
    method Parse (line 315) | public Parse(activeEdit: TextEditor): IDocGen {
    method getLogicalLine (line 385) | private getLogicalLine(): string {
    method Tokenize (line 463) | private Tokenize(expression: string): CppToken[] {
    method GetReturnAndArgs (line 485) | private GetReturnAndArgs(line: string): [CppArgument, CppArgument[]] {
    method RemoveUnusedTokens (line 528) | private RemoveUnusedTokens(tree: CppParseTree): CppParseTree {
    method GetArgumentList (line 545) | private GetArgumentList(tree: CppParseTree): CppParseTree[] {
    method IsFuncPtr (line 578) | private IsFuncPtr(nodes: Array<CppToken | CppParseTree>) {
    method IsArrayPtr (line 582) | private IsArrayPtr(nodes: Array<CppToken | CppParseTree>) {
    method StripNonTypeNodes (line 598) | private StripNonTypeNodes(tree: CppParseTree) {
    method GetArgumentFromCastOperator (line 608) | private GetArgumentFromCastOperator(line: string) {
    method GetArgumentFromTrailingReturn (line 613) | private GetArgumentFromTrailingReturn(tree: CppParseTree, startTrailin...
    method GetArgumentFromFuncPtr (line 653) | private GetArgumentFromFuncPtr(tree: CppParseTree): CppArgument {
    method GetDefaultArgument (line 688) | private GetDefaultArgument(tree: CppParseTree): CppArgument {
    method GetArgument (line 720) | private GetArgument(tree: CppParseTree): CppArgument {
    method GetSubExprStartEnd (line 766) | private GetSubExprStartEnd(expression: string, startSearch: number, op...
    method GetTemplate (line 788) | private GetTemplate(expression: string): string {
    method GetArgsFromTemplate (line 811) | private GetArgsFromTemplate(template: string): string[] {
    method isVsCodeAutoComplete (line 859) | private isVsCodeAutoComplete(line: string): boolean {

FILE: src/Lang/Cpp/CppToken.ts
  type CppTokenType (line 1) | enum CppTokenType {
  class CppToken (line 19) | class CppToken {
    method constructor (line 23) | constructor(type: CppTokenType, value: string) {

FILE: src/extension.ts
  type Version (line 8) | enum Version {
  function activate (line 16) | function activate(context: vscode.ExtensionContext) {

FILE: src/templatedString.ts
  type ITemplate (line 6) | interface ITemplate {
  function getIndentedTemplate (line 11) | function getIndentedTemplate(replace: string): string {
  function getTemplatedString (line 42) | function getTemplatedString(original: string, template: ITemplate): stri...
  function generateFromTemplate (line 55) | function generateFromTemplate(
  function getMultiTemplatedString (line 75) | function getMultiTemplatedString(

FILE: src/test/CppTests/TestSetup.ts
  class TestSetup (line 15) | class TestSetup {
    method constructor (line 21) | constructor(method: string) {
    method SetLine (line 28) | public SetLine(method: string): TestSetup {
    method SetLines (line 32) | public SetLines(lines: string[], addIndent = true): TestSetup {
    method GetResult (line 58) | public GetResult(): string {

FILE: src/test/index.ts
  function setupNyc (line 5) | function setupNyc() {
  function run (line 15) | function run(): Promise<void> {

FILE: src/test/runTests.ts
  function main (line 5) | async function main() {

FILE: src/test/tools/MockDocument.ts
  class MockDocument (line 4) | class MockDocument implements vscode.TextDocument {
    method constructor (line 15) | public constructor(lines: vscode.TextLine[]) {
    method save (line 19) | public save(): Thenable<boolean> {
    method lineAt (line 23) | public lineAt(position: any): any {
    method offsetAt (line 29) | public offsetAt(position: vscode.Position): number {
    method positionAt (line 32) | public positionAt(offset: number): vscode.Position {
    method getText (line 35) | public getText(range?: vscode.Range): string {
    method getWordRangeAtPosition (line 38) | public getWordRangeAtPosition(position: vscode.Position, regex?: RegEx...
    method validateRange (line 41) | public validateRange(range: vscode.Range): vscode.Range {
    method validatePosition (line 44) | public validatePosition(position: vscode.Position): vscode.Position {

FILE: src/test/tools/MockEditor.ts
  class MockEditor (line 7) | class MockEditor implements TextEditor {
    method constructor (line 15) | public constructor(s: MockSelection, d: MockDocument) {
    method edit (line 21) | public edit(callback: (editBuilder: vscode.TextEditorEdit) => void,
    method insertSnippet (line 26) | public insertSnippet(snippet: vscode.SnippetString,
    method setDecorations (line 31) | public setDecorations(decorationType: vscode.TextEditorDecorationType,
    method revealRange (line 35) | public revealRange(range: vscode.Range, revealType?: vscode.TextEditor...
    method show (line 38) | public show(column?: vscode.ViewColumn): void {
    method hide (line 41) | public hide(): void {

FILE: src/test/tools/MockLine.ts
  class MockLine (line 3) | class MockLine implements vscode.TextLine {
    method constructor (line 10) | public constructor(text: string) {

FILE: src/test/tools/MockPosition.ts
  class MockPosition (line 3) | class MockPosition implements vscode.Position {
    method constructor (line 6) | public constructor(l: number, c: number) {
    method isBefore (line 10) | public isBefore(other: vscode.Position): boolean {
    method isBeforeOrEqual (line 13) | public isBeforeOrEqual(other: vscode.Position): boolean {
    method isAfter (line 16) | public isAfter(other: vscode.Position): boolean {
    method isAfterOrEqual (line 19) | public isAfterOrEqual(other: vscode.Position): boolean {
    method isEqual (line 22) | public isEqual(other: vscode.Position): boolean {
    method compareTo (line 25) | public compareTo(other: vscode.Position): number {
    method translate (line 30) | public translate(lineDelta?: any, characterDelta?: any): any {
    method with (line 35) | public with(line?: any, character?: any): any {

FILE: src/test/tools/MockSelection.ts
  class MockSelection (line 4) | class MockSelection implements vscode.Selection {
    method constructor (line 13) | public constructor(a: MockPosition) {
    method contains (line 18) | public contains(positionOrRange: vscode.Range | vscode.Position): bool...
    method isEqual (line 21) | public isEqual(other: vscode.Range): boolean {
    method intersection (line 24) | public intersection(range: vscode.Range): vscode.Range {
    method union (line 27) | public union(other: vscode.Range): vscode.Range {
    method with (line 32) | public with(start?: any, end?: any): any {

FILE: src/test/tools/MockTextEditorEdit.ts
  class MockTextEditorEdit (line 3) | class MockTextEditorEdit implements vscode.TextEditorEdit {
    method replace (line 5) | public replace(location: vscode.Position | vscode.Range | vscode.Selec...
    method insert (line 8) | public insert(location: vscode.Position, value: string): void {
    method delete (line 11) | public delete(location: vscode.Range | vscode.Selection): void {
    method setEndOfLine (line 14) | public setEndOfLine(endOfLine: vscode.EndOfLine): void {

FILE: src/util.ts
  function inComment (line 10) | function inComment(activeEditor: vscode.TextEditor, activeLine: number):...
  function getIndentation (line 27) | function getIndentation(editor: vscode.TextEditor = vscode.window.active...
  function getEnvVars (line 36) | function getEnvVars(replace: string): string {
Condensed preview — 54 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (246K chars).
[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 665,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\n**Describe the "
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 604,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: enhancement\nassignees: ''\n\n---\n\n**Is"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "chars": 263,
    "preview": "# Description\n\nAnything you'd like to add\n\n## Code example\n\n```Cpp\n// Put your code here\n```\n\n### Expected result\n\n```Cp"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 345,
    "preview": "# Fix/Feature/Other\n\n## Fix\n\n- [ ] Link to issue. If there is no issue please describe it using at least the issue templ"
  },
  {
    "path": ".github/workflows/cd.yml",
    "chars": 1297,
    "preview": "name: Release\n\non:\n  push:\n    branches:\n      - master\n  pull_request:\n    branches:\n      - master\n  release:\n    type"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 1770,
    "preview": "name: CI\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\njobs:\n  lint:\n    runs-on: ubun"
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "chars": 2440,
    "preview": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# Y"
  },
  {
    "path": ".gitignore",
    "chars": 104,
    "preview": "out\nnode_modules\n.vscode-test/\n*.vsix\ncoverage/\n.DS_Store\n/npm-debug.log\n/.nyc_output/\npackage-lock.json"
  },
  {
    "path": ".nycrc",
    "chars": 527,
    "preview": "{\n    \"extends\": \"@istanbuljs/nyc-config-typescript\",\n    \"cache\": false,\n    \"cwd\": \"./\",\n    \"exclude\": [\n        \"**/"
  },
  {
    "path": ".vscode/launch.json",
    "chars": 1062,
    "preview": "// A launch configuration that compiles the extension and then opens it inside a new window\n{\n    \"version\": \"0.1.0\",\n  "
  },
  {
    "path": ".vscode/settings.json",
    "chars": 315,
    "preview": "// Place your settings in this file to overwrite default and user settings.\n{\n    \"files.exclude\": {\n        \"out\": fals"
  },
  {
    "path": ".vscode/tasks.json",
    "chars": 494,
    "preview": "// See https://go.microsoft.com/fwlink/?LinkId=733558\n// for the documentation about the tasks.json format\n{\n    \"versio"
  },
  {
    "path": ".vscodeignore",
    "chars": 184,
    "preview": ".vscode/**\n.vscode-test/**\nout/test/**\nout/**/*.map\nsrc/**\n.gitignore\ntsconfig.json\nvsc-extension-quickstart.md\n.travis."
  },
  {
    "path": "CHANGELOG.md",
    "chars": 7878,
    "preview": "# Change Log\n\n## [1.4.0]\n\n### Feature\n\n- Use `vscode.workspace.workspaceFolders[0]` to construct SimpleGit instance (#26"
  },
  {
    "path": "LICENSE",
    "chars": 1091,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2017-2021 Christoph Schlosser\n\nPermission is hereby granted, free of charge, to any"
  },
  {
    "path": "README.md",
    "chars": 8600,
    "preview": "# Generate Doxygen Comments in VS Code\n\nThis VS Code Extensions provides Doxygen Documentation generation on the fly by "
  },
  {
    "path": "package.json",
    "chars": 11429,
    "preview": "{\n  \"name\": \"doxdocgen\",\n  \"displayName\": \"Doxygen Documentation Generator\",\n  \"description\": \"Let me generate Doxygen d"
  },
  {
    "path": "src/CodeParserController.ts",
    "chars": 4382,
    "preview": "import {\n    Disposable,\n    Position,\n    Range,\n    TextDocumentContentChangeEvent,\n    TextEditor,\n    TextLine,\n    "
  },
  {
    "path": "src/Common/ICodeParser.ts",
    "chars": 268,
    "preview": "import { TextEditor } from \"vscode\";\nimport { IDocGen } from \"./IDocGen\";\n\nexport default interface ICodeParser {\n    /*"
  },
  {
    "path": "src/Common/IDocGen.ts",
    "chars": 338,
    "preview": "import { Range } from \"vscode\";\nimport GitConfig from \"../GitConfig\";\n\nexport interface IDocGen {\n    /**\n     * @brief "
  },
  {
    "path": "src/Config.ts",
    "chars": 7069,
    "preview": "import { workspace } from \"vscode\";\n// tslint:disable:max-classes-per-file\n// tslint:disable:max-line-length\n\nclass C {\n"
  },
  {
    "path": "src/DoxygenCompletionItemProvider.ts",
    "chars": 28306,
    "preview": "import * as vscode from \"vscode\";\nimport { inComment } from \"./util\";\n\n// tslint:disable:max-line-length\n\n/*https://gith"
  },
  {
    "path": "src/GitConfig.ts",
    "chars": 903,
    "preview": "import simpleGit, { ConfigValues, SimpleGit } from \"simple-git\";\nimport { workspace } from \"vscode\";\n\nexport default cla"
  },
  {
    "path": "src/Lang/Cpp/CppArgument.ts",
    "chars": 162,
    "preview": "import { CppParseTree } from \"./CppParseTree\";\n\nexport class CppArgument {\n    public name: string = null;\n    public ty"
  },
  {
    "path": "src/Lang/Cpp/CppDocGen.ts",
    "chars": 18789,
    "preview": "import * as moment from \"moment\";\nimport { Position, Range, Selection, TextEditor } from \"vscode\";\nimport { IDocGen } fr"
  },
  {
    "path": "src/Lang/Cpp/CppParseTree.ts",
    "chars": 4340,
    "preview": "import { CppToken, CppTokenType } from \"./CppToken\";\n\nexport class CppParseTree {\n\n    /**\n     * Create a tree from Cpp"
  },
  {
    "path": "src/Lang/Cpp/CppParser.ts",
    "chars": 34021,
    "preview": "import { Position, TextDocumentContentChangeEvent, TextEditor, TextLine, workspace } from \"vscode\";\nimport ICodeParser f"
  },
  {
    "path": "src/Lang/Cpp/CppToken.ts",
    "chars": 467,
    "preview": "export enum CppTokenType {\n    Symbol,\n    Pointer,\n    Reference,\n    ArraySubscript,\n    OpenParenthesis,\n    ClosePar"
  },
  {
    "path": "src/extension.ts",
    "chars": 1808,
    "preview": "\"use strict\";\n// The module 'vscode' contains the VS Code extensibility API\n// Import the module and reference it with t"
  },
  {
    "path": "src/templatedString.ts",
    "chars": 2743,
    "preview": "import { getEnvVars } from \"./util\";\n\n/**\n * Represent a templated variable in string\n */\nexport interface ITemplate {\n "
  },
  {
    "path": "src/test/CppTests/Attributes.test.ts",
    "chars": 3224,
    "preview": "//\n// Note: This example test is leveraging the Mocha test framework.\n// Please refer to their documentation on https://"
  },
  {
    "path": "src/test/CppTests/Con-AndDestructor.test.ts",
    "chars": 2425,
    "preview": "//\n// Note: This example test is leveraging the Mocha test framework.\n// Please refer to their documentation on https://"
  },
  {
    "path": "src/test/CppTests/Config.test.ts",
    "chars": 15243,
    "preview": "//\n// Note: This example test is leveraging the Mocha test framework.\n// Please refer to their documentation on https://"
  },
  {
    "path": "src/test/CppTests/FileDescription.test.ts",
    "chars": 3591,
    "preview": "//\n// Note: This example test is leveraging the Mocha test framework.\n// Please refer to their documentation on https://"
  },
  {
    "path": "src/test/CppTests/FunctionPointer.test.ts",
    "chars": 2376,
    "preview": "//\n// Note: This example test is leveraging the Mocha test framework.\n// Please refer to their documentation on https://"
  },
  {
    "path": "src/test/CppTests/Operators.test.ts",
    "chars": 14156,
    "preview": "//\n// Note: This example test is leveraging the Mocha test framework.\n// Please refer to their documentation on https://"
  },
  {
    "path": "src/test/CppTests/Parameters.test.ts",
    "chars": 15239,
    "preview": "//\n// Note: This example test is leveraging the Mocha test framework.\n// Please refer to their documentation on https://"
  },
  {
    "path": "src/test/CppTests/Preprocessor.test.ts",
    "chars": 1745,
    "preview": "//\n// Note: This example test is leveraging the Mocha test framework.\n// Please refer to their documentation on https://"
  },
  {
    "path": "src/test/CppTests/ReturnTypes.test.ts",
    "chars": 6865,
    "preview": "//\n// Note: This example test is leveraging the Mocha test framework.\n// Please refer to their documentation on https://"
  },
  {
    "path": "src/test/CppTests/SmartText.test.ts",
    "chars": 6005,
    "preview": "//\n// Note: This example test is leveraging the Mocha test framework.\n// Please refer to their documentation on https://"
  },
  {
    "path": "src/test/CppTests/Templates.test.ts",
    "chars": 2720,
    "preview": "//\n// Note: This example test is leveraging the Mocha test framework.\n// Please refer to their documentation on https://"
  },
  {
    "path": "src/test/CppTests/TestSetup.ts",
    "chars": 2273,
    "preview": "import * as vscode from \"vscode\";\n\nimport CodeParser from \"../../Common/ICodeParser\";\nimport { IDocGen } from \"../../Com"
  },
  {
    "path": "src/test/CppTests/TrailingReturns.test.ts",
    "chars": 1737,
    "preview": "//\n// Note: This example test is leveraging the Mocha test framework.\n// Please refer to their documentation on https://"
  },
  {
    "path": "src/test/index.ts",
    "chars": 1113,
    "preview": "import * as glob from \"glob\";\nimport * as Mocha from \"mocha\";\nimport * as path from \"path\";\n\nfunction setupNyc() {\n    c"
  },
  {
    "path": "src/test/runTests.ts",
    "chars": 864,
    "preview": "import * as path from \"path\";\n\nimport { runTests } from \"vscode-test\";\n\nasync function main() {\n  try {\n    // The folde"
  },
  {
    "path": "src/test/tools/MockDocument.ts",
    "chars": 1659,
    "preview": "import * as vscode from \"vscode\";\nimport MockLine from \"./MockLine\";\n\nexport default class MockDocument implements vscod"
  },
  {
    "path": "src/test/tools/MockEditor.ts",
    "chars": 1914,
    "preview": "import * as vscode from \"vscode\";\nimport { Range, TextEditor } from \"vscode\";\nimport MockDocument from \"./MockDocument\";"
  },
  {
    "path": "src/test/tools/MockLine.ts",
    "chars": 399,
    "preview": "import * as vscode from \"vscode\";\n\nexport default class MockLine implements vscode.TextLine {\n    public lineNumber: num"
  },
  {
    "path": "src/test/tools/MockPosition.ts",
    "chars": 1516,
    "preview": "import * as vscode from \"vscode\";\n\nexport default class MockPosition implements vscode.Position {\n    public line: numbe"
  },
  {
    "path": "src/test/tools/MockSelection.ts",
    "chars": 1233,
    "preview": "import * as vscode from \"vscode\";\nimport MockPosition from \"./MockPosition\";\n\nexport default class MockSelection impleme"
  },
  {
    "path": "src/test/tools/MockTextEditorEdit.ts",
    "chars": 646,
    "preview": "import * as vscode from \"vscode\";\n\nexport default class MockTextEditorEdit implements vscode.TextEditorEdit {\n    public"
  },
  {
    "path": "src/util.ts",
    "chars": 1731,
    "preview": "import * as env from \"env-var\";\nimport * as vscode from \"vscode\";\n\n/**\n * Check if a specific line will be inside a comm"
  },
  {
    "path": "tsconfig.json",
    "chars": 283,
    "preview": "{\n    \"compilerOptions\": {\n        \"module\": \"commonjs\",\n        \"target\": \"es6\",\n        \"outDir\": \"out\",\n        \"lib\""
  },
  {
    "path": "tslint.json",
    "chars": 149,
    "preview": "{\n    \"defaultSeverity\": \"error\",\n    \"extends\": [\n        \"tslint:recommended\"\n    ],\n    \"jsRules\": {},\n    \"rules\": {"
  }
]

About this extraction

This page contains the full source code of the cschlosser/doxdocgen GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 54 files (226.3 KB), approximately 56.9k tokens, and a symbol index with 139 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!