├── .DS_Store ├── .gitignore ├── LICENSE ├── README.md ├── app.go ├── build ├── .DS_Store ├── README.md ├── appicon.png ├── darwin │ └── Info.plist └── windows │ ├── code-counter.exe.manifest │ ├── icon.ico │ ├── info.json │ ├── installer │ ├── project.nsi │ └── wails_tools.nsh │ └── wails.exe.manifest ├── count ├── countCode.go ├── countFile.go ├── countFile_test.go ├── defaultConfig.go ├── readDir.go └── utils.go ├── frontend ├── .DS_Store ├── dist │ └── .gitkeep ├── index.html ├── package-lock.json ├── package.json ├── public │ └── .gitkeep ├── src │ ├── .DS_Store │ ├── App.vue │ ├── assets │ │ ├── github.png │ │ └── logo.png │ ├── components │ │ ├── CItem.vue │ │ └── CModal.vue │ ├── main.js │ ├── router │ │ └── index.js │ ├── views │ │ ├── AddFile.vue │ │ ├── Home.vue │ │ └── ShowResult.vue │ └── wailsjs │ │ ├── go │ │ └── models.ts │ │ └── runtime │ │ ├── package.json │ │ ├── runtime.d.ts │ │ └── runtime.js └── vite.config.js ├── go.mod ├── go.sum ├── main.go ├── screenshot ├── .DS_Store ├── screenshot1.png ├── screenshot2.png └── screenshot3.png ├── scripts ├── build-macos-arm.sh ├── build-macos-intel.sh ├── build-macos.sh ├── build-windows.sh ├── build.sh └── install-wails-cli.sh └── wails.json /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanchenchen/CodeCounter/56a6e9a4697231e1b4c5aedb17e2507fd74d661e/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Wails bin directory 2 | build/bin 3 | 4 | # IDEs 5 | .idea 6 | .vscode 7 | .DS_Store 8 | 9 | # fronted resources 10 | frontend/node_modules 11 | frontend/src/wailsjs 12 | frontend/dist/index.html 13 | frontend/dist/assets 14 | frontend/package.json.md5 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Alan Chen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Code Counter 2 | 3 | > An application to count code in wails(golang) 4 | 5 | > version: 2.0.1 6 | 7 | > Author: Alan Chen 8 | 9 | > Technology stack: Vue2 + Vite + iview + Golang + Wails v2 10 | 11 | `electron版本见branch v1` 12 | 13 | ### Features 14 | * 采用wails v2包生成app、exe可执行文件,方便离线使用。 15 | * wails使用系统自带webview,而不是electron内置,软件打包后安装包2~3M,比内置chromium方案磁盘占用、内存占用率优秀很多 16 | * golang的go routine在读取文件和cpu计算会充分利用多核处理器,处理速度更快 17 | * 支持文件夹读取文件,自定义统计哪个文件夹下代码数量 18 | * 支持文件(夹)过滤,支持对应后缀名文件过滤 19 | * 支持对应后缀名文件添加自定义注释规则,目前只自带很小一部分代码的默认注释规则 20 | 21 | ![](./screenshot/screenshot1.png) 22 | ![](./screenshot/screenshot2.png) 23 | ![](./screenshot/screenshot3.png) 24 | 25 | ### Usage Help 26 | 1. 点击即可读取文件夹,目前仅支持统计一个文件夹,再次添加会覆盖 27 | 2. 过滤功能只对文件夹生效,在弹窗中按enter添加,注意添加后缀名必须带上点 28 | 3. 注释规则文件名不需要带点,规则分为多行注释和单行注释,非必填,一个后缀名文件可以添加多个规则,但不可重复。 29 | 4. 软件默认过滤`node_modules`和`.git`两个文件夹。 30 | 5. 软件默认提供js、ts、go、jsx、tsx、vue、svelte、html、css、java、vue、c和cpp等后缀名文件的注释规则 31 | 6. 读取代码的原理其实是将目标文件的buffer转换成utf-8的string,所以只支持uft-8编码的文件,。大多数code的编码格式是utf-8。 32 | 33 | ### Download 34 | > 见[release](https://github.com/alanchenchen/CodeCounter/releases) 35 | 36 | ### Directory Tree 37 | ``` bash 38 | ├─build 编译平台相关的配置文件、可执行程序 39 | ├─count code counter的主要go module 40 | ├─frontend wails展示的前端静态资源,wails不强关联前端框架和构建工具 41 | │ ├─src 42 | │ └─wailsjs wails在加载静态资源时自动生成的方法bindings,见wails文档 43 | │ ├─index.html 44 | │ ├─package.json 45 | │ └─vite.config.js vite config配置 46 | ├─screenshot 47 | ├─scripts wails的常用命令组合的shell脚本,见wails文档 48 | ├─app.go wails程序的app结构体,主要用于绑定go方法到js运行时,见wails文档 49 | ├─main.go wails程序的入口,初始化,见wails文档 50 | └─wails.json wails cli打包程序需要的配置,见wails文档 51 | ``` 52 | 53 | ### Development Setup 54 | #### Required dependencies 55 | - Go 1.17+ 56 | - wails v2 beta+ 57 | - NPM (Node 15+) 58 | > 详细见[wails文档](https://wails.io/zh-Hans/docs/gettingstarted/installation) 59 | 60 | #### Dev liveload 61 | 项目逻辑分为前端和后端 62 | - 前端可以使用任意框架,兼容性不用过于考虑,因为windows平台使用的webview2(和chromium一致),前端使用go绑定方法也十分简单,都是挂载windows对象 63 | - 后端的go方法可以使用任意go module,当需要绑定给js运行时,只需要在`app.go`里扩展App struct的接收器方法即可 64 | - 如果需要在程序运行中使用前端项目的hot load热更新,必须要先打开一个终端运行`wails dev`,然后另一个终端运行`npm run dev` 65 | - wails读取前端静态资源的策略比较奇怪,所以不建议使用wails的frontend构建 66 | 67 | #### Build 68 | ```bash 69 | # 使用upx压缩打包 70 | $ wails build --upx 71 | ``` 72 | - target是mac平台 73 | - 本机必须是mac系统,可以同时编译amd64、arm64架构 74 | - target是windows平台 75 | - 只能编译windows,可以同时编译amd64、arm64架构 76 | 77 | - 所有平台均可使用upx来压缩,压缩比例非常强 78 | - mac平台编译完的是app文件,windows默认编译完的是exe,所有平台均可使用nsis来打包exe 79 | - wails使用的方案是系统自带的webview,目前macos/linux主流版本均内置,windows平台使用的webview2只在部分win10和正式win11内置,所以当你的windwos系统中不存在webview2时,程序启动后会引导你安装,大概118M左右 80 | -------------------------------------------------------------------------------- /app.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "code-counter/count" 5 | "context" 6 | "os" 7 | "path/filepath" 8 | 9 | "github.com/wailsapp/wails/v2/pkg/runtime" 10 | ) 11 | 12 | // App application struct 13 | type App struct { 14 | ctx context.Context 15 | } 16 | 17 | type CustomCommentRules map[string][]count.CommentRule 18 | 19 | // NewApp creates a new App application struct 20 | func NewApp() *App { 21 | return &App{} 22 | } 23 | 24 | // ---- app lifeCycle ---- 25 | 26 | // startup is called at application startup 27 | func (a *App) startup(ctx context.Context) { 28 | // Perform your setup here 29 | a.ctx = ctx 30 | } 31 | 32 | // domReady is called after the front-end dom has been loaded 33 | func (a *App) domReady(ctx context.Context) { 34 | // Add your action here 35 | runtime.WindowShow(a.ctx) 36 | } 37 | 38 | // beforeClose is called when the application is about to quit, 39 | // either by clicking the window close button or calling runtime.Quit. 40 | // Returning true will cause the application to continue, 41 | // false will continue shutdown as normal. 42 | func (a *App) beforeClose(ctx context.Context) (prevent bool) { 43 | return false 44 | } 45 | 46 | // shutdown is called at application termination 47 | func (a *App) shutdown(ctx context.Context) { 48 | // Perform your teardown here 49 | } 50 | 51 | // ---- app native go methods bindings to js ---- 52 | 53 | // Quit will quit the application 54 | func (a *App) Quit() { 55 | runtime.Quit(a.ctx) 56 | } 57 | 58 | // ReadCodeLinesByDirFiles calculate code counter from dir, return counter data 59 | func (a *App) ReadCodeLinesByDirFiles(dir string, exclude []string, customRules CustomCommentRules) ([]count.FileCodeCounter, error) { 60 | // 合并customRules到count.DefaultRuleConfig,customRules优先级更高 61 | // 必须初始化一个map,而不是直接赋值count.DefaultRuleConfig 62 | // map是指针传递,这样会改变count.DefaultRuleConfig原值 63 | fileRules := make(CustomCommentRules) 64 | for k, v := range count.DefaultRuleConfig { 65 | fileRules[k] = v 66 | } 67 | for k, v := range customRules { 68 | fileRules[k] = v 69 | } 70 | // 合并exclude到count.ExcludePaths 71 | excludeList := append(count.ExcludePaths, exclude...) 72 | 73 | lineCounters, err := count.ReadCodeLinesByDirFiles(dir, excludeList, fileRules) 74 | 75 | res := make([]count.FileCodeCounter, 0) 76 | for countData := range lineCounters { 77 | res = append(res, countData) 78 | } 79 | return res, err 80 | } 81 | 82 | // OpenDialog show a system dialog to choose a file directory 83 | func (a *App) OpenDirectoryDialog(dialogOptions runtime.OpenDialogOptions) (string, error) { 84 | return runtime.OpenDirectoryDialog(a.ctx, dialogOptions) 85 | } 86 | 87 | // SaveFileDialog show a system dialog to choose a saving file path 88 | func (a *App) SaveFileDialog(dialogOptions runtime.SaveDialogOptions) (string, error) { 89 | return runtime.SaveFileDialog(a.ctx, dialogOptions) 90 | } 91 | 92 | // WriteFile write string data to path 93 | func (a *App) WriteFile(path string, data string) error { 94 | // 创建文件 95 | file, err := os.Create(path) 96 | if err != nil { 97 | return err 98 | } 99 | defer file.Close() 100 | // 写入buf到文件 101 | _, writeErr := file.Write([]byte(data)) 102 | if writeErr != nil { 103 | return writeErr 104 | } 105 | return nil 106 | } 107 | 108 | // Extname return path ext name 109 | func (a *App) Extname(path string) string { 110 | return filepath.Ext(path) 111 | } 112 | -------------------------------------------------------------------------------- /build/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanchenchen/CodeCounter/56a6e9a4697231e1b4c5aedb17e2507fd74d661e/build/.DS_Store -------------------------------------------------------------------------------- /build/README.md: -------------------------------------------------------------------------------- 1 | # Build Directory 2 | 3 | The build directory is used to house all the build files and assets for your application. 4 | 5 | The structure is: 6 | 7 | * bin - Output directory 8 | * dialog - Icons for dialogs 9 | * tray - Icons for the system tray 10 | * mac - MacOS specific files 11 | * linux - Linux specific files 12 | * windows - Windows specific files 13 | 14 | ## Dialog Icons 15 | 16 | Place any PNG file in this directory to be able to use them in message dialogs. 17 | The files should have names in the following format: `name[-(light|dark)][2x].png` 18 | 19 | Examples: 20 | 21 | * `mypic.png` - Standard definition icon with ID `mypic` 22 | * `mypic-light.png` - Standard definition icon with ID `mypic`, used when system theme is light 23 | * `mypic-dark.png` - Standard definition icon with ID `mypic`, used when system theme is dark 24 | * `mypic2x.png` - High definition icon with ID `mypic` 25 | * `mypic-light2x.png` - High definition icon with ID `mypic`, used when system theme is light 26 | * `mypic-dark2x.png` - High definition icon with ID `mypic`, used when system theme is dark 27 | 28 | ### Order of preference 29 | 30 | Icons are selected with the following order of preference: 31 | 32 | For High Definition displays: 33 | * name-(theme)2x.png 34 | * name2x.png 35 | * name-(theme).png 36 | * name.png 37 | 38 | For Standard Definition displays: 39 | * name-(theme).png 40 | * name.png 41 | 42 | ## Tray 43 | 44 | Place any PNG file in this directory to be able to use them as tray icons. 45 | The name of the filename will be the ID to reference the image. 46 | 47 | Example: 48 | 49 | * `mypic.png` - May be referenced using `runtime.Tray.SetIcon("mypic")` 50 | 51 | ## Mac 52 | 53 | The `darwin` directory holds files specific to Mac builds, such as `Info.plist`. 54 | These may be customised and used as part of the build. To return these files to the default state, simply delete them and 55 | build with the `-package` flag. 56 | 57 | ## Windows 58 | 59 | The `windows` directory contains the manifest and rc files used when building with the `-package` flag. 60 | These may be customised for your application. To return these files to the default state, simply delete them and 61 | build with the `-package` flag. -------------------------------------------------------------------------------- /build/appicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanchenchen/CodeCounter/56a6e9a4697231e1b4c5aedb17e2507fd74d661e/build/appicon.png -------------------------------------------------------------------------------- /build/darwin/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | CFBundlePackageTypeAPPL 4 | CFBundleName{{.Info.ProductName}} 5 | CFBundleExecutable{{.Name}} 6 | CFBundleIdentifiercom.alanchenchen.{{.Name}} 7 | CFBundleVersion{{.Info.ProductVersion}} 8 | CFBundleGetInfoString{{.Info.Comments}} 9 | CFBundleShortVersionString{{.Info.ProductVersion}} 10 | CFBundleIconFileiconfile 11 | LSMinimumSystemVersion10.13.0 12 | NSHighResolutionCapabletrue 13 | NSHumanReadableCopyright{{.Info.Copyright}} 14 | -------------------------------------------------------------------------------- /build/windows/code-counter.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | true/pm 12 | permonitorv2,permonitor 13 | 14 | 15 | -------------------------------------------------------------------------------- /build/windows/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanchenchen/CodeCounter/56a6e9a4697231e1b4c5aedb17e2507fd74d661e/build/windows/icon.ico -------------------------------------------------------------------------------- /build/windows/info.json: -------------------------------------------------------------------------------- 1 | { 2 | "fixed": { 3 | "file_version": "{{.Info.ProductVersion}}" 4 | }, 5 | "info": { 6 | "0000": { 7 | "ProductVersion": "{{.Info.ProductVersion}}", 8 | "CompanyName": "{{.Info.CompanyName}}", 9 | "FileDescription": "{{.Info.ProductName}}", 10 | "LegalCopyright": "{{.Info.Copyright}}", 11 | "ProductName": "{{.Info.ProductName}}", 12 | "Comments": "{{.Info.Comments}}" 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /build/windows/installer/project.nsi: -------------------------------------------------------------------------------- 1 | Unicode true 2 | 3 | #### 4 | ## Please note: Template replacements don't work in this file. They are provided with default defines like 5 | ## mentioned underneath. 6 | ## If the keyword is not defined, "wails_tools.nsh" will populate them with the values from ProjectInfo. 7 | ## If they are defined here, "wails_tools.nsh" will not touch them. This allows to use this project.nsi manually 8 | ## from outside of Wails for debugging and development of the installer. 9 | ## 10 | ## For development first make a wails nsis build to populate the "wails_tools.nsh": 11 | ## > wails build --target windows/amd64 --nsis 12 | ## Then you can call makensis on this file with specifying the path to your binary: 13 | ## For a AMD64 only installer: 14 | ## > makensis -DARG_WAILS_AMD64_BINARY=../../bin/app.exe 15 | ## For a ARM64 only installer: 16 | ## > makensis -DARG_WAILS_ARM64_BINARY=../../bin/app.exe 17 | ## For a installer with both architectures: 18 | ## > makensis -DARG_WAILS_AMD64_BINARY=../../bin/app-amd64.exe -DARG_WAILS_ARM64_BINARY=../../bin/app-arm64.exe 19 | #### 20 | ## The following information is taken from the ProjectInfo file, but they can be overwritten here. 21 | #### 22 | ## !define INFO_PROJECTNAME "MyProject" # Default "{{.Name}}" 23 | ## !define INFO_COMPANYNAME "MyCompany" # Default "{{.Info.CompanyName}}" 24 | ## !define INFO_PRODUCTNAME "MyProduct" # Default "{{.Info.ProductName}}" 25 | ## !define INFO_PRODUCTVERSION "1.0.0" # Default "{{.Info.ProductVersion}}" 26 | ## !define INFO_COPYRIGHT "Copyright" # Default "{{.Info.Copyright}}" 27 | ### 28 | ## !define PRODUCT_EXECUTABLE "Application.exe" # Default "${INFO_PROJECTNAME}.exe" 29 | ## !define UNINST_KEY_NAME "UninstKeyInRegistry" # Default "${INFO_COMPANYNAME}${INFO_PRODUCTNAME}" 30 | #### 31 | ## !define REQUEST_EXECUTION_LEVEL "admin" # Default "admin" see also https://nsis.sourceforge.io/Docs/Chapter4.html 32 | #### 33 | ## Include the wails tools 34 | #### 35 | !include "wails_tools.nsh" 36 | 37 | # The version information for this two must consist of 4 parts 38 | VIProductVersion "${INFO_PRODUCTVERSION}.0" 39 | VIFileVersion "${INFO_PRODUCTVERSION}.0" 40 | 41 | VIAddVersionKey "CompanyName" "${INFO_COMPANYNAME}" 42 | VIAddVersionKey "FileDescription" "${INFO_PRODUCTNAME} Installer" 43 | VIAddVersionKey "ProductVersion" "${INFO_PRODUCTVERSION}" 44 | VIAddVersionKey "FileVersion" "${INFO_PRODUCTVERSION}" 45 | VIAddVersionKey "LegalCopyright" "${INFO_COPYRIGHT}" 46 | VIAddVersionKey "ProductName" "${INFO_PRODUCTNAME}" 47 | 48 | !include "MUI.nsh" 49 | 50 | !define MUI_ICON "../icon.ico" 51 | !define MUI_UNICON "../icon.ico" 52 | # !define MUI_WELCOMEFINISHPAGE_BITMAP "resources/leftimage.bmp" #Include this to add a bitmap on the left side of the Welcome Page. Must be a size of 164x314 53 | !define MUI_FINISHPAGE_NOAUTOCLOSE # Wait on the INSTFILES page so the user can take a look into the details of the installation steps 54 | !define MUI_ABORTWARNING # This will warn the user if they exit from the installer. 55 | 56 | !insertmacro MUI_PAGE_WELCOME # Welcome to the installer page. 57 | # !insertmacro MUI_PAGE_LICENSE "resources/eula.txt" # Adds a EULA page to the installer 58 | !insertmacro MUI_PAGE_DIRECTORY # In which folder install page. 59 | !insertmacro MUI_PAGE_INSTFILES # Installing page. 60 | !insertmacro MUI_PAGE_FINISH # Finished installation page. 61 | 62 | !insertmacro MUI_UNPAGE_INSTFILES # Uinstalling page 63 | 64 | !insertmacro MUI_LANGUAGE "English" # Set the Language of the installer 65 | 66 | ## The following two statements can be used to sign the installer and the uninstaller. The path to the binaries are provided in %1 67 | #!uninstfinalize 'signtool --file "%1"' 68 | #!finalize 'signtool --file "%1"' 69 | 70 | Name "${INFO_PRODUCTNAME}" 71 | OutFile "../../bin/${INFO_PROJECTNAME}-${ARCH}-installer.exe" # Name of the installer's file. 72 | InstallDir "$PROGRAMFILES64\${INFO_COMPANYNAME}\${INFO_PRODUCTNAME}" # Default installing folder ($PROGRAMFILES is Program Files folder). 73 | ShowInstDetails show # This will always show the installation details. 74 | 75 | Function .onInit 76 | !insertmacro wails.checkArchitecture 77 | FunctionEnd 78 | 79 | Section 80 | !insertmacro wails.webview2runtime 81 | 82 | SetOutPath $INSTDIR 83 | 84 | !insertmacro wails.files 85 | 86 | CreateShortcut "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}" 87 | CreateShortCut "$DESKTOP\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}" 88 | 89 | !insertmacro wails.writeUninstaller 90 | SectionEnd 91 | 92 | Section "uninstall" 93 | RMDir /r "$AppData\${PRODUCT_EXECUTABLE}" # Remove the WebView2 DataPath 94 | 95 | RMDir /r $INSTDIR 96 | 97 | Delete "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" 98 | Delete "$DESKTOP\${INFO_PRODUCTNAME}.lnk" 99 | 100 | !insertmacro wails.deleteUninstaller 101 | SectionEnd 102 | -------------------------------------------------------------------------------- /build/windows/installer/wails_tools.nsh: -------------------------------------------------------------------------------- 1 | # DO NOT EDIT - Generated automatically by `wails build` 2 | 3 | !include "x64.nsh" 4 | !include "WinVer.nsh" 5 | !include "FileFunc.nsh" 6 | 7 | !ifndef INFO_PROJECTNAME 8 | !define INFO_PROJECTNAME "{{.Name}}" 9 | !endif 10 | !ifndef INFO_COMPANYNAME 11 | !define INFO_COMPANYNAME "{{.Info.CompanyName}}" 12 | !endif 13 | !ifndef INFO_PRODUCTNAME 14 | !define INFO_PRODUCTNAME "{{.Info.ProductName}}" 15 | !endif 16 | !ifndef INFO_PRODUCTVERSION 17 | !define INFO_PRODUCTVERSION "{{.Info.ProductVersion}}" 18 | !endif 19 | !ifndef INFO_COPYRIGHT 20 | !define INFO_COPYRIGHT "{{.Info.Copyright}}" 21 | !endif 22 | !ifndef PRODUCT_EXECUTABLE 23 | !define PRODUCT_EXECUTABLE "${INFO_PROJECTNAME}.exe" 24 | !endif 25 | !ifndef UNINST_KEY_NAME 26 | !define UNINST_KEY_NAME "${INFO_COMPANYNAME}${INFO_PRODUCTNAME}" 27 | !endif 28 | !define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINST_KEY_NAME}" 29 | 30 | !ifndef REQUEST_EXECUTION_LEVEL 31 | !define REQUEST_EXECUTION_LEVEL "admin" 32 | !endif 33 | 34 | RequestExecutionLevel "${REQUEST_EXECUTION_LEVEL}" 35 | 36 | !ifdef ARG_WAILS_AMD64_BINARY 37 | !define SUPPORTS_AMD64 38 | !endif 39 | 40 | !ifdef ARG_WAILS_ARM64_BINARY 41 | !define SUPPORTS_ARM64 42 | !endif 43 | 44 | !ifdef SUPPORTS_AMD64 45 | !ifdef SUPPORTS_ARM64 46 | !define ARCH "amd64_arm64" 47 | !else 48 | !define ARCH "amd64" 49 | !endif 50 | !else 51 | !ifdef SUPPORTS_ARM64 52 | !define ARCH "arm64" 53 | !else 54 | !error "Wails: Undefined ARCH, please provide at least one of ARG_WAILS_AMD64_BINARY or ARG_WAILS_ARM64_BINARY" 55 | !endif 56 | !endif 57 | 58 | !macro wails.checkArchitecture 59 | !ifndef WAILS_WIN10_REQUIRED 60 | !define WAILS_WIN10_REQUIRED "This product is only supported on Windows 10 (Server 2016) and later." 61 | !endif 62 | 63 | !ifndef WAILS_ARCHITECTURE_NOT_SUPPORTED 64 | !define WAILS_ARCHITECTURE_NOT_SUPPORTED "This product can't be installed on the current Windows architecture. Supports: ${ARCH}" 65 | !endif 66 | 67 | ${If} ${AtLeastWin10} 68 | !ifdef SUPPORTS_AMD64 69 | ${if} ${IsNativeAMD64} 70 | Goto ok 71 | ${EndIf} 72 | !endif 73 | 74 | !ifdef SUPPORTS_ARM64 75 | ${if} ${IsNativeARM64} 76 | Goto ok 77 | ${EndIf} 78 | !endif 79 | 80 | IfSilent silentArch notSilentArch 81 | silentArch: 82 | SetErrorLevel 65 83 | Abort 84 | notSilentArch: 85 | MessageBox MB_OK "${WAILS_ARCHITECTURE_NOT_SUPPORTED}" 86 | Quit 87 | ${else} 88 | IfSilent silentWin notSilentWin 89 | silentWin: 90 | SetErrorLevel 64 91 | Abort 92 | notSilentWin: 93 | MessageBox MB_OK "${WAILS_WIN10_REQUIRED}" 94 | Quit 95 | ${EndIf} 96 | 97 | ok: 98 | !macroend 99 | 100 | !macro wails.files 101 | !ifdef SUPPORTS_AMD64 102 | ${if} ${IsNativeAMD64} 103 | File "/oname=${PRODUCT_EXECUTABLE}" "${ARG_WAILS_AMD64_BINARY}" 104 | ${EndIf} 105 | !endif 106 | 107 | !ifdef SUPPORTS_ARM64 108 | ${if} ${IsNativeARM64} 109 | File "/oname=${PRODUCT_EXECUTABLE}" "${ARG_WAILS_ARM64_BINARY}" 110 | ${EndIf} 111 | !endif 112 | !macroend 113 | 114 | !macro wails.writeUninstaller 115 | WriteUninstaller "$INSTDIR\uninstall.exe" 116 | 117 | SetRegView 64 118 | WriteRegStr HKLM "${UNINST_KEY}" "Publisher" "${INFO_COMPANYNAME}" 119 | WriteRegStr HKLM "${UNINST_KEY}" "DisplayName" "${INFO_PRODUCTNAME}" 120 | WriteRegStr HKLM "${UNINST_KEY}" "DisplayVersion" "${INFO_PRODUCTVERSION}" 121 | WriteRegStr HKLM "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\${PRODUCT_EXECUTABLE}" 122 | WriteRegStr HKLM "${UNINST_KEY}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\"" 123 | WriteRegStr HKLM "${UNINST_KEY}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S" 124 | 125 | ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2 126 | IntFmt $0 "0x%08X" $0 127 | WriteRegDWORD HKLM "${UNINST_KEY}" "EstimatedSize" "$0" 128 | !macroend 129 | 130 | !macro wails.deleteUninstaller 131 | Delete "$INSTDIR\uninstall.exe" 132 | 133 | SetRegView 64 134 | DeleteRegKey HKLM "${UNINST_KEY}" 135 | !macroend 136 | 137 | # Install webview2 by launching the bootstrapper 138 | # See https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/distribution#online-only-deployment 139 | !macro wails.webview2runtime 140 | !ifndef WAILS_INSTALL_WEBVIEW_DETAILPRINT 141 | !define WAILS_INSTALL_WEBVIEW_DETAILPRINT "Installing: WebView2 Runtime" 142 | !endif 143 | 144 | SetRegView 64 145 | # If the admin key exists and is not empty then webview2 is already installed 146 | ReadRegStr $0 HKLM "SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv" 147 | ${If} $0 != "" 148 | Goto ok 149 | ${EndIf} 150 | 151 | ${If} ${REQUEST_EXECUTION_LEVEL} == "user" 152 | # If the installer is run in user level, check the user specific key exists and is not empty then webview2 is already installed 153 | ReadRegStr $0 HKCU "Software\Microsoft\EdgeUpdate\Clients{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv" 154 | ${If} $0 != "" 155 | Goto ok 156 | ${EndIf} 157 | ${EndIf} 158 | 159 | SetDetailsPrint both 160 | DetailPrint "${WAILS_INSTALL_WEBVIEW_DETAILPRINT}" 161 | SetDetailsPrint listonly 162 | 163 | InitPluginsDir 164 | CreateDirectory "$pluginsdir\webview2bootstrapper" 165 | SetOutPath "$pluginsdir\webview2bootstrapper" 166 | File "tmp/MicrosoftEdgeWebview2Setup.exe" 167 | ExecWait '"$pluginsdir\webview2bootstrapper\MicrosoftEdgeWebview2Setup.exe" /silent /install' 168 | 169 | SetDetailsPrint both 170 | ok: 171 | !macroend -------------------------------------------------------------------------------- /build/windows/wails.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | true/pm 12 | permonitorv2,permonitor 13 | 14 | 15 | -------------------------------------------------------------------------------- /count/countCode.go: -------------------------------------------------------------------------------- 1 | package count 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | type lineCode struct { 8 | LineNO int `json:"lineNO"` 9 | Code string `json:"code,omitempty"` 10 | CommentFlag string `json:"commentFlag,omitempty"` 11 | } 12 | 13 | type LineCounter struct { 14 | TotalLineNum int `json:"totalLineNum"` 15 | BlankLineNum int `json:"blankLineNum"` 16 | CommentLineNum int `json:"commentLineNum"` 17 | TotalLine []lineCode `json:"totalLineCode"` 18 | BlankLine []lineCode `json:"blankLineCode"` 19 | CommentLine []lineCode `json:"commentLineCode"` 20 | } 21 | 22 | // countCodeWithCache 通过读取string buffer,解析代码文件中的注释行、空白行, 23 | // 必须保证s是utf-8编码 24 | func countCodeWithCache(s string, rules []CommentRule) LineCounter { 25 | var totalLine, blankLine, commentLine []lineCode 26 | // 先去除换行符,拿到每行代码的切片 27 | OSFileEOL := "\n" 28 | lineWithoutEOL := strings.Split(s, OSFileEOL) 29 | // 对于同一行代码,不同注释规则是否开启多行注释,需要分开管理 30 | // 为了减少内存占用,只存储len(rules)长度/容量的切片 31 | enableMultipleComment := make([]bool, len(rules)) 32 | 33 | for k, v := range lineWithoutEOL { 34 | commentFlag := "" 35 | lineNO := k + 1 36 | // 需要处理代码中的空格前后缀和CRLF模式下的换行符前后缀 37 | code := strings.Trim(v, "\r\t") 38 | 39 | // TODO: 40 | // 如果空白行在多行注释内,会被处理成空白行 41 | if strings.Compare(code, "") == 0 { 42 | blankLine = append(blankLine, lineCode{lineNO, code, commentFlag}) 43 | } 44 | 45 | for i, rule := range rules { 46 | // 单行注释 47 | if strings.Compare(rule.row, "") != 0 && 48 | strings.HasPrefix(code, rule.row) { 49 | commentFlag = rule.row 50 | commentLine = append(commentLine, lineCode{lineNO, code, commentFlag}) 51 | } 52 | 53 | // 避免注释规则为空,若为空,则每行代码均被判断成注释 54 | if strings.Compare(rule.block.start, "") == 0 || 55 | strings.Compare(rule.block.end, "") == 0 { 56 | continue 57 | } 58 | 59 | // 多行注释 60 | if strings.HasPrefix(code, rule.block.start) || 61 | strings.HasSuffix(code, rule.block.end) || 62 | enableMultipleComment[i] { 63 | commentFlag = rule.block.start + rule.block.end 64 | commentLine = append(commentLine, lineCode{lineNO, code, commentFlag}) 65 | } 66 | 67 | // 多行注释写在多行 68 | if strings.HasPrefix(code, rule.block.start) && 69 | !strings.HasSuffix(code, rule.block.end) { 70 | // 开启多行注释 71 | enableMultipleComment[i] = true 72 | } else if strings.HasSuffix(code, rule.block.end) { 73 | // 关闭多行注释 74 | enableMultipleComment[i] = false 75 | } 76 | } 77 | totalLine = append(totalLine, lineCode{lineNO, code, commentFlag}) 78 | } 79 | 80 | return LineCounter{ 81 | len(totalLine), 82 | len(blankLine), 83 | len(commentLine), 84 | totalLine, 85 | blankLine, 86 | commentLine, 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /count/countFile.go: -------------------------------------------------------------------------------- 1 | package count 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "sync" 7 | "unicode/utf8" 8 | ) 9 | 10 | type FileCodeCounter struct { 11 | FilePath string `json:"filePath"` 12 | FileType string `json:"fileType"` 13 | Data LineCounter `json:"data,omitempty"` 14 | } 15 | 16 | // 读取单个文件,计算代码空白行、注释行 17 | func readCodeLineByFile(path string, commentRule []CommentRule, msgChan chan<- FileCodeCounter, wg *sync.WaitGroup) { 18 | defer wg.Done() 19 | // 解析代码文件中的空白行、注释行 20 | codeCounter := FileCodeCounter{ 21 | path, 22 | "", 23 | LineCounter{}, 24 | } 25 | file, _ := os.ReadFile(path) 26 | valid := utf8.Valid(file) 27 | if valid { 28 | codeCounter.FileType = "code" 29 | codeCounter.Data = countCodeWithCache(string(file), commentRule) 30 | } else { 31 | codeCounter.FileType = "binary" 32 | } 33 | msgChan <- codeCounter 34 | 35 | // fmt.Printf("file: \u001b[1;36m%v\u001b[0m, BlankLineNum: %v, CommentLineNum: %v, TotalLineNum: %v\n", filepath.Base(path), lineCounter.BlankLineNum, lineCounter.CommentLineNum, lineCounter.TotalLineNum) 36 | // for _, line := range lineCounter.TotalLine { 37 | // fmt.Printf("line: %v, flag: %q, code: %q\n", line.LineNO, line.CommentFlag, line.Code) 38 | // } 39 | } 40 | 41 | // 读取dir下所有文件,计算代码空白行、注释行,返回一个接收channel,可以多次读取,当所有文件都被计算完成后,channel会自动关闭 42 | func ReadCodeLinesByDirFiles(rootDir string, exclude []string, fileRules map[string][]CommentRule) (<-chan FileCodeCounter, error) { 43 | files, err := ReadDirTree(rootDir, exclude) 44 | wg := new(sync.WaitGroup) 45 | msgChan := make(chan FileCodeCounter, len(files)) 46 | if err != nil { 47 | return msgChan, err 48 | } 49 | 50 | for _, file := range files { 51 | extname := filepath.Ext(file) 52 | if len(extname) > 1 { 53 | extname = extname[1:] 54 | } 55 | rule := fileRules[extname] 56 | 57 | wg.Add(1) 58 | go readCodeLineByFile(file, rule, msgChan, wg) 59 | } 60 | 61 | // 文件一边count,一边读取chanel 62 | // 完全的channel pipe,channel带缓冲,不会阻塞多个go routine计算 63 | go func() { 64 | wg.Wait() 65 | // 必须关闭,因为range仅当close chan才跳出 66 | close(msgChan) 67 | }() 68 | return msgChan, nil 69 | } 70 | -------------------------------------------------------------------------------- /count/countFile_test.go: -------------------------------------------------------------------------------- 1 | package count 2 | 3 | import ( 4 | "fmt" 5 | "path/filepath" 6 | "testing" 7 | ) 8 | 9 | func BenchmarkCountFileByDir(b *testing.B) { 10 | for i := 0; i < b.N; i++ { 11 | // 读取dir下的文件信息 12 | exclude := []string{"go.mod", ".css", ".scss"} 13 | rootDir, _ := filepath.Abs("./") 14 | filesLineCode, err := ReadCodeLinesByDirFiles(rootDir, exclude, make(map[string][]CommentRule) 15 | if err != nil { 16 | fmt.Println(err) 17 | } 18 | for range filesLineCode { 19 | // fmt.Printf("chan len is %v,chan cap is %v, msg comment NO is %v.\n", len(msgChan), cap(msgChan), msg.CommentLineNum) 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /count/defaultConfig.go: -------------------------------------------------------------------------------- 1 | package count 2 | 3 | // 块注释 4 | type blockComment struct { 5 | start string 6 | end string 7 | } 8 | 9 | // 注释规则 10 | type CommentRule struct { 11 | row string 12 | block blockComment 13 | } 14 | 15 | // 新建文件注释规则 16 | func NewCommentRule(row, blockStart, blockEnd string) CommentRule { 17 | return CommentRule{ 18 | row: row, 19 | block: blockComment{blockStart, blockEnd}, 20 | } 21 | } 22 | 23 | // 默认的文件注释规则map 24 | var DefaultRuleConfig = make(map[string][]CommentRule) 25 | 26 | // 默认过滤的文件(目录)slice 27 | var ExcludePaths = []string{ 28 | "node_modules", 29 | ".git", 30 | ".gitignore", 31 | ".npmignore", 32 | } 33 | 34 | // 初始化文件的默认注释规则 DefaultRuleConfig 35 | func init() { 36 | DefaultRuleConfig["js"] = []CommentRule{ 37 | { 38 | row: "//", block: blockComment{start: "/*", end: "*/"}, 39 | }, 40 | } 41 | 42 | DefaultRuleConfig["ts"] = []CommentRule{ 43 | { 44 | row: "//", block: blockComment{start: "/*", end: "*/"}, 45 | }, 46 | } 47 | 48 | DefaultRuleConfig["go"] = []CommentRule{ 49 | { 50 | row: "//", block: blockComment{start: "/*", end: "*/"}, 51 | }, 52 | } 53 | 54 | DefaultRuleConfig["jsx"] = []CommentRule{ 55 | { 56 | row: "//", block: blockComment{start: "/*", end: "*/"}, 57 | }, 58 | } 59 | 60 | DefaultRuleConfig["tsx"] = []CommentRule{ 61 | { 62 | row: "//", block: blockComment{start: "/*", end: "*/"}, 63 | }, 64 | } 65 | 66 | DefaultRuleConfig["vue"] = []CommentRule{ 67 | { 68 | row: "//", block: blockComment{start: "/*", end: "*/"}, 69 | }, 70 | { 71 | block: blockComment{start: ""}, 72 | }, 73 | } 74 | 75 | DefaultRuleConfig["svelte"] = []CommentRule{ 76 | { 77 | row: "//", block: blockComment{start: "/*", end: "*/"}, 78 | }, 79 | { 80 | block: blockComment{start: ""}, 81 | }, 82 | } 83 | 84 | DefaultRuleConfig["html"] = []CommentRule{ 85 | { 86 | block: blockComment{start: ""}, 87 | }, 88 | } 89 | 90 | DefaultRuleConfig["css"] = []CommentRule{ 91 | { 92 | block: blockComment{start: "/*", end: "*/"}, 93 | }, 94 | } 95 | 96 | DefaultRuleConfig["java"] = []CommentRule{ 97 | { 98 | row: "//", block: blockComment{start: "/*", end: "*/"}, 99 | }, 100 | } 101 | 102 | DefaultRuleConfig["c"] = []CommentRule{ 103 | { 104 | row: "//", block: blockComment{start: "/*", end: "*/"}, 105 | }, 106 | } 107 | 108 | DefaultRuleConfig["cpp"] = []CommentRule{ 109 | { 110 | row: "//", block: blockComment{start: "/*", end: "*/"}, 111 | }, 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /count/readDir.go: -------------------------------------------------------------------------------- 1 | package count 2 | 3 | import ( 4 | "io/fs" 5 | "os" 6 | "path/filepath" 7 | "strings" 8 | ) 9 | 10 | // 读取文件目录下的所有文件(夹)路径,并根据exclude过滤 11 | // 12 | // path:完整的os路径,filepath包拼接 13 | // 14 | // exclude:文件(夹)basename、文件后缀名的列表,例 []string{ "count", "count.go", ".go" } 15 | func ReadDirTree(path string, exclude []string) ([]string, error) { 16 | var readDirErr error 17 | filesList := make([]string, 0) 18 | rootFs := os.DirFS(path) 19 | 20 | walkErr := fs.WalkDir(rootFs, ".", func(p string, d fs.DirEntry, err error) error { 21 | if err != nil { 22 | return fs.SkipDir 23 | } 24 | 25 | basename := d.Name() 26 | extname := filepath.Ext(basename) 27 | 28 | hasInclueds := stringSome(exclude, func(v string) bool { 29 | return strings.Compare(v, basename) == 0 || (!d.IsDir() && strings.Compare(v, extname) == 0) 30 | }) 31 | // fmt.Printf("basename: %v, extname: %v, hasInclueds: %v\n", basename, extname, hasInclueds) 32 | 33 | // 如果被exclude匹配到文件(夹)名或文件后缀名,则跳过 34 | if hasInclueds { 35 | if d.IsDir() { 36 | return fs.SkipDir 37 | } else { 38 | return nil 39 | } 40 | } 41 | 42 | // 如果匹配到非exclude的文件名,append 43 | if !d.IsDir() { 44 | fullPath := filepath.Join(path, p) 45 | filesList = append(filesList, fullPath) 46 | } 47 | 48 | return nil 49 | }) 50 | 51 | if walkErr != nil { 52 | readDirErr = walkErr 53 | } 54 | return filesList, readDirErr 55 | } 56 | -------------------------------------------------------------------------------- /count/utils.go: -------------------------------------------------------------------------------- 1 | package count 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | // slice some with,判断s里面是否存在某项,由withFn完全匹配,v支持string类型,是s的子项 8 | func stringSome(s []string, withFn func(v string) bool) bool { 9 | isInclueded := false 10 | for _, val := range s { 11 | isInclueded = withFn(val) 12 | if isInclueded { 13 | break 14 | } 15 | } 16 | return isInclueded 17 | } 18 | 19 | // slice includes with,判断s里面是否存在r,完全匹配,目前支持 string类型 20 | func stringIncludes(s []string, r string) bool { 21 | return stringSome(s, func(v string) bool { 22 | return strings.Compare(v, r) == 0 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /frontend/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanchenchen/CodeCounter/56a6e9a4697231e1b4c5aedb17e2507fd74d661e/frontend/.DS_Store -------------------------------------------------------------------------------- /frontend/dist/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanchenchen/CodeCounter/56a6e9a4697231e1b4c5aedb17e2507fd74d661e/frontend/dist/.gitkeep -------------------------------------------------------------------------------- /frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | code-counter 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "code-counter", 3 | "version": "2.0.1", 4 | "author": "alanchenchen", 5 | "private": true, 6 | "scripts": { 7 | "dev": "vite build --watch", 8 | "build": "vite build" 9 | }, 10 | "dependencies": { 11 | "iview": "^2.14.0-rc.4", 12 | "vue": "^2.3.3", 13 | "vue-router": "^3.0.1" 14 | }, 15 | "devDependencies": { 16 | "vite-plugin-vue2": "^1.9.3", 17 | "sass": "^1.49.9", 18 | "vite": "^2.8.6", 19 | "vite-plugin-html": "^3.2.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /frontend/public/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanchenchen/CodeCounter/56a6e9a4697231e1b4c5aedb17e2507fd74d661e/frontend/public/.gitkeep -------------------------------------------------------------------------------- /frontend/src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanchenchen/CodeCounter/56a6e9a4697231e1b4c5aedb17e2507fd74d661e/frontend/src/.DS_Store -------------------------------------------------------------------------------- /frontend/src/App.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 109 | 110 | 115 | 181 | -------------------------------------------------------------------------------- /frontend/src/assets/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanchenchen/CodeCounter/56a6e9a4697231e1b4c5aedb17e2507fd74d661e/frontend/src/assets/github.png -------------------------------------------------------------------------------- /frontend/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanchenchen/CodeCounter/56a6e9a4697231e1b4c5aedb17e2507fd74d661e/frontend/src/assets/logo.png -------------------------------------------------------------------------------- /frontend/src/components/CItem.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 34 | 35 | -------------------------------------------------------------------------------- /frontend/src/components/CModal.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 62 | 63 | -------------------------------------------------------------------------------- /frontend/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import App from "./App.vue"; 3 | import router from "./router"; 4 | 5 | Vue.config.productionTip = false; 6 | 7 | import iView from "iview"; 8 | import "iview/dist/styles/iview.css"; 9 | Vue.use(iView); 10 | 11 | //路由跳转加入loadingbar全局提示 12 | router.beforeEach((to, from, next) => { 13 | iView.LoadingBar.start(); 14 | next(); 15 | }); 16 | 17 | router.afterEach(route => { 18 | iView.LoadingBar.finish(); 19 | }); 20 | 21 | new Vue({ 22 | render: (h) => h(App), 23 | router 24 | }).$mount("#app"); -------------------------------------------------------------------------------- /frontend/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Router from "vue-router"; 2 | import Vue from "vue"; 3 | 4 | import Home from "@/views/Home.vue"; 5 | import AddFile from "@/views/AddFile.vue"; 6 | import ShowResult from "@/views/ShowResult.vue"; 7 | 8 | Vue.use(Router); 9 | 10 | export default new Router({ 11 | // mode: "hash", 12 | routes: [ 13 | { 14 | path: "/", 15 | component: Home 16 | }, 17 | { 18 | path: "/addFile", 19 | component: AddFile 20 | }, 21 | { 22 | path: "/showResult", 23 | component: ShowResult 24 | } 25 | ] 26 | }); -------------------------------------------------------------------------------- /frontend/src/views/AddFile.vue: -------------------------------------------------------------------------------- 1 | 78 | 79 | 281 | 282 | 308 | -------------------------------------------------------------------------------- /frontend/src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 34 | 35 | 74 | -------------------------------------------------------------------------------- /frontend/src/views/ShowResult.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 95 | 96 | -------------------------------------------------------------------------------- /frontend/src/wailsjs/go/models.ts: -------------------------------------------------------------------------------- 1 | export namespace count { 2 | 3 | export class lineCode { 4 | lineNO: number; 5 | code?: string; 6 | commentFlag?: string; 7 | 8 | static createFrom(source: any = {}) { 9 | return new lineCode(source); 10 | } 11 | 12 | constructor(source: any = {}) { 13 | if ('string' === typeof source) source = JSON.parse(source); 14 | this.lineNO = source["lineNO"]; 15 | this.code = source["code"]; 16 | this.commentFlag = source["commentFlag"]; 17 | } 18 | } 19 | export class LineCounter { 20 | totalLineNum: number; 21 | blankLineNum: number; 22 | commentLineNum: number; 23 | totalLineCode: lineCode[]; 24 | blankLineCode: lineCode[]; 25 | commentLineCode: lineCode[]; 26 | 27 | static createFrom(source: any = {}) { 28 | return new LineCounter(source); 29 | } 30 | 31 | constructor(source: any = {}) { 32 | if ('string' === typeof source) source = JSON.parse(source); 33 | this.totalLineNum = source["totalLineNum"]; 34 | this.blankLineNum = source["blankLineNum"]; 35 | this.commentLineNum = source["commentLineNum"]; 36 | this.totalLineCode = this.convertValues(source["totalLineCode"], lineCode); 37 | this.blankLineCode = this.convertValues(source["blankLineCode"], lineCode); 38 | this.commentLineCode = this.convertValues(source["commentLineCode"], lineCode); 39 | } 40 | 41 | convertValues(a: any, classs: any, asMap: boolean = false): any { 42 | if (!a) { 43 | return a; 44 | } 45 | if (a.slice) { 46 | return (a as any[]).map(elem => this.convertValues(elem, classs)); 47 | } else if ("object" === typeof a) { 48 | if (asMap) { 49 | for (const key of Object.keys(a)) { 50 | a[key] = new classs(a[key]); 51 | } 52 | return a; 53 | } 54 | return new classs(a); 55 | } 56 | return a; 57 | } 58 | } 59 | export class FileCodeCounter { 60 | filePath: string; 61 | fileType: string; 62 | data?: LineCounter; 63 | 64 | static createFrom(source: any = {}) { 65 | return new FileCodeCounter(source); 66 | } 67 | 68 | constructor(source: any = {}) { 69 | if ('string' === typeof source) source = JSON.parse(source); 70 | this.filePath = source["filePath"]; 71 | this.fileType = source["fileType"]; 72 | this.data = this.convertValues(source["data"], LineCounter); 73 | } 74 | 75 | convertValues(a: any, classs: any, asMap: boolean = false): any { 76 | if (!a) { 77 | return a; 78 | } 79 | if (a.slice) { 80 | return (a as any[]).map(elem => this.convertValues(elem, classs)); 81 | } else if ("object" === typeof a) { 82 | if (asMap) { 83 | for (const key of Object.keys(a)) { 84 | a[key] = new classs(a[key]); 85 | } 86 | return a; 87 | } 88 | return new classs(a); 89 | } 90 | return a; 91 | } 92 | } 93 | 94 | 95 | } 96 | 97 | -------------------------------------------------------------------------------- /frontend/src/wailsjs/runtime/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@wailsapp/runtime", 3 | "version": "2.0.0", 4 | "description": "Wails Javascript runtime library", 5 | "main": "runtime.js", 6 | "types": "runtime.d.ts", 7 | "scripts": { 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/wailsapp/wails.git" 12 | }, 13 | "keywords": [ 14 | "Wails", 15 | "Javascript", 16 | "Go" 17 | ], 18 | "author": "Lea Anthony ", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/wailsapp/wails/issues" 22 | }, 23 | "homepage": "https://github.com/wailsapp/wails#readme" 24 | } 25 | -------------------------------------------------------------------------------- /frontend/src/wailsjs/runtime/runtime.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | _ __ _ __ 3 | | | / /___ _(_) /____ 4 | | | /| / / __ `/ / / ___/ 5 | | |/ |/ / /_/ / / (__ ) 6 | |__/|__/\__,_/_/_/____/ 7 | The electron alternative for Go 8 | (c) Lea Anthony 2019-present 9 | */ 10 | 11 | export interface Position { 12 | x: number; 13 | y: number; 14 | } 15 | 16 | export interface Size { 17 | w: number; 18 | h: number; 19 | } 20 | 21 | export interface Screen { 22 | isCurrent: boolean; 23 | isPrimary: boolean; 24 | width : number 25 | height : number 26 | } 27 | 28 | // Environment information such as platform, buildtype, ... 29 | export interface EnvironmentInfo { 30 | buildType: string; 31 | platform: string; 32 | arch: string; 33 | } 34 | 35 | // [EventsEmit](https://wails.io/docs/reference/runtime/events#eventsemit) 36 | // emits the given event. Optional data may be passed with the event. 37 | // This will trigger any event listeners. 38 | export function EventsEmit(eventName: string, ...data: any): void; 39 | 40 | // [EventsOn](https://wails.io/docs/reference/runtime/events#eventson) sets up a listener for the given event name. 41 | export function EventsOn(eventName: string, callback: (...data: any) => void): () => void; 42 | 43 | // [EventsOnMultiple](https://wails.io/docs/reference/runtime/events#eventsonmultiple) 44 | // sets up a listener for the given event name, but will only trigger a given number times. 45 | export function EventsOnMultiple(eventName: string, callback: (...data: any) => void, maxCallbacks: number): () => void; 46 | 47 | // [EventsOnce](https://wails.io/docs/reference/runtime/events#eventsonce) 48 | // sets up a listener for the given event name, but will only trigger once. 49 | export function EventsOnce(eventName: string, callback: (...data: any) => void): () => void; 50 | 51 | // [EventsOff](https://wails.io/docs/reference/runtime/events#eventsoff) 52 | // unregisters the listener for the given event name. 53 | export function EventsOff(eventName: string, ...additionalEventNames: string[]): void; 54 | 55 | // [EventsOffAll](https://wails.io/docs/reference/runtime/events#eventsoffall) 56 | // unregisters all listeners. 57 | export function EventsOffAll(): void; 58 | 59 | // [LogPrint](https://wails.io/docs/reference/runtime/log#logprint) 60 | // logs the given message as a raw message 61 | export function LogPrint(message: string): void; 62 | 63 | // [LogTrace](https://wails.io/docs/reference/runtime/log#logtrace) 64 | // logs the given message at the `trace` log level. 65 | export function LogTrace(message: string): void; 66 | 67 | // [LogDebug](https://wails.io/docs/reference/runtime/log#logdebug) 68 | // logs the given message at the `debug` log level. 69 | export function LogDebug(message: string): void; 70 | 71 | // [LogError](https://wails.io/docs/reference/runtime/log#logerror) 72 | // logs the given message at the `error` log level. 73 | export function LogError(message: string): void; 74 | 75 | // [LogFatal](https://wails.io/docs/reference/runtime/log#logfatal) 76 | // logs the given message at the `fatal` log level. 77 | // The application will quit after calling this method. 78 | export function LogFatal(message: string): void; 79 | 80 | // [LogInfo](https://wails.io/docs/reference/runtime/log#loginfo) 81 | // logs the given message at the `info` log level. 82 | export function LogInfo(message: string): void; 83 | 84 | // [LogWarning](https://wails.io/docs/reference/runtime/log#logwarning) 85 | // logs the given message at the `warning` log level. 86 | export function LogWarning(message: string): void; 87 | 88 | // [WindowReload](https://wails.io/docs/reference/runtime/window#windowreload) 89 | // Forces a reload by the main application as well as connected browsers. 90 | export function WindowReload(): void; 91 | 92 | // [WindowReloadApp](https://wails.io/docs/reference/runtime/window#windowreloadapp) 93 | // Reloads the application frontend. 94 | export function WindowReloadApp(): void; 95 | 96 | // [WindowSetSystemDefaultTheme](https://wails.io/docs/next/reference/runtime/window#windowsetsystemdefaulttheme) 97 | // *Windows only* 98 | // Sets window theme to system default (dark/light). 99 | export function WindowSetSystemDefaultTheme(): void; 100 | 101 | // [WindowSetLightTheme](https://wails.io/docs/next/reference/runtime/window#windowsetlighttheme) 102 | // *Windows only* 103 | // Sets window to light theme. 104 | export function WindowSetLightTheme(): void; 105 | 106 | // [WindowSetDarkTheme](https://wails.io/docs/next/reference/runtime/window#windowsetdarktheme) 107 | // *Windows only* 108 | // Sets window to dark theme. 109 | export function WindowSetDarkTheme(): void; 110 | 111 | // [WindowCenter](https://wails.io/docs/reference/runtime/window#windowcenter) 112 | // Centers the window on the monitor the window is currently on. 113 | export function WindowCenter(): void; 114 | 115 | // [WindowSetTitle](https://wails.io/docs/reference/runtime/window#windowsettitle) 116 | // Sets the text in the window title bar. 117 | export function WindowSetTitle(title: string): void; 118 | 119 | // [WindowFullscreen](https://wails.io/docs/reference/runtime/window#windowfullscreen) 120 | // Makes the window full screen. 121 | export function WindowFullscreen(): void; 122 | 123 | // [WindowUnfullscreen](https://wails.io/docs/reference/runtime/window#windowunfullscreen) 124 | // Restores the previous window dimensions and position prior to full screen. 125 | export function WindowUnfullscreen(): void; 126 | 127 | // [WindowIsFullscreen](https://wails.io/docs/reference/runtime/window#windowisfullscreen) 128 | // Returns the state of the window, i.e. whether the window is in full screen mode or not. 129 | export function WindowIsFullscreen(): Promise; 130 | 131 | // [WindowSetSize](https://wails.io/docs/reference/runtime/window#windowsetsize) 132 | // Sets the width and height of the window. 133 | export function WindowSetSize(width: number, height: number): Promise; 134 | 135 | // [WindowGetSize](https://wails.io/docs/reference/runtime/window#windowgetsize) 136 | // Gets the width and height of the window. 137 | export function WindowGetSize(): Promise; 138 | 139 | // [WindowSetMaxSize](https://wails.io/docs/reference/runtime/window#windowsetmaxsize) 140 | // Sets the maximum window size. Will resize the window if the window is currently larger than the given dimensions. 141 | // Setting a size of 0,0 will disable this constraint. 142 | export function WindowSetMaxSize(width: number, height: number): void; 143 | 144 | // [WindowSetMinSize](https://wails.io/docs/reference/runtime/window#windowsetminsize) 145 | // Sets the minimum window size. Will resize the window if the window is currently smaller than the given dimensions. 146 | // Setting a size of 0,0 will disable this constraint. 147 | export function WindowSetMinSize(width: number, height: number): void; 148 | 149 | // [WindowSetPosition](https://wails.io/docs/reference/runtime/window#windowsetposition) 150 | // Sets the window position relative to the monitor the window is currently on. 151 | export function WindowSetPosition(x: number, y: number): void; 152 | 153 | // [WindowGetPosition](https://wails.io/docs/reference/runtime/window#windowgetposition) 154 | // Gets the window position relative to the monitor the window is currently on. 155 | export function WindowGetPosition(): Promise; 156 | 157 | // [WindowHide](https://wails.io/docs/reference/runtime/window#windowhide) 158 | // Hides the window. 159 | export function WindowHide(): void; 160 | 161 | // [WindowShow](https://wails.io/docs/reference/runtime/window#windowshow) 162 | // Shows the window, if it is currently hidden. 163 | export function WindowShow(): void; 164 | 165 | // [WindowMaximise](https://wails.io/docs/reference/runtime/window#windowmaximise) 166 | // Maximises the window to fill the screen. 167 | export function WindowMaximise(): void; 168 | 169 | // [WindowToggleMaximise](https://wails.io/docs/reference/runtime/window#windowtogglemaximise) 170 | // Toggles between Maximised and UnMaximised. 171 | export function WindowToggleMaximise(): void; 172 | 173 | // [WindowUnmaximise](https://wails.io/docs/reference/runtime/window#windowunmaximise) 174 | // Restores the window to the dimensions and position prior to maximising. 175 | export function WindowUnmaximise(): void; 176 | 177 | // [WindowIsMaximised](https://wails.io/docs/reference/runtime/window#windowismaximised) 178 | // Returns the state of the window, i.e. whether the window is maximised or not. 179 | export function WindowIsMaximised(): Promise; 180 | 181 | // [WindowMinimise](https://wails.io/docs/reference/runtime/window#windowminimise) 182 | // Minimises the window. 183 | export function WindowMinimise(): void; 184 | 185 | // [WindowUnminimise](https://wails.io/docs/reference/runtime/window#windowunminimise) 186 | // Restores the window to the dimensions and position prior to minimising. 187 | export function WindowUnminimise(): void; 188 | 189 | // [WindowIsMinimised](https://wails.io/docs/reference/runtime/window#windowisminimised) 190 | // Returns the state of the window, i.e. whether the window is minimised or not. 191 | export function WindowIsMinimised(): Promise; 192 | 193 | // [WindowIsNormal](https://wails.io/docs/reference/runtime/window#windowisnormal) 194 | // Returns the state of the window, i.e. whether the window is normal or not. 195 | export function WindowIsNormal(): Promise; 196 | 197 | // [WindowSetBackgroundColour](https://wails.io/docs/reference/runtime/window#windowsetbackgroundcolour) 198 | // Sets the background colour of the window to the given RGBA colour definition. This colour will show through for all transparent pixels. 199 | export function WindowSetBackgroundColour(R: number, G: number, B: number, A: number): void; 200 | 201 | // [ScreenGetAll](https://wails.io/docs/reference/runtime/window#screengetall) 202 | // Gets the all screens. Call this anew each time you want to refresh data from the underlying windowing system. 203 | export function ScreenGetAll(): Promise; 204 | 205 | // [BrowserOpenURL](https://wails.io/docs/reference/runtime/browser#browseropenurl) 206 | // Opens the given URL in the system browser. 207 | export function BrowserOpenURL(url: string): void; 208 | 209 | // [Environment](https://wails.io/docs/reference/runtime/intro#environment) 210 | // Returns information about the environment 211 | export function Environment(): Promise; 212 | 213 | // [Quit](https://wails.io/docs/reference/runtime/intro#quit) 214 | // Quits the application. 215 | export function Quit(): void; 216 | 217 | // [Hide](https://wails.io/docs/reference/runtime/intro#hide) 218 | // Hides the application. 219 | export function Hide(): void; 220 | 221 | // [Show](https://wails.io/docs/reference/runtime/intro#show) 222 | // Shows the application. 223 | export function Show(): void; 224 | -------------------------------------------------------------------------------- /frontend/src/wailsjs/runtime/runtime.js: -------------------------------------------------------------------------------- 1 | /* 2 | _ __ _ __ 3 | | | / /___ _(_) /____ 4 | | | /| / / __ `/ / / ___/ 5 | | |/ |/ / /_/ / / (__ ) 6 | |__/|__/\__,_/_/_/____/ 7 | The electron alternative for Go 8 | (c) Lea Anthony 2019-present 9 | */ 10 | 11 | export function LogPrint(message) { 12 | window.runtime.LogPrint(message); 13 | } 14 | 15 | export function LogTrace(message) { 16 | window.runtime.LogTrace(message); 17 | } 18 | 19 | export function LogDebug(message) { 20 | window.runtime.LogDebug(message); 21 | } 22 | 23 | export function LogInfo(message) { 24 | window.runtime.LogInfo(message); 25 | } 26 | 27 | export function LogWarning(message) { 28 | window.runtime.LogWarning(message); 29 | } 30 | 31 | export function LogError(message) { 32 | window.runtime.LogError(message); 33 | } 34 | 35 | export function LogFatal(message) { 36 | window.runtime.LogFatal(message); 37 | } 38 | 39 | export function EventsOnMultiple(eventName, callback, maxCallbacks) { 40 | window.runtime.EventsOnMultiple(eventName, callback, maxCallbacks); 41 | } 42 | 43 | export function EventsOn(eventName, callback) { 44 | EventsOnMultiple(eventName, callback, -1); 45 | } 46 | 47 | export function EventsOff(eventName, ...additionalEventNames) { 48 | return window.runtime.EventsOff(eventName, ...additionalEventNames); 49 | } 50 | 51 | export function EventsOnce(eventName, callback) { 52 | EventsOnMultiple(eventName, callback, 1); 53 | } 54 | 55 | export function EventsEmit(eventName) { 56 | let args = [eventName].slice.call(arguments); 57 | return window.runtime.EventsEmit.apply(null, args); 58 | } 59 | 60 | export function WindowReload() { 61 | window.runtime.WindowReload(); 62 | } 63 | 64 | export function WindowReloadApp() { 65 | window.runtime.WindowReloadApp(); 66 | } 67 | 68 | export function WindowSetSystemDefaultTheme() { 69 | window.runtime.WindowSetSystemDefaultTheme(); 70 | } 71 | 72 | export function WindowSetLightTheme() { 73 | window.runtime.WindowSetLightTheme(); 74 | } 75 | 76 | export function WindowSetDarkTheme() { 77 | window.runtime.WindowSetDarkTheme(); 78 | } 79 | 80 | export function WindowCenter() { 81 | window.runtime.WindowCenter(); 82 | } 83 | 84 | export function WindowSetTitle(title) { 85 | window.runtime.WindowSetTitle(title); 86 | } 87 | 88 | export function WindowFullscreen() { 89 | window.runtime.WindowFullscreen(); 90 | } 91 | 92 | export function WindowUnfullscreen() { 93 | window.runtime.WindowUnfullscreen(); 94 | } 95 | 96 | export function WindowIsFullscreen() { 97 | return window.runtime.WindowIsFullscreen(); 98 | } 99 | 100 | export function WindowGetSize() { 101 | return window.runtime.WindowGetSize(); 102 | } 103 | 104 | export function WindowSetSize(width, height) { 105 | window.runtime.WindowSetSize(width, height); 106 | } 107 | 108 | export function WindowSetMaxSize(width, height) { 109 | window.runtime.WindowSetMaxSize(width, height); 110 | } 111 | 112 | export function WindowSetMinSize(width, height) { 113 | window.runtime.WindowSetMinSize(width, height); 114 | } 115 | 116 | export function WindowSetPosition(x, y) { 117 | window.runtime.WindowSetPosition(x, y); 118 | } 119 | 120 | export function WindowGetPosition() { 121 | return window.runtime.WindowGetPosition(); 122 | } 123 | 124 | export function WindowHide() { 125 | window.runtime.WindowHide(); 126 | } 127 | 128 | export function WindowShow() { 129 | window.runtime.WindowShow(); 130 | } 131 | 132 | export function WindowMaximise() { 133 | window.runtime.WindowMaximise(); 134 | } 135 | 136 | export function WindowToggleMaximise() { 137 | window.runtime.WindowToggleMaximise(); 138 | } 139 | 140 | export function WindowUnmaximise() { 141 | window.runtime.WindowUnmaximise(); 142 | } 143 | 144 | export function WindowIsMaximised() { 145 | return window.runtime.WindowIsMaximised(); 146 | } 147 | 148 | export function WindowMinimise() { 149 | window.runtime.WindowMinimise(); 150 | } 151 | 152 | export function WindowUnminimise() { 153 | window.runtime.WindowUnminimise(); 154 | } 155 | 156 | export function WindowSetBackgroundColour(R, G, B, A) { 157 | window.runtime.WindowSetBackgroundColour(R, G, B, A); 158 | } 159 | 160 | export function ScreenGetAll() { 161 | return window.runtime.ScreenGetAll(); 162 | } 163 | 164 | export function WindowIsMinimised() { 165 | return window.runtime.WindowIsMinimised(); 166 | } 167 | 168 | export function WindowIsNormal() { 169 | return window.runtime.WindowIsNormal(); 170 | } 171 | 172 | export function BrowserOpenURL(url) { 173 | window.runtime.BrowserOpenURL(url); 174 | } 175 | 176 | export function Environment() { 177 | return window.runtime.Environment(); 178 | } 179 | 180 | export function Quit() { 181 | window.runtime.Quit(); 182 | } 183 | 184 | export function Hide() { 185 | window.runtime.Hide(); 186 | } 187 | 188 | export function Show() { 189 | window.runtime.Show(); 190 | } 191 | -------------------------------------------------------------------------------- /frontend/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import { createVuePlugin } from "vite-plugin-vue2"; 3 | import path from "path"; 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [ 8 | createVuePlugin(), 9 | ], 10 | resolve: { 11 | alias: { 12 | "@": path.resolve(__dirname, "src"), 13 | }, 14 | }, 15 | build: { 16 | rollupOptions: { 17 | output: { 18 | entryFileNames: `assets/[name].js`, 19 | chunkFileNames: `assets/[name].js`, 20 | assetFileNames: `assets/[name].[ext]`, 21 | }, 22 | }, 23 | }, 24 | }); 25 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module code-counter 2 | 3 | go 1.19 4 | 5 | require github.com/wailsapp/wails/v2 v2.2.0 6 | 7 | require ( 8 | github.com/bep/debounce v1.2.1 // indirect 9 | github.com/go-ole/go-ole v1.2.6 // indirect 10 | github.com/google/uuid v1.1.2 // indirect 11 | github.com/imdario/mergo v0.3.12 // indirect 12 | github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect 13 | github.com/labstack/echo/v4 v4.9.0 // indirect 14 | github.com/labstack/gommon v0.3.1 // indirect 15 | github.com/leaanthony/go-ansi-parser v1.0.1 // indirect 16 | github.com/leaanthony/gosod v1.0.3 // indirect 17 | github.com/leaanthony/slicer v1.5.0 // indirect 18 | github.com/mattn/go-colorable v0.1.11 // indirect 19 | github.com/mattn/go-isatty v0.0.14 // indirect 20 | github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect 21 | github.com/pkg/errors v0.9.1 // indirect 22 | github.com/samber/lo v1.27.1 // indirect 23 | github.com/tkrajina/go-reflector v0.5.5 // indirect 24 | github.com/valyala/bytebufferpool v1.0.0 // indirect 25 | github.com/valyala/fasttemplate v1.2.1 // indirect 26 | github.com/wailsapp/mimetype v1.4.1 // indirect 27 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect 28 | golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect 29 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect 30 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect 31 | golang.org/x/text v0.3.7 // indirect 32 | ) 33 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= 2 | github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= 7 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 8 | github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= 9 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 10 | github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= 11 | github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= 12 | github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck= 13 | github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= 14 | github.com/labstack/echo/v4 v4.9.0 h1:wPOF1CE6gvt/kmbMR4dGzWvHMPT+sAEUJOwOTtvITVY= 15 | github.com/labstack/echo/v4 v4.9.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks= 16 | github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o= 17 | github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= 18 | github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc= 19 | github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA= 20 | github.com/leaanthony/go-ansi-parser v1.0.1 h1:97v6c5kYppVsbScf4r/VZdXyQ21KQIfeQOk2DgKxGG4= 21 | github.com/leaanthony/go-ansi-parser v1.0.1/go.mod h1:7arTzgVI47srICYhvgUV4CGd063sGEeoSlych5yeSPM= 22 | github.com/leaanthony/gosod v1.0.3 h1:Fnt+/B6NjQOVuCWOKYRREZnjGyvg+mEhd1nkkA04aTQ= 23 | github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4= 24 | github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY= 25 | github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY= 26 | github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= 27 | github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= 28 | github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= 29 | github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= 30 | github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= 31 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 32 | github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 h1:acNfDZXmm28D2Yg/c3ALnZStzNaZMSagpbr96vY6Zjc= 33 | github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= 34 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 35 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 36 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 37 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 38 | github.com/samber/lo v1.27.1 h1:sTXwkRiIFIQG+G0HeAvOEnGjqWeWtI9cg5/n51KrxPg= 39 | github.com/samber/lo v1.27.1/go.mod h1:it33p9UtPMS7z72fP4gw/EIfQB2eI8ke7GR2wc6+Rhg= 40 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 41 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 42 | github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= 43 | github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M= 44 | github.com/tkrajina/go-reflector v0.5.5 h1:gwoQFNye30Kk7NrExj8zm3zFtrGPqOkzFMLuQZg1DtQ= 45 | github.com/tkrajina/go-reflector v0.5.5/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4= 46 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 47 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 48 | github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= 49 | github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= 50 | github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs= 51 | github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= 52 | github.com/wailsapp/wails/v2 v2.2.0 h1:+zRTNjwqyz1kofT0J2R1FpxXB+m3cZJzW3RjxDgxWNw= 53 | github.com/wailsapp/wails/v2 v2.2.0/go.mod h1:zdc9YVrIijjuNNkLOO3UpTU6M12TJe6TlriL+3ttlMM= 54 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= 55 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 56 | golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= 57 | golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= 58 | golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 59 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= 60 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 61 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 62 | golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 63 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 64 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 65 | golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 66 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 67 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 68 | golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 69 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= 70 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 71 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 72 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 73 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 74 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 75 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 76 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 77 | gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= 78 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 79 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 80 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 81 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 82 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "embed" 5 | "log" 6 | 7 | "github.com/wailsapp/wails/v2/pkg/options/assetserver" 8 | "github.com/wailsapp/wails/v2/pkg/options/mac" 9 | 10 | "github.com/wailsapp/wails/v2" 11 | "github.com/wailsapp/wails/v2/pkg/options" 12 | "github.com/wailsapp/wails/v2/pkg/options/windows" 13 | ) 14 | 15 | //go:embed frontend/dist 16 | var assets embed.FS 17 | 18 | //go:embed build/appicon.png 19 | var icon []byte 20 | 21 | func main() { 22 | // Create an instance of the app structure 23 | app := NewApp() 24 | 25 | // Create application with options 26 | err := wails.Run(&options.App{ 27 | Title: "Code Counter", 28 | Width: 320, 29 | Height: 550, 30 | DisableResize: true, 31 | Fullscreen: false, 32 | Frameless: true, 33 | StartHidden: true, 34 | HideWindowOnClose: false, 35 | BackgroundColour: options.NewRGBA(255, 255, 255, 0), // 先让背景色透明,然后在页面加载后显示窗口 36 | AssetServer: &assetserver.Options{ 37 | Assets: assets, 38 | }, 39 | OnStartup: app.startup, 40 | OnDomReady: app.domReady, 41 | OnShutdown: app.shutdown, 42 | Bind: []interface{}{ 43 | app, 44 | }, 45 | // Windows platform specific options 46 | Windows: &windows.Options{ 47 | WebviewIsTransparent: false, 48 | WindowIsTranslucent: false, 49 | DisableWindowIcon: false, 50 | }, 51 | Mac: &mac.Options{ 52 | TitleBar: mac.TitleBarHiddenInset(), 53 | Appearance: mac.NSAppearanceNameDarkAqua, 54 | WebviewIsTransparent: true, 55 | WindowIsTranslucent: true, 56 | About: &mac.AboutInfo{ 57 | Title: "Code Counter", 58 | Message: "© Alan Chen 2022 ", 59 | Icon: icon, 60 | }, 61 | }, 62 | }) 63 | 64 | if err != nil { 65 | log.Fatal(err) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /screenshot/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanchenchen/CodeCounter/56a6e9a4697231e1b4c5aedb17e2507fd74d661e/screenshot/.DS_Store -------------------------------------------------------------------------------- /screenshot/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanchenchen/CodeCounter/56a6e9a4697231e1b4c5aedb17e2507fd74d661e/screenshot/screenshot1.png -------------------------------------------------------------------------------- /screenshot/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanchenchen/CodeCounter/56a6e9a4697231e1b4c5aedb17e2507fd74d661e/screenshot/screenshot2.png -------------------------------------------------------------------------------- /screenshot/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanchenchen/CodeCounter/56a6e9a4697231e1b4c5aedb17e2507fd74d661e/screenshot/screenshot3.png -------------------------------------------------------------------------------- /scripts/build-macos-arm.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | echo -e "Start running the script..." 4 | cd ../ 5 | 6 | echo -e "Start building the app for macos platform..." 7 | wails build -clean -platform darwin/arm64 8 | 9 | echo -e "End running the script!" 10 | -------------------------------------------------------------------------------- /scripts/build-macos-intel.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | echo -e "Start running the script..." 4 | cd ../ 5 | 6 | echo -e "Start building the app for macos platform..." 7 | wails build -clean -platform darwin 8 | 9 | echo -e "End running the script!" 10 | -------------------------------------------------------------------------------- /scripts/build-macos.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | echo -e "Start running the script..." 4 | cd ../ 5 | 6 | echo -e "Start building the app for macos platform..." 7 | wails build -clean -platform darwin/universal 8 | 9 | echo -e "End running the script!" 10 | -------------------------------------------------------------------------------- /scripts/build-windows.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | echo -e "Start running the script..." 4 | cd ../ 5 | 6 | echo -e "Start building the app for windows platform..." 7 | wails build -clean -platform windows/amd64 8 | 9 | echo -e "End running the script!" 10 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | echo -e "Start running the script..." 4 | cd ../ 5 | 6 | echo -e "Start building the app..." 7 | wails build -clean 8 | 9 | echo -e "End running the script!" 10 | -------------------------------------------------------------------------------- /scripts/install-wails-cli.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | echo -e "Start running the script..." 4 | cd ../ 5 | 6 | echo -e "Current Go version: \c" 7 | go version 8 | 9 | echo -e "Install the Wails command line tool..." 10 | go install github.com/wailsapp/wails/v2/cmd/wails@latest 11 | 12 | echo -e "Successful installation!" 13 | 14 | echo -e "End running the script!" 15 | -------------------------------------------------------------------------------- /wails.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Code Counter", 3 | "assetdir": "frontend/dist", 4 | "frontend:build": "npm run build", 5 | "frontend:install": "npm install", 6 | "frontend:dev": "npm run dev", 7 | "frontend:dev:watcher": "", 8 | "wailsjsdir": "frontend/src", 9 | "version": "2", 10 | "Path": "", 11 | "BuildDir": "", 12 | "outputfilename": "Code Counter", 13 | "OutputType": "", 14 | "Platform": "", 15 | "runNonNativeBuildHooks": false, 16 | "postBuildHooks": null, 17 | "Author": { 18 | "name": "alanchenchen", 19 | "email": "739709491@qq.com" 20 | }, 21 | "Info": { 22 | "companyName": "Code Counter", 23 | "productName": "Code Counter", 24 | "productVersion": "2.0.1", 25 | "copyright": "Copyright Alan Chen @2022", 26 | "comments": "Built using Wails (https://wails.app)" 27 | }, 28 | "debounceMS": 100, 29 | "devserverurl": "http://localhost:34115", 30 | "appargs": "", 31 | "nsisType": "" 32 | } --------------------------------------------------------------------------------