Showing preview only (1,844K chars total). Download the full file or copy to clipboard to get everything.
Repository: vlang/v-analyzer
Branch: main
Commit: d5f13c0736f0
Files: 525
Total size: 13.5 MB
Directory structure:
gitextract_69osocjt/
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug-report.yml
│ │ ├── config.yml
│ │ └── feature-request.yml
│ └── workflows/
│ ├── analyzer_tests.yml
│ ├── build_ci.yml
│ ├── install_ci.yml
│ ├── lint.yml
│ ├── release.yml
│ ├── tree_sitter_v.yml
│ ├── version_test.vv
│ └── vscode_extension_tests.yml
├── .gitignore
├── .gitmodules
├── .v-analyzer/
│ └── config.toml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── build.vsh
├── editors/
│ └── code/
│ ├── .editorconfig
│ ├── .eslintignore
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── .vscodeignore
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── docs/
│ │ └── troubleshooting.md
│ ├── languages/
│ │ ├── v-language-configuration.json
│ │ └── vmod-language-configuration.json
│ ├── media/
│ │ └── welcome.css
│ ├── package.json
│ ├── scripts/
│ │ ├── build.js
│ │ └── minify_json.js
│ ├── src/
│ │ ├── bootstrap.ts
│ │ ├── client.ts
│ │ ├── commands.ts
│ │ ├── ctx.ts
│ │ ├── exec.ts
│ │ ├── extension.ts
│ │ ├── log.ts
│ │ ├── lsp_ext.ts
│ │ ├── stateUtils.ts
│ │ ├── tcp.ts
│ │ ├── utils.ts
│ │ └── welcome.ts
│ ├── syntaxes/
│ │ ├── stree.tmGrammar.json
│ │ ├── tests/
│ │ │ ├── accessor.vv
│ │ │ ├── comma.vv
│ │ │ ├── comment.vv
│ │ │ ├── comparison.vv
│ │ │ ├── dot.int.vv
│ │ │ ├── escape.vv
│ │ │ ├── hashtag.vv
│ │ │ ├── method.vv
│ │ │ ├── numbers.vv
│ │ │ ├── optional.vv
│ │ │ ├── pubfn.vv
│ │ │ ├── string.vv
│ │ │ ├── type.vv
│ │ │ └── variable.vv
│ │ ├── v.mod.tmLanguage.json
│ │ └── v.tmLanguage.json
│ └── tsconfig.json
├── install.vsh
├── src/
│ ├── analyzer/
│ │ ├── Indexer.v
│ │ ├── IndexingManager.v
│ │ ├── OpenedFile.v
│ │ ├── README.md
│ │ ├── index/
│ │ │ ├── FileIndex.v
│ │ │ ├── Index.v
│ │ │ ├── IndexDeserializer.v
│ │ │ ├── IndexSerializer.v
│ │ │ ├── IndexingRoot.v
│ │ │ ├── IndexingRoot_test.v
│ │ │ ├── PerFileIndex.v
│ │ │ ├── README.md
│ │ │ └── StubTree.v
│ │ ├── lang/
│ │ │ └── utils.v
│ │ ├── parser/
│ │ │ ├── README.md
│ │ │ ├── batch.v
│ │ │ └── parser.v
│ │ └── psi/
│ │ ├── Argument.v
│ │ ├── ArrayCreation.v
│ │ ├── AstNode.v
│ │ ├── Attribute.v
│ │ ├── AttributeExpression.v
│ │ ├── Attributes.v
│ │ ├── AttributesOwner.v
│ │ ├── BinaryExpression.v
│ │ ├── Block.v
│ │ ├── CallExpression.v
│ │ ├── Comment.v
│ │ ├── CompileTimeIfExpression.v
│ │ ├── ConstantDeclaration.v
│ │ ├── ConstantDefinition.v
│ │ ├── EmbeddedDefinition.v
│ │ ├── EnumDeclaration.v
│ │ ├── EnumFieldDeclaration.v
│ │ ├── FieldDeclaration.v
│ │ ├── FieldName.v
│ │ ├── ForStatement.v
│ │ ├── FunctionLiteral.v
│ │ ├── FunctionOrMethodDeclaration.v
│ │ ├── GenericArgumentsOwner.v
│ │ ├── GenericParameter.v
│ │ ├── GenericParameters.v
│ │ ├── GenericParametersOwner.v
│ │ ├── GenericTypeArguments.v
│ │ ├── GenericTypeInferer.v
│ │ ├── GenericTypeReifier.v
│ │ ├── GlobalVarDefinition.v
│ │ ├── Identifier.v
│ │ ├── IfExpression.v
│ │ ├── ImportAlias.v
│ │ ├── ImportDeclaration.v
│ │ ├── ImportList.v
│ │ ├── ImportName.v
│ │ ├── ImportPath.v
│ │ ├── ImportSpec.v
│ │ ├── IndexExpression.v
│ │ ├── InterfaceDeclaration.v
│ │ ├── InterfaceMethodDeclaration.v
│ │ ├── KeyedElement.v
│ │ ├── Literal.v
│ │ ├── MapInitExpression.v
│ │ ├── MapKeyedElement.v
│ │ ├── MatchExpression.v
│ │ ├── ModuleClause.v
│ │ ├── MutExpression.v
│ │ ├── MutabilityModifiers.v
│ │ ├── MutabilityOwner.v
│ │ ├── OptionPropagationExpression.v
│ │ ├── OrBlockExpression.v
│ │ ├── ParameterDeclaration.v
│ │ ├── ParameterList.v
│ │ ├── PlainType.v
│ │ ├── Position.v
│ │ ├── PrinterVisitor.v
│ │ ├── PsiDocCommentOwner.v
│ │ ├── PsiElement.v
│ │ ├── PsiElementImpl.v
│ │ ├── PsiElementVisitor.v
│ │ ├── PsiFile.v
│ │ ├── PsiNamedElement.v
│ │ ├── PsiReference.v
│ │ ├── PsiScopeProcessor.v
│ │ ├── PsiTreeWalker.v
│ │ ├── PsiTypedElement.v
│ │ ├── QualifiedType.v
│ │ ├── README.md
│ │ ├── Range.v
│ │ ├── Receiver.v
│ │ ├── RecursiveVisitor.v
│ │ ├── ReferenceExpression.v
│ │ ├── ReferenceExpressionBase.v
│ │ ├── ReferenceImpl.v
│ │ ├── ResolveCache.v
│ │ ├── ResultPropagationExpression.v
│ │ ├── SelectiveImportList.v
│ │ ├── SelectorExpression.v
│ │ ├── Signature.v
│ │ ├── SignatureOwner.v
│ │ ├── SliceExpression.v
│ │ ├── SourceFile.v
│ │ ├── StaticMethodDeclaration.v
│ │ ├── StaticReceiver.v
│ │ ├── StringLiteral.v
│ │ ├── StructDeclaration.v
│ │ ├── StructFieldScope.v
│ │ ├── StubBase.v
│ │ ├── StubBasedPsiElement.v
│ │ ├── StubElement.v
│ │ ├── StubIndex.v
│ │ ├── StubIndexSink.v
│ │ ├── StubList.v
│ │ ├── StubbedElementTypeImpl.v
│ │ ├── TextRange.v
│ │ ├── TreeWalker.v
│ │ ├── TypeAliasDeclaration.v
│ │ ├── TypeCache.v
│ │ ├── TypeInferer.v
│ │ ├── TypeInitializer.v
│ │ ├── TypeReferenceExpression.v
│ │ ├── UnaryExpression.v
│ │ ├── UnsafeExpression.v
│ │ ├── ValueAttribute.v
│ │ ├── VarDeclaration.v
│ │ ├── VarDefinition.v
│ │ ├── VisibilityModifiers.v
│ │ ├── doc_comment_extractor.v
│ │ ├── element_factory.v
│ │ ├── search/
│ │ │ ├── ReferencesSearch.v
│ │ │ ├── common.v
│ │ │ ├── implementations.v
│ │ │ ├── implmenttion_methods.v
│ │ │ ├── super_methods.v
│ │ │ └── supers.v
│ │ ├── types/
│ │ │ ├── AliasType.v
│ │ │ ├── ArrayType.v
│ │ │ ├── BaseNamedType.v
│ │ │ ├── BaseType.v
│ │ │ ├── ChannelType.v
│ │ │ ├── EnumType.v
│ │ │ ├── FixedArrayType.v
│ │ │ ├── FunctionType.v
│ │ │ ├── GenericInstantiationType.v
│ │ │ ├── GenericType.v
│ │ │ ├── InterfaceType.v
│ │ │ ├── MapType.v
│ │ │ ├── MultiReturnType.v
│ │ │ ├── OptionType.v
│ │ │ ├── PointerType.v
│ │ │ ├── PrimitiveType.v
│ │ │ ├── ResultType.v
│ │ │ ├── StructType.v
│ │ │ ├── ThreadType.v
│ │ │ ├── Type.v
│ │ │ ├── TypeVisitor.v
│ │ │ ├── UnknownType.v
│ │ │ ├── VoidPtrType.v
│ │ │ └── helpers.v
│ │ ├── types_util.v
│ │ ├── utils.v
│ │ └── walk.v
│ ├── bytes/
│ │ ├── README.md
│ │ ├── deserialize.v
│ │ ├── serialization_test.v
│ │ └── serialize.v
│ ├── check-updates.v
│ ├── clear-cache.v
│ ├── config/
│ │ ├── EditorConfig.v
│ │ └── constants.v
│ ├── init.v
│ ├── jsonrpc/
│ │ ├── README.md
│ │ ├── jsonrpc.v
│ │ ├── jsonrpc_test.v
│ │ ├── server.v
│ │ ├── server_test.v
│ │ └── server_test_utils/
│ │ └── server_test_utils.v
│ ├── loglib/
│ │ ├── ColorMode.v
│ │ ├── Entry.v
│ │ ├── Formatter.v
│ │ ├── LogLevel.v
│ │ ├── Logger.v
│ │ ├── TextFormatter.v
│ │ ├── log.v
│ │ └── utils.v
│ ├── lsp/
│ │ ├── README.md
│ │ ├── capabilities.v
│ │ ├── client.v
│ │ ├── code_action.v
│ │ ├── code_lens.v
│ │ ├── color_presentation.v
│ │ ├── completion.v
│ │ ├── declaration.v
│ │ ├── definition.v
│ │ ├── diagnostics.v
│ │ ├── document_color.v
│ │ ├── document_highlight.v
│ │ ├── document_link.v
│ │ ├── document_symbol.v
│ │ ├── ext.v
│ │ ├── file_resource.v
│ │ ├── folding_range.v
│ │ ├── formatting.v
│ │ ├── hover.v
│ │ ├── implementation.v
│ │ ├── initialize.v
│ │ ├── inlay_hint.v
│ │ ├── log/
│ │ │ ├── log.v
│ │ │ └── log_test.v
│ │ ├── lsp.v
│ │ ├── lsp_test.v
│ │ ├── progress.v
│ │ ├── references.v
│ │ ├── rename.v
│ │ ├── semantic_tokens.v
│ │ ├── signature_help.v
│ │ ├── symbol.v
│ │ ├── text_document.v
│ │ ├── text_sync.v
│ │ ├── window.v
│ │ └── workspace.v
│ ├── main.v
│ ├── metadata/
│ │ ├── metadata.v
│ │ └── stubs/
│ │ ├── README.md
│ │ ├── arrays.v
│ │ ├── attributes/
│ │ │ ├── Attribute.v
│ │ │ ├── Deprecated.v
│ │ │ ├── DeprecatedAfter.v
│ │ │ ├── Flag.v
│ │ │ ├── Heap.v
│ │ │ ├── Json.v
│ │ │ ├── JsonAsNumber.v
│ │ │ ├── Manualfree.v
│ │ │ ├── Noinit.v
│ │ │ ├── Noreturn.v
│ │ │ ├── Omitempty.v
│ │ │ ├── Table.v
│ │ │ └── Unsafe.v
│ │ ├── builtin_compile_time.v
│ │ ├── c_decl.v
│ │ ├── channels.v
│ │ ├── compile_time.v
│ │ ├── compile_time_constants.v
│ │ ├── compile_time_reflection.v
│ │ ├── errors.v
│ │ ├── implicit.v
│ │ ├── primitives.v
│ │ ├── threads.v
│ │ └── vweb.v
│ ├── project/
│ │ ├── flavors/
│ │ │ ├── MacToolchainFlavor.v
│ │ │ ├── SymlinkToolchainFlavor.v
│ │ │ ├── SysPathToolchainFlavor.v
│ │ │ ├── ToolchainFlavor.v
│ │ │ ├── UserHomeToolchainFlavor.v
│ │ │ ├── VenvToolchainFlavor.v
│ │ │ └── WinToolchainFlavor.v
│ │ └── project.v
│ ├── server/
│ │ ├── BackgroundThread.v
│ │ ├── README.md
│ │ ├── ResponseWriter.v
│ │ ├── code_lens/
│ │ │ └── CodeLensVisitor.v
│ │ ├── completion/
│ │ │ ├── CompletionContext.v
│ │ │ ├── CompletionProvider.v
│ │ │ ├── CompletionResultSet.v
│ │ │ └── providers/
│ │ │ ├── AssertCompletionProvider.v
│ │ │ ├── AttributesCompletionProvider.v
│ │ │ ├── CompileTimeConstantCompletionProvider.v
│ │ │ ├── FunctionLikeCompletionProvider.v
│ │ │ ├── ImportsCompletionProvider.v
│ │ │ ├── InitsCompletionProvider.v
│ │ │ ├── JsonAttributeCompletionProvider.v
│ │ │ ├── KeywordsCompletionProvider.v
│ │ │ ├── LoopKeywordsCompletionProvider.v
│ │ │ ├── ModuleNameCompletionProvider.v
│ │ │ ├── ModulesImportProvider.v
│ │ │ ├── NilKeywordCompletionProvider.v
│ │ │ ├── OrBlockExpressionCompletionProvider.v
│ │ │ ├── PureBlockExpressionCompletionProvider.v
│ │ │ ├── PureBlockStatementCompletionProvider.v
│ │ │ ├── ReferenceCompletionProcessor.v
│ │ │ ├── ReferenceCompletionProvider.v
│ │ │ ├── ReturnCompletionProvider.v
│ │ │ ├── StructLiteralCompletion.v
│ │ │ └── TopLevelCompletionProvider.v
│ │ ├── diagnostics.v
│ │ ├── documentation/
│ │ │ ├── keywords_provider.v
│ │ │ └── provider.v
│ │ ├── features_code_actions.v
│ │ ├── features_code_lens.v
│ │ ├── features_completion.v
│ │ ├── features_definition.v
│ │ ├── features_did_change.v
│ │ ├── features_did_change_watched_files.v
│ │ ├── features_did_close.v
│ │ ├── features_did_open.v
│ │ ├── features_did_save.v
│ │ ├── features_document_highlight.v
│ │ ├── features_document_symbol.v
│ │ ├── features_execute_command.v
│ │ ├── features_folding_range.v
│ │ ├── features_formatting.v
│ │ ├── features_formatting_test.v
│ │ ├── features_hover.v
│ │ ├── features_implementation.v
│ │ ├── features_inlay_hints.v
│ │ ├── features_prepare_rename.v
│ │ ├── features_references.v
│ │ ├── features_rename.v
│ │ ├── features_semantic_tokens.v
│ │ ├── features_signature_help.v
│ │ ├── features_type_definition.v
│ │ ├── features_view_stub_tree.v
│ │ ├── features_workspace_symbol.v
│ │ ├── file_diff/
│ │ │ └── Diff.v
│ │ ├── folding/
│ │ │ └── visitor.v
│ │ ├── general.v
│ │ ├── hints/
│ │ │ └── InlayHintsVisitor.v
│ │ ├── inspections/
│ │ │ ├── Report.v
│ │ │ ├── ReportsSource.v
│ │ │ └── compiler/
│ │ │ ├── CompilerReportsSource.v
│ │ │ └── utils.v
│ │ ├── intentions/
│ │ │ ├── AddFlagAttributeIntention.v
│ │ │ ├── AddHeapAttributeIntention.v
│ │ │ ├── CompilerQuickFix.v
│ │ │ ├── ImportModuleQuickFix.v
│ │ │ ├── Intention.v
│ │ │ ├── MakeMutableQuickFix.v
│ │ │ ├── MakePublicIntention.v
│ │ │ └── utils.v
│ │ ├── language_server.v
│ │ ├── progress/
│ │ │ └── progress.v
│ │ ├── protocol/
│ │ │ └── Client.v
│ │ ├── semantic/
│ │ │ ├── DumbAwareSemanticVisitor.v
│ │ │ ├── ResolvingSemanticVisitor.v
│ │ │ ├── SemanticToken.v
│ │ │ ├── constants.v
│ │ │ └── encode.v
│ │ ├── setup_test.v
│ │ ├── tform/
│ │ │ ├── README.md
│ │ │ └── tform.v
│ │ └── workspace/
│ │ └── ProjectResolver.v
│ ├── streams/
│ │ └── streams.v
│ ├── testing/
│ │ ├── Benchmark.v
│ │ ├── BenchmarkRunner.v
│ │ ├── Test.v
│ │ ├── TestFixture.v
│ │ ├── Tester.v
│ │ └── client/
│ │ └── TestClient.v
│ ├── tests/
│ │ ├── analyzer_test.v
│ │ ├── bench.v
│ │ ├── completion.v
│ │ ├── definitions.v
│ │ ├── documentation.v
│ │ ├── implementations.v
│ │ ├── supers.v
│ │ ├── testdata/
│ │ │ ├── benchmarks/
│ │ │ │ ├── checker.vv
│ │ │ │ └── inlay_hints.vv
│ │ │ ├── documentation/
│ │ │ │ ├── rendered.vv
│ │ │ │ ├── rendered.vv.md
│ │ │ │ ├── stubs.vv
│ │ │ │ └── stubs.vv.md
│ │ │ └── types/
│ │ │ ├── bool_operators.vv
│ │ │ ├── call_expression.vv
│ │ │ ├── chan_type.vv
│ │ │ ├── constants.vv
│ │ │ ├── fields.vv
│ │ │ ├── for_loop.vv
│ │ │ ├── for_loops.vv
│ │ │ ├── function_literal.vv
│ │ │ ├── generics.vv
│ │ │ ├── if_expression.vv
│ │ │ ├── json_decode.vv
│ │ │ ├── literals.vv
│ │ │ ├── map_init_expression.vv
│ │ │ ├── match_expression.vv
│ │ │ ├── parameters.vv
│ │ │ ├── pointers.vv
│ │ │ ├── receiver.vv
│ │ │ ├── slice_and_index_expression.vv
│ │ │ ├── type_initializer.vv
│ │ │ └── unsafe_expression.vv
│ │ └── types.v
│ ├── tools/
│ │ └── project-checker.v
│ ├── up.v
│ ├── utils/
│ │ ├── text_utils.v
│ │ └── text_utils_test.v
│ └── utils.v
├── tree_sitter_v/
│ ├── .gitattributes
│ ├── .gitignore
│ ├── .prettierrc.js
│ ├── LICENSE
│ ├── README.md
│ ├── bindings/
│ │ ├── bindings.c.v
│ │ ├── bindings.h
│ │ ├── bindings.v
│ │ ├── generate_types.vsh
│ │ ├── node_types.v
│ │ └── simple_test.v
│ ├── examples/
│ │ ├── cursor.v
│ │ ├── simple.v
│ │ └── with_old_tree.v
│ ├── grammar.js
│ ├── package.json
│ ├── queries/
│ │ ├── helix.highlights.scm
│ │ └── highlights.scm
│ ├── src/
│ │ ├── grammar.json
│ │ ├── node-types.json
│ │ ├── parser.c
│ │ └── tree_sitter/
│ │ ├── alloc.h
│ │ ├── array.h
│ │ └── parser.h
│ ├── test/
│ │ └── corpus/
│ │ ├── anon_struct.txt
│ │ ├── array_creation.txt
│ │ ├── assert_statement.txt
│ │ ├── attributes.txt
│ │ ├── bitshift_left.txt
│ │ ├── call_expression.txt
│ │ ├── channels.txt
│ │ ├── comments.txt
│ │ ├── compile_time.txt
│ │ ├── compile_time_selector_expression.txt
│ │ ├── const_declaration.txt
│ │ ├── enum_declaration.txt
│ │ ├── enum_fetch.txt
│ │ ├── error_propagation.txt
│ │ ├── expression_list.txt
│ │ ├── for_statement.txt
│ │ ├── function_declaration.txt
│ │ ├── function_literal.txt
│ │ ├── generics.txt
│ │ ├── global_var_declaration.txt
│ │ ├── hash_statement.txt
│ │ ├── if_expression.txt
│ │ ├── imports.txt
│ │ ├── in_expression.txt
│ │ ├── interface_declaration.txt
│ │ ├── is_as_expression.txt
│ │ ├── json_call.txt
│ │ ├── labeled_statement.txt
│ │ ├── lock_expression.txt
│ │ ├── map_init_expression.txt
│ │ ├── match_expression.txt
│ │ ├── method_declaration.txt
│ │ ├── module_clause.txt
│ │ ├── safe_access.txt
│ │ ├── select_expression.txt
│ │ ├── shebang.txt
│ │ ├── slice_expression.txt
│ │ ├── source_file.txt
│ │ ├── spawn_expression.txt
│ │ ├── special_call_expression.txt
│ │ ├── static_method_declaration.txt
│ │ ├── string_literal.txt
│ │ ├── struct_declaration.txt
│ │ ├── type_declaration.txt
│ │ ├── type_initializer.txt
│ │ ├── types.txt
│ │ ├── unsafe_expression.txt
│ │ └── var_declaration.txt
│ └── tree-sitter.json
└── v.mod
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.{v,js,json}]
indent_style = tab
[*.md]
max_line_length = 100
================================================
FILE: .gitattributes
================================================
* text=auto eol=lf
*.bat eol=crlf
**/*.v linguist-language=V
**/*.vv linguist-language=V
**/*.vsh linguist-language=V
**/v.mod linguist-language=V
================================================
FILE: .github/ISSUE_TEMPLATE/bug-report.yml
================================================
---
name: 🐛 Bug Report
description: Report a bug
title: (please, provide bug report summary here)
labels: [ bug ]
body:
- type: textarea
id: description
attributes:
label: Describe the bug
description: What is the problem? A clear and concise description of the bug.
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected Behavior
description: What did you expect to happen?
validations:
required: true
- type: textarea
id: current
attributes:
label: Current Behavior
description: |
What actually happened?
Please include full errors, uncaught exceptions, stack traces, and relevant logs.
If service/functions responses are relevant, please include wire logs.
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Reproduction Steps
description: |
Provide a self-contained, concise snippet of code that can be used to reproduce the issue.
For more complex issues provide a repo with the smallest sample that reproduces the bug.
Avoid including business logic or unrelated code, it makes diagnosis more difficult.
The code sample should be an SSCCE. See http://sscce.org/ for details.
In short, please provide a code sample that we can copy/paste, run and reproduce.
validations:
required: true
- type: textarea
id: solution
attributes:
label: Possible Solution
description: Suggest a fix/reason for the bug
validations:
required: false
- type: textarea
id: context
attributes:
label: Additional Information/Context
description: |
Anything else that might be relevant for troubleshooting this bug.
Providing context helps us come up with a solution that is most useful in the real world.
validations:
required: false
- type: textarea
id: environment
attributes:
label: Environment details (`v doctor` output)
validations:
required: true
- type: input
id: editor-name
attributes:
label: Editor name
validations:
required: true
- type: input
id: v-analyzer-version
attributes:
label: v-analyzer Version
description: |
`v-analyzer --version` or in Command Palette: `v-analyzer: Show v-analyzer server version`
validations:
required: true
- type: input
id: vs-code-extension-version
attributes:
label: VS Code Extension Version
description: |
If you are using the VS Code extension, please provide the version of the extension.
validations:
required: false
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
- name: 💬 General Questions and Answers about v-analyzer
url: https://github.com/vlang/v-analyzer/discussions/categories/q-a
about: You can ask and answer questions about v-analyzer in the discussions forum.
- name: 💬 V Discord Server
url: https://discord.gg/vlang
about: You can join our Discord server for real time discussion and support
================================================
FILE: .github/ISSUE_TEMPLATE/feature-request.yml
================================================
---
name: 🚀 Feature Request
description: Suggest an idea for v-analyzer project
title: "(short issue description)"
labels: [ feature-request ]
body:
- type: textarea
id: description
attributes:
label: Describe the feature
description: A clear and concise description of the feature you are proposing.
validations:
required: true
- type: textarea
id: use-case
attributes:
label: Use Case
description: |
Why do you need this feature? For example: "I'm always frustrated when..."
validations:
required: true
- type: textarea
id: other
attributes:
label: Other Information
description: |
Any alternative solutions or features you considered, a more detailed explanation, stack traces, related issues, links for context, etc.
validations:
required: false
================================================
FILE: .github/workflows/analyzer_tests.yml
================================================
name: Analyzer CI
on:
push:
paths-ignore:
- '.github/**'
- '!**/analyzer_tests.yml'
- '!**/version_test.vv'
- 'editors/code/**'
- '**/*.md'
pull_request:
paths-ignore:
- '.github/**'
- '!**/analyzer_tests.yml'
- '!**/version_test.vv'
- 'editors/code/**'
- '**/*.md'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
jobs:
test:
strategy:
matrix:
include:
- os: macos-latest
cc: clang
- os: ubuntu-latest
cc: gcc
- os: windows-latest
cc: gcc
fail-fast: false
runs-on: ${{ matrix.os }}
env:
VFLAGS: -cg -cc ${{ matrix.cc }}
steps:
- name: Install V
id: install-v
uses: vlang/setup-v@v1.4
with:
check-latest: true
- name: Checkout v-analyzer
uses: actions/checkout@v4
with:
submodules: true
- name: Run tests
run: v test .
- name: Install v-analyzer
if: runner.os != 'Windows'
run: |
# Build and install v-analyzer at the head ref of the submitted changes.
v build.vsh
sudo mv ./bin/v-analyzer /usr/local/bin/v-analyzer
v-analyzer --version
- name: Verify version
# TODO: include Windows
if: runner.os != 'Windows'
run: v .github/workflows/version_test.vv
================================================
FILE: .github/workflows/build_ci.yml
================================================
name: Build CI
on:
push:
paths-ignore:
- '.github/**'
- '!**/build_ci.yml'
- '**/test/**'
- '**/tests/**'
- '**/*.md'
- '**/test_*.v'
- '**/*_test.v'
pull_request:
paths-ignore:
- '.github/**'
- '!**/build_ci.yml'
- '**/test/**'
- '**/tests/**'
- '**/*.md'
- '**/test_*.v'
- '**/*_test.v'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
mode: [debug, dev, release]
fail-fast: false
runs-on: ${{ matrix.os }}
steps:
- name: Install V
id: install-v
uses: vlang/setup-v@v1.4
with:
check-latest: true
- name: Checkout v-analyzer
uses: actions/checkout@v4
with:
submodules: true
- name: Build
run: v build.vsh ${{ matrix.mode }}
- name: Check if the build is successful
run: ./bin/v-analyzer --version
================================================
FILE: .github/workflows/install_ci.yml
================================================
name: Install CI
on:
push:
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
jobs:
install:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
fail-fast: false
runs-on: ${{ matrix.os }}
steps:
- name: Install V
uses: vlang/setup-v@v1.4
with:
check-latest: true
- name: Install via webscript
run: v download -RD https://raw.githubusercontent.com/vlang/v-analyzer/main/install.vsh
- name: Verify installation success
run: ~/.config/v-analyzer/bin/v-analyzer --version
- name: Checkout v-analyzer
uses: actions/checkout@v4
- name: Cleanup and prepare installation
shell: bash
run: |
rm -r ~/.config/v-analyzer/bin/v-analyzer
mkdir Downloads
mv ./install.vsh Downloads/install.vsh
# While the webscript installation step tests the general functionality of direct installation via the web,
# regressions related to the install script would only become visible AFTER pushing changes to the main branch.
# We reduce the potential for errors by simulating downloading the script and running it separately.
- name: Install via local script
run: cd Downloads && v install.vsh
- name: Verify installation success
run: ~/.config/v-analyzer/bin/v-analyzer --version
================================================
FILE: .github/workflows/lint.yml
================================================
name: Lint
on:
push:
paths:
- '**.v'
- '**.vsh'
- '**/lint.yml'
pull_request:
paths:
- '**.v'
- '**.vsh'
- '**/lint.yml'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
jobs:
fmt:
runs-on: ubuntu-latest
steps:
- name: Install V
id: install-v
uses: vlang/setup-v@v1.4
with:
check-latest: true
- name: Checkout v-analyzer
uses: actions/checkout@v4
- name: Verify formatting
run: |
set +e
v fmt -c .
exit_code=$?
# Don't fail on internal errors.
if [[ $exit_code -ne 0 && $exit_code -ne 5 ]]; then
v fmt -diff .
exit 1
fi
================================================
FILE: .github/workflows/release.yml
================================================
name: Release
on:
workflow_dispatch:
push:
branches:
- main
tags:
- '*'
paths-ignore:
- '**/test/**'
- '**/tests/**'
- '**/test_*.v'
- '**/*_test.v'
- '**/*.md'
- '.github/**'
- '!**/release.yml'
permissions:
contents: write
env:
PROJECT_NAME: v-analyzer
jobs:
build-v-analyzer:
strategy:
matrix:
target: [linux-x86_64, darwin-x86_64, darwin-arm64, windows-x86_64]
build_type: [dev, debug, release]
include:
- target: windows-x86_64
os: windows-latest
bin_ext: .exe
- target: linux-x86_64
os: ubuntu-latest
- target: darwin-x86_64
os: macos-13
- target: darwin-arm64
os: macos-latest
- build_type: release
cflags: -fno-exceptions -fno-unwind-tables -fno-asynchronous-unwind-tables -frename-registers -ftree-vectorize
fail-fast: false
runs-on: ${{ matrix.os }}
steps:
- name: Install V
uses: vlang/setup-v@v1.4
with:
check-latest: true
- name: Checkout v-analyzer
uses: actions/checkout@v4
with:
submodules: true
- name: Compile ${{ matrix.build_type }} build
id: compile
env:
CFLAGS: ${{ matrix.cflags }} -pipe
VFLAGS: ${{ matrix.vflags }}
shell: bash
run: |
v build.vsh ${{ matrix.build_type }}
if [[ "${{ matrix.os }}" != "macos-latest" ]]; then
strip --strip-unneeded ./bin/v-analyzer${{ matrix.bin_ext }}
strip --discard-all ./bin/v-analyzer${{ matrix.bin_ext }}
fi
if [[ "${{ matrix.build_type }}" != "release" ]]; then
echo "SUFFIX=-${{ matrix.build_type }}" >> "$GITHUB_OUTPUT"
fi
- name: Create artifact
env:
ARTIFACT_NAME: ${{ env.PROJECT_NAME }}-${{ matrix.target }}${{ steps.compile.outputs.SUFFIX }}
shell: bash
run: 7z a -tzip ${{ env.ARTIFACT_NAME }}.zip ./bin/v-analyzer${{ matrix.bin_ext }}
- name: Upload artifact
uses: actions/upload-artifact@v4
env:
ARTIFACT_NAME: ${{ env.PROJECT_NAME }}-${{ matrix.target }}${{ steps.compile.outputs.SUFFIX }}
with:
name: ${{ env.ARTIFACT_NAME }}
path: ${{ env.ARTIFACT_NAME }}.zip
build-vscode:
runs-on: ubuntu-latest
steps:
- name: Install Nodejs
uses: actions/setup-node@v4
with:
node-version: 20
- name: Checkout v-analyzer
uses: actions/checkout@v4
- name: Compile
id: compile
shell: bash
run: |
pushd editors/code
version=$(sed -E -n 's/^\s+"version": "([^"]+)".*/\1/gp' package.json)
echo "VERSION=$version" >> "$GITHUB_OUTPUT"
retry=0
echo "[+] Install dependencies"
npm install
echo "[+] Package start"
set +e
while [[ ${retry} < 3 ]]; do
if npm run package; then
echo "[+] Package done"
break
else
sleep 1
let retry++
echo "[+] Package fail, ${retry} retry"
fi
done
set -e
popd
- name: Upload artifact
uses: actions/upload-artifact@v4
env:
ARTIFACT_NAME: vscode-${{ env.PROJECT_NAME }}-${{ steps.compile.outputs.VERSION }}
with:
name: ${{ env.ARTIFACT_NAME }}
path: editors/code/${{ env.ARTIFACT_NAME }}.vsix
release:
runs-on: ubuntu-latest
needs: [build-v-analyzer, build-vscode]
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: ${{ env.PROJECT_NAME }}
merge-multiple: true
- name: Update nightly tag
if: github.ref_type != 'tag'
uses: richardsimko/update-tag@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: nightly
- name: Generate development release body
if: github.ref_type != 'tag'
id: generate_body
shell: bash
run: |
now=$(date -u +'%Y-%m-%d %H:%M:%S UTC')
echo "BODY=Generated on <samp>$now</samp> from commit ${{ github.sha }}." >> "$GITHUB_OUTPUT"
- name: Release development version
if: github.ref_type != 'tag'
uses: ncipollo/release-action@v1
with:
artifacts: ${{ env.PROJECT_NAME }}/*
tag: nightly
body: ${{ steps.generate_body.outputs.BODY }}
name: v-analyzer development build
allowUpdates: true
prerelease: true
- name: Release latest version
if: github.ref_type == 'tag'
uses: ncipollo/release-action@v1
with:
artifacts: ${{ env.PROJECT_NAME }}/*
allowUpdates: true
omitBodyDuringUpdate: true
omitNameDuringUpdate: true
================================================
FILE: .github/workflows/tree_sitter_v.yml
================================================
name: Tree-sitter CI
on:
push:
paths:
- 'tree_sitter_v/**'
- '**/test_tree_sitter_v.yml'
pull_request:
paths:
- 'tree_sitter_v/**'
- '**/test_tree_sitter_v.yml'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
jobs:
test-grammar:
runs-on: ubuntu-latest
defaults:
run:
working-directory: tree_sitter_v
steps:
- name: Checkout v-analyzer
uses: actions/checkout@v4
- name: Install dependencies
run: npm update
- name: Run tests
run: npm run test
test-bindings:
runs-on: ubuntu-latest
steps:
- name: Install V
id: install-v
uses: vlang/setup-v@v1.4
with:
check-latest: true
- name: Checkout v-analyzer
uses: actions/checkout@v4
with:
submodules: true
- name: Run tests
run: v test tree_sitter_v/bindings
- name: Run examples
run: |
cd tree_sitter_v
exit_code=0
for example in $(find -wholename '*/examples/*.v'); do
v run $example
if [ $? -ne 0 ]; then
exit_code=1
echo $exit_code
echo "Failed to run example \`$example\`"
fi
done
exit $exit_code
================================================
FILE: .github/workflows/version_test.vv
================================================
import os
import v.vmod
fn test_version() {
if os.getenv('CI') != 'true' {
eprintln('WARNING: expecting usage in combination with CI workflow.')
}
for k, v in os.environ() {
println('>>> env key: ${k} | value: ${v}')
}
sha := os.getenv('GITHUB_WORKFLOW_SHA')
assert sha.len > 10
git_ref := sha.trim_space()[..7]
manifest := vmod.decode(@VMOD_FILE)!
assert manifest.name == 'v-analyzer'
// Move out of the project directory to ensure that we exclude the possiblity of
// deriving the commit reference from v-analyzer's directory at program startup.
os.chdir('/tmp/')!
analyzer_version := os.execute_opt('v-analyzer --version')!.output.all_after_last(' ').trim_space()
assert '${manifest.version}.${git_ref}' == analyzer_version
}
================================================
FILE: .github/workflows/vscode_extension_tests.yml
================================================
name: VS Code Extension CI
on:
push:
paths:
- 'editors/code/**'
- '**/vscode_extension_tests.yml'
pull_request:
paths:
- 'editors/code/**'
- '**/vscode_extension_tests.yml'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
fail-fast: false
name: Test VS Code Extension on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
steps:
- name: Install Nodejs
uses: actions/setup-node@v4
with:
node-version: 20
- name: Checkout v-analyzer
uses: actions/checkout@v4
- name: Install dependencies
run: cd editors/code && npm update
- name: Run tests
run: cd editors/code && npm run test
================================================
FILE: .gitignore
================================================
# Binaries for programs and plugins
main
v-analyzer
**/*.exe
**/*.exp
**/*.exe~
**/*.so
**/*.dylib
**/*.dll
**/*.log
**/*.lib
**/*.obj
# Ignore binary output folders
bin
build
# Ignore cache folders
cache
# Ignore common editor/system specific metadata
.DS_Store
.idea
.vscode
*.iml
# ENV
.env
# vweb and database
*.db
# generated executables on unix:
install
generate_types
cmd/v-analyzer/v-analyzer
================================================
FILE: .gitmodules
================================================
[submodule "tree_sitter_v/bindings/core"]
path = tree_sitter_v/bindings/core
url = https://github.com/tree-sitter/tree-sitter.git
================================================
FILE: .v-analyzer/config.toml
================================================
# Specifies the path to the V installation directory with `v` executable.
# If not set, the plugin will try to find it on its own.
# Basically, you don't need to set it.
#custom_vroot = "~/v"
# Specifies the path where to store the cache.
# By default, it is stored in the system's cache directory.
# You can set it to `./` to store the cache in the project's directory, this is useful
# if you want to debug the analyzer.
# Basically, you don't need to set it.
custom_cache_dir = "./cache"
# Specifies whenever to enable semantic tokens or not.
# - `full` — enables all semantic tokens. In this mode analyzer resolves all symbols
# in the file to provide the most accurate highlighting.
# - `syntax` — enables only syntax tokens, such tokens highlight structural elements
# such as field names or import names.
# The fastest option, which is always enabled when the file contains more than 1000 lines.
# - `none` — disables semantic tokens.
# By default, `full` for files with less than 1000 lines, `syntax` for files with more.
enable_semantic_tokens = "full"
# Specifies inlay hints to show.
[inlay_hints]
# Specifies whenever to enable inlay hints or not.
# By default, they are enabled.
enable = true
# Specifies whenever to show type hints for ranges or not.
# Example:
# ```
# 0 ≤ .. < 10
# ^ ^
# ```
# or:
# ```
# a[0 ≤ .. < 10]
# ^ ^
# ```
enable_range_hints = true
# Specifies whenever to show type hints for variables or not.
# Example:
# ```
# name : Foo := foo()
# ^^^^^
# ```
enable_type_hints = true
# Specifies whenever to show hints for implicit err variables or not.
# Example:
# ```
# foo() or { err ->
# ^^^^^^
# }
# ```
enable_implicit_err_hints = true
# Specifies whenever to show hints for function parameters in call or not.
# Example:
# ```
# fn foo(a int, b int) int {}
#
# foo(a: 1, b: 2)
# ^^ ^^
enable_parameter_name_hints = true
# Specifies whenever to show type hints for constants or not.
# Example:
# ```
# const foo : int = 1
# ^^^^^
# ```
enable_constant_type_hints = true
# Specifies whenever to show hints for enum field values or not.
# Example:
# ```
# enum Foo {
# bar = 0
# ^^^
# baz = 1
# ^^^
# }
# ```
enable_enum_field_value_hints = true
# Specifies code lenses to show.
[code_lens]
# Specifies whenever to enable code lenses or not.
# By default, they are enabled.
enable = true
# Specifies whenever to show code lenses for main function to run current directory or not.
# Example:
# ```
# ▶ Run
# fn main() {}
# ```
enable_run_lens = true
# Specifies whenever to show code lenses for interface inheritors or not.
# Example:
# ```
# 2 implementations
# interface Foo {}
# ```
enable_inheritors_lens = true
# Specifies whenever to show code lenses for structs implementing interfaces or not.
# Example:
# ```
# implemented 2 interfaces
# struct Boo {}
# ```
enable_super_interfaces_lens = true
# Specifies whenever to show code lenses for test functions to run test or whole file or not.
# Example:
# ```
# ▶ Run test | all file tests
# fn test_foo() {}
# ```
# Note: "all file tests" is shown only for the first test function in the file.
enable_run_tests_lens = true
================================================
FILE: CHANGELOG.md
================================================
# v-analyser Changelog
## [0.0.6] - 2025/02/27
Sixth public release.
This release contains mainly installation issue fixes.
You can now install it on all platforms, using an uniform command:
`v download -RD https://raw.githubusercontent.com/vlang/v-analyzer/main/install.vsh`
It also has improvements to the diagnostic output of the build and install scripts.
V-analyzer has also been fixed to compile with the latest and stricter V versions.
The last notable change is the upgrade of our tree_sitter bindings.
## [0.0.5] - 2024/11/13
Fifth public release.
### New features and enhancements:
∙ analyzer: add module index paths and wrapper module path as code block (#102)
∙ analyzer: add `pub` access modifier to publicly used struct fields (#85)
∙ analyzer: create vtmp directory for check-updates/up commands (#125)
∙ analyzer: fix anonymous functions are self-invoking type mismatch (#48)
∙ analyzer: fix build for paths with spaces (#83)
∙ analyzer: Fix `code_description` (fixes zed-v) (#119)
∙ analyzer: fix doc_comment_extractor (#50)
∙ analyzer: fix vmodules_root check in setup_vpaths (#95)
∙ analyzer: fix work progress shown as 0% when finishing indexing (#84)
∙ analyzer: improve setup, extend client and log messages (#100)
∙ analyzer: merge creation of vtmp directory for check-updates/up commands (#126)
∙ analyzer: move const used to download install.vsh script (#127)
∙ analyzer: reduce nesting in setup_toolchain
∙ analyzer: rework path handling to simplify and reduce load (#86)
∙ analyzer: simplify, improve completion context detection
∙ analyzer: simplify, remove unnecessary abstraction
∙ analyzer: update deprecated
∙ analyzer: update deprecated unix time access (#99)
∙ analyzer: use latest install script when updating (#81)
∙ analyzer: support shebang syntax (#34)
∙ analyzer: add inline struct field comments (#52)
∙ analyzer: use build version aware caching (#57)
∙ analyzer: update parser.c to align with the current development state (#13),
fixes highlighting for code in between 2 block comments
∙ build: follow a default directory structure for V projects (#25)
∙ build: update install.vsh to make repeated usage of path expand fn obsolete (#24)
∙ install: add debug and dev binaries install (#60)
∙ install: update the `git clone` options for install.vsh
∙ server: add `range_clause` highlight (#9)
∙ tree_sitter: correctly recognize shebang (#26)
∙ tree_sitter: support `for mut is` clause (#77)
∙ tree_sitter: support short lambda (#56)
∙ tree_sitter: detach shebang from comment (#32)
### Fixes to existing features:
∙ fix: building of IndexingRoot.v
∙ fix: fix behaviour of pascal_case_to_snake_case after V commit 5329a0a67
∙ fix: fix goto definition for field names. (#135)
∙ fix: fix hanging on vfmt-ing large files on windows (#130)
∙ fix: fix install.vsh
∙ fix: fix module index by making sure to index also src/ and modules/ folders too (#138)
∙ fix: fix raw string with `\` (#64)
∙ fix: fix wrong macos target in release CI (#139)
∙ fix: move tools/project-checker.v to its corresponding directory
∙ fix: remove obsolete v.mod file in metadata submod
∙ fix: resolve compiler complaints (#27)
∙ fix: restore `.v` extensions for metadata/stubs, add test (#74)
∙ fix: src/analyzer/index/IndexingRoot.v
∙ fix: tree-sitter bindings examples, extend workflow to run examples
∙ fix: update npm `generate` script
∙ fix: version regression after eae3f91, add test (#61)
∙ tree_sitter: fix issuse bug (#42)
∙ tree_sitter: fix parser error on unescaped dollar identifier in string literals, add test (#79)
∙ tree_sitter: fix parsing of nested comments, extend tests (#76)
∙ tree_sitter: fix qualified type (#8)
∙ tree_sitter: rewrite comment grammar, detach line- and block comments (#71)
∙ tree_sitter: add sum type to tree node (#87)
### Documentation:
∙ docs: update README.md with more detailed instructions about
how to clone the project locally, fix `v check-md` warnings
∙ docs: fix for Neovim LSP/Mason (#122)
∙ docs: fix typo in readme
∙ docs: make submodule info in readme better visible and its commands easier to copy
∙ docs: refine readme before a potential structural update
∙ docs: update readme badges (#38)
∙ docs: update README.md mason install instructions (from https://github.com/v-analyzer/v-analyzer/pull/102)
∙ docs: update workflow path in tree-sitter badge
### Others:
∙ chore: fix typos (#44)
∙ chore: format all the files with the new fmt (#112)
∙ chore: format all the files with the new vfmt (#117)
∙ chore: format all the files with the new vfmt (#120)
∙ chore: format all the files with the new vfmt (#121)
∙ chore: run `v fmt -w install.vsh`
∙ chore: remove obsolete `.editorconfig` file in subdir, format
∙ chore: remove useless `compiler_flag` and copy `.exe` on windows (#108)
∙ chore: run the linter CI for changes made to just .vsh files too
∙ chore: updare editors/code dependencies (#33)
∙ chore: update deprecated `index_last` to `last_index` (#72)
∙ chore: update .gitattributes (#70)
∙ chore: update tree-sitter dependencies (#31)
∙ chore: use `.vv` extension for meta- and testdata files (#53)
∙ ci: add concurrency config (#67)
∙ ci: add linting and formatting automation to tree-sitter_v (#68)
∙ ci: add retry to release/build-vscode (#54)
∙ ci: add step to verify code formatting (#66)
∙ ci: add `.vsix` artifacts to release asset uploads (#47)
∙ ci: change `actions/upload-artifact@v3` to `actions/upload-artifact@v4` (#20)
∙ ci: change `vlang/setup-v@v1.3` to `vlang/setup-v@v1.4` (#19)
∙ ci: extend coverage in workflows
∙ ci: extend release workflow; automate assets uploads on tag creation (#39)
∙ ci: fix binary path in nightly ci (#36)
∙ ci: make sure that install_ci.yml is run for every change.
∙ ci: simplify, cover CI changes (#30)
∙ ci: use dedicated lint workflow to verify formatting (#97)
∙ refactor: decouple tree_sitter grammar and bindings (#37)
∙ refactor: simplify doc_comment_extractor, reduce load (#51)
∙ refactor: simplify grammar for `in/!in` and `is/!is`
∙ refactor: simplify path handling, remove unused utils (#40)
∙ refactor: store project metadata in metadata module (#59)
∙ refactor: update project structure (#69)
∙ tests: add test for the toolchain path setup (#96)
∙ tests: fix paths in bindings test, add test to workflow
∙ tests: fix analyzer test (#92)
∙ tests: update testdata (#45)
∙ tests: update tests to run with `v test` (#46)
∙ tree_sitter: add .prettierignore (#89)
∙ tree_sitter: improve clarity and quality of grammar (#78)
∙ tree_sitter: improve selector expression grammar
∙ tree_sitter: minimal cleanup, add optional `;` support between statements in {} blocks (#88)
∙ tree_sitter: Update dependencies (#18)
∙ tree_sitter: update tree-sitter-cli version to 0.22.2 (#41)
## [0.0.4-beta.1] - 2024/01/09
Forth public release.
Note: this is still a beta version, do expect bugs, and report them in
our [issues tracker](https://github.com/vlang/v-analyzer/issues) .
### Syntax enhancements & bug fixes:
∙ Update comment rule (#5).
∙ Fix string interpolation.
∙ Fix comment string parse error (https://github.com/v-analyzer/v-analyzer/pull/85).
∙ Fix attribute shading (#2).
∙ Fix `parameters`.
∙ Fix the type descriptions in the primitives.v stub.
∙ Simplify `handle_jsonrpc` (https://github.com/v-analyzer/v-analyzer/pull/86).
### VSCode Extension:
∙ Show the full path to the found v-analyzer binary,
when the VSCode extension runs its bootstrap, to make
diagnosing problems easier.
∙ Update the vscode extension package to vscode-v-analyzer-0.0.4.vsix
### CI enhancements:
∙ Use `ubuntu-20.04` for building the executables, to be compatible with more Linux distros.
∙ Use `v build.vsh debug` for nightly releases, so the executables produce usable backtraces.
∙ Silence the nightly releases, simplify the .yml script that builds
them (https://github.com/v-analyzer/v-analyzer/pull/83).
### Others:
∙ Update README.md to also include instructions for the mason.nvim Neovim
package manager (https://github.com/v-analyzer/v-analyzer/pull/90).
∙ Fix notices and warnings with latest V.
∙ Exclude .git/* and `_test.v` files from indexing by the language server,
see (https://github.com/v-analyzer/v-analyzer/pull/89).
∙ Use a git submodule for https://github.com/tree-sitter/tree-sitter.git, see
(https://github.com/v-analyzer/v-analyzer/pull/81).
∙ Use gcc for building on windows (https://github.com/v-analyzer/v-analyzer/pull/87).
∙ Update build scripts (https://github.com/v-analyzer/v-analyzer/pull/84).
∙ Fix version comparison in install.vsh .
∙ Migrate from https://github.com/v-analyzer/v-analyzer/ to https://github.com/vlang/v-analyzer/ .
## [0.0.3-beta.1] - 2023/12/13
Third public release.
### Syntax enhancements & bug fixes:
∙ Fix support for multiline comments (https://github.com/v-analyzer/v-analyzer/pull/75)
∙ Fix interface ref type highlight (https://github.com/v-analyzer/v-analyzer/pull/76)
∙ Fix support for struct field attributes (https://github.com/v-analyzer/v-analyzer/pull/74)
∙ Fix interface embeds and interface fields (https://github.com/v-analyzer/v-analyzer/pull/78)
∙ Fix `assert cond, message` statements (https://github.com/v-analyzer/v-analyzer/pull/65)
∙ Support @[attribute], fix signature, fix interface highlights
### Language server enhancements:
∙ Enable exit commands, to prevent lingering v-analyzer processes after
an editor restart (https://github.com/v-analyzer/v-analyzer/pull/77)
∙ server: fix NO_RESULT_CALLBACK_FOUND in neovim (https://github.com/v-analyzer/v-analyzer/pull/59)
∙ Build the v-analyzer executable on linux as static in release mode, to
make it more robust and usable in more distros.
### Others:
∙ docs: add neovim install instructions (https://github.com/v-analyzer/v-analyzer/pull/63)
∙ CI improvements, to make releases easier, and to keep the code quality high.
∙ Update the vscode extension package to vscode-v-analyzer-0.0.3.vsix
∙ Make `v-analyzer --version` show the build commit as well.
Note: this is still a beta version, expect bugs and please report them in
our [issues tracker](https://github.com/vlang/v-analyzer/issues) .
## [0.0.2-beta.1] - 2023/11/21
Second public release.
Small internal improvements to the documentation, ci, build scripts.
Fix compilation with latest V.
Update https://github.com/v-analyzer/v-tree-sitter from the latest
upstream version from https://github.com/tree-sitter/tree-sitter .
This is still a beta version, expect bugs and please report them in
our [issues tracker](https://github.com/vlang/v-analyzer/issues) .
## [0.0.1-beta.1] - 2023/07/03
First public release.
Please note that this is a beta version, so it may contain any bugs.
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2023 V Open Source Community Association (VOSCA)
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
================================================
<img width="200px" src="https://github.com/vlang/v-analyzer/blob/2d5d12e4b82ce8d695576957145ff27a33a988c2/docs/cover-light.png#gh-light-mode-only">
<img width="200px" src="https://github.com/vlang/v-analyzer/blob/2d5d12e4b82ce8d695576957145ff27a33a988c2/docs/cover-dark.png#gh-dark-mode-only">
# v-analyzer
[![][badge__vscode_ext]](https://marketplace.visualstudio.com/items?itemName=VOSCA.vscode-v-analyzer)
[![][badge__build_ci]](https://github.com/vlang/v-analyzer/actions/workflows/build_ci.yml?query=branch%3Amain)
[![][badge__tests_ci]](https://github.com/vlang/v-analyzer/actions/workflows/analyzer_tests.yml?query=branch%3Amain)
[![][badge__tree_sitter_ci]](https://github.com/vlang/v-analyzer/actions/workflows/tree_sitter_v.yml?query=branch%3Amain)
[![][badge__vscode_ext_ci]](https://github.com/vlang/v-analyzer/actions/workflows/vscode_extension_tests.yml?query=branch%3Amain)
Bring IDE features for the V programming language to VS Code, Vim, and other editors.
The features provided by v-analyzer include:
- code completion/IntelliSense
- go to definition, type definition
- find all references, document symbol, symbol renaming
- types and documentation on hover
- inlay hints for types and some construction like `or` block
- semantic syntax highlighting
- formatting
- signature help
## Installation
### Linux, macOS, Windows
Note: the following command will download `install.vsh` to the current directory, then
run it, and then *delete it*. If there is a pre-existing file with this name, make sure it
is safe, when it is overwritten/deleted, or change the current directory (the script itself
can be run from anywhere).
```sh
v download -RD https://raw.githubusercontent.com/vlang/v-analyzer/main/install.vsh
```
Note: if you get messages about `response does not start with HTTP/`, try going to the
main V repository, then do `./v -d use_openssl cmd/tools/vdownload.v` .
After that, retry the same command:
```sh
v download -RD https://raw.githubusercontent.com/vlang/v-analyzer/main/install.vsh
```
## Pre-built binaries
You can download pre-built binaries from the [release page](https://github.com/vlang/v-analyzer/releases).
Currently, we provide binaries for Linux (x64), macOS (x64 and ARM), and Windows (x64).
## Building from source
> [!NOTE]
> This repository uses Git submodules.
> In practice, this means that you either have to:
>
> ```sh
> git clone --filter=blob:none --recursive --shallow-submodules https://github.com/vlang/v-analyzer
> ```
>
> ... or, if you used just `git clone https://github.com/vlang/v-analyzer`, you can execute below
> inside your local `v-analyzer` clone:
>
> ```sh
> git submodule init && git submodule update
> ```
>
> If you do not do either, the symptom is that when you try to build v-analyzer, you will get a
> C compiler message, about `lib.c not found`
> [!TIP]
> On Windows, use GCC for building, as TCC can run into some issues.
Update V to the latest version:
```bash
v up
```
You can build a debug or release version of the binary.
The debug version will be slower, but faster to compile.
```bash
v build.vsh debug
```
```bash
v build.vsh release
```
The compiled binary will be located in the `bin/` folder.
## Setup
Add the `bin/` folder to your `$PATH` environment variable to make the `v-analyzer` command easily
accessible.
You can also specify the path to the binary in your VS Code settings:
```json
{
"v-analyzer.serverPath": "/path/to/v-analyzer/bin/v-analyzer"
}
```
> **Note**
> Restart VS Code after changing the settings or PATH.
### Config
v-analyzer is configured using global or local config.
The global config is located in `~/.config/v-analyzer/config.toml`, changing it will affect all
projects.
A local config can be created with the `v-analyzer init` command at the root of the project.
Once created, it will be in `./.v-analyzer/config.toml`.
Each setting in the config has a detailed description.
Pay attention to the `custom_vroot` setting, if v-analyzer cannot find where V was installed, then
you will need to specify the path to it manually in this field.
## Updating
To update `v-analyzer` to the latest version, run:
```bash
v-analyzer up
```
You can also update to a nightly version:
```bash
v-analyzer up --nightly
```
> **Note**
> In the nightly version you will get the latest changes, but they may not be stable!
## VS Code extension
The VS Code extension is available via the [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=VOSCA.vscode-v-analyzer).
The source code for extension is contained in the [`editors/code`](https://github.com/vlang/v-analyzer/tree/main/editors/code) folder of this repository.
## NVIM LSP / Mason
For Neovim users, v-analyzer is available via [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#v_analyzer) plugin.
It is part of the [mason registry](https://mason-registry.dev/registry/list#v-analyzer) and could be installed with both Neovim plugins:
- [mason.nvim](https://github.com/williamboman/mason.nvim) with `:MasonInstall v-analyzer` command
- [mason-lspconfig.nvim](https://github.com/williamboman/mason-lspconfig.nvim) with `:LspInstall` command
## Authors
- `jsonrpc`, `lsp`, `tree_sitter_v` modules written initially by
[VLS authors](https://github.com/vlang/vls) and after that in 2023 it was modified by the
[VOSCA](https://github.com/vlang-association).
## Thanks
- [VLS](https://github.com/vlang/vls) authors for the initial Language Server implementation!
- [vscode-vlang](https://github.com/vlang/vscode-vlang) authors for the first VS Code extension!
- [rust-analyzer](https://github.com/rust-lang/rust-analyzer) and [gopls](https://github.com/golang/tools/tree/master/gopls) for the inspiration!
- [Tree-sitter](https://github.com/tree-sitter/tree-sitter) authors for the cool parsing library!
## License
This project is under the **MIT License**.
The full license text can be found in the [LICENSE](https://github.com/vlang/v-analyzer/blob/main/LICENSE) file.
[badge__vscode_ext]: https://img.shields.io/badge/VS_Code-extension-1da2e2?logo=visualstudiocode&logoWidth=11&logoColor=959da5&labelColor=333
[badge__build_ci]: https://img.shields.io/github/actions/workflow/status/vlang/v-analyzer/build_ci.yml?style=flat-rounded&branch=main&logo=github&&logoColor=959da5&labelColor=333&label=Build
[badge__tests_ci]: https://img.shields.io/github/actions/workflow/status/vlang/v-analyzer/analyzer_tests.yml?style=flat-rounded&branch=main&logo=github&&logoColor=959da5&labelColor=333&label=Analyzer
[badge__tree_sitter_ci]: https://img.shields.io/github/actions/workflow/status/vlang/v-analyzer/tree_sitter_v.yml?style=flat-rounded&branch=main&logo=github&&logoColor=959da5&labelColor=333&label=Tree-sitter
[badge__vscode_ext_ci]: https://img.shields.io/github/actions/workflow/status/vlang/v-analyzer/vscode_extension_tests.yml?style=flat-rounded&branch=main&logo=github&&logoColor=959da5&labelColor=333&label=VS%20Code%20Extension
================================================
FILE: build.vsh
================================================
#!/usr/bin/env -S v
// This script is used to build the v-analyzer binary.
// Usage: `v build.vsh [debug|dev|release]`
// By default, doing just `v build.vsh` will use debug mode.
import os
import cli
import term
import time
import src.metadata
const vexe = @VEXE
const bin_path = './bin/v-analyzer' + $if windows { '.exe' } $else { '' }
const build_time = time.now()
const build_commit = get_build_commit()
const build_datetime = build_time.format_ss()
const gcheck = term.bold(term.green('✓'))
const ynote = term.bold(term.gray('ⓘ '))
const is_nixos = os.exists('/etc/NIXOS')
enum ReleaseMode {
release
debug
dev
}
fn get_build_commit() string {
// In pull requests, GA creates a merge commit, to test the latest changes,
// as if they would have been merged in the main branch. However for building
// and version checking, we need the commit hash of the actual last change in the PR.
committish := os.getenv_opt('GITHUB_WORKFLOW_SHA') or { 'HEAD' }
return os.execute('git rev-parse --short ${committish}').output.trim_space()
}
fn eline(msg string) {
eprintln('${term.bold(term.red('[ERROR]'))} ${msg}')
}
fn detect_build_os() {
$if windows {
println('${ynote} Detected Windows .')
}
$if macos {
println('${ynote} Detected macOS .')
}
$if linux {
println('${ynote} Detected Linux .')
}
$if freebsd {
println('${ynote} Detected FreeBSD .')
}
if is_nixos {
println('${ynote} NIXOS detected ... The build *should NOT* be static .')
}
}
fn (m ReleaseMode) compile_cmd() string {
base_build_cmd := '${os.quoted_path(@VEXE)} ${os.quoted_path(@VMODROOT)} -o ${quoted_path(bin_path)} -no-parallel'
cc := if v := os.getenv_opt('CC') {
'-cc ${v}'
} else {
$if windows {
// TCC cannot build tree-sitter on Windows.
'-cc gcc'
} $else $if linux {
// GCC is needed for libbacktrace (unwind.h) and tree-sitter support.
'-cc gcc'
} $else {
// Let `-prod` toggle the appropriate production compiler.
''
}
}
cflags := $if cross_compile_macos_arm64 ? {
'-cflags "-target arm64-apple-darwin"'
} $else $if cross_compile_macos_x86_64 ? {
'-cflags "-target x86_64-apple-darwin"'
} $else $if linux {
if !is_nixos && m == .release {
'-cflags -static'
} else {
''
}
} $else {
''
}
libbacktrace := $if windows { '' } $else { '-d use_libbacktrace' }
build_cmd := '${base_build_cmd} ${cc} ${cflags}'.trim_space()
mut resulting_cmd := match m {
.release { '${build_cmd} -prod' }
.debug { '${build_cmd} -g ${libbacktrace}' }
.dev { '${build_cmd} -d show_ast_on_hover -g ${libbacktrace}' }
}
$if !windows {
// Treesitter's generated C code uses gotos;
// Older V versions of the json codegen generated `if(cond) \nstatement; statement2;` with wrong indentation, instead of blocks;
// => Adding the flags below allows v-analyzer to be compiled with -cstrict, and wider range of supported C compilers
resulting_cmd += ' -cflags "-Wno-misleading-indentation -Wno-jump-misses-init -Wno-error=jump-misses-init -Wno-typedef-redefinition"'
}
return resulting_cmd
}
fn prepare_output_dir() string {
output_dir := './bin'
if os.exists(output_dir) {
return output_dir
}
os.mkdir(output_dir) or { eline('Failed to create output directory: ${err}') }
return output_dir
}
fn build(mode ReleaseMode, explicit_debug bool) {
odir := prepare_output_dir()
println('${gcheck} Prepared output directory `${odir}` .')
detect_build_os()
vexe_version := os.execute('${os.quoted_path(vexe)} version').output.trim_space()
println('${ynote} Building with ${vexe_version} .')
println('${ynote} Building v-analyzer at commit: ${build_commit} .')
println('${ynote} Building start time: ${build_datetime} .')
cmd := mode.compile_cmd()
println('${ynote} Compiling v-analyzer in ${term.bold(mode.str())} mode, using:')
println(cmd)
if mode == .release {
println('This may take 1-2 minutes... Please wait.')
}
if !explicit_debug && mode == .debug {
println('')
println('Note: to build in ${term.bold('release')} mode, run `${term.bold('v build.vsh release')}` .')
println(' Release mode is recommended for production use.')
println(' At runtime, it is about 30-40% faster than debug mode.')
println('')
}
os.execute_opt(cmd) or {
eline('Failed to build v-analyzer')
eprintln(err)
exit(1)
}
final_path := abs_path(bin_path)
nbytes := os.file_size(final_path)
println('${ynote} The binary size in bytes is: ${nbytes:8} .')
println('${ynote} The binary is located here: ${term.bold(final_path)} .')
elapsed_ms := f64((time.now() - build_time).milliseconds())
println('${gcheck} Successfully built v-analyzer, in ${elapsed_ms / 1000.0:5.3f}s .')
}
// main program:
os.setenv('BUILD_DATETIME', build_datetime, true)
os.setenv('BUILD_COMMIT', build_commit, true)
mut cmd := cli.Command{
name: 'v-analyzer-builder'
version: metadata.manifest.version
description: 'Builds the v-analyzer binary.'
posix_mode: true
execute: fn (_ cli.Command) ! {
build(.debug, false)
}
}
// debug builds the v-analyzer binary in debug mode.
// This is the default mode.
// Thanks to -d use_libbacktrace, the binary will print beautiful stack traces,
// which is very useful for debugging.
cmd.add_command(cli.Command{
name: 'debug'
description: 'Builds the v-analyzer binary in debug mode.'
execute: fn (_ cli.Command) ! {
build(.debug, true)
}
})
// dev builds the v-analyzer binary in development mode.
// In this mode, additional development features are enabled.
cmd.add_command(cli.Command{
name: 'dev'
description: 'Builds the v-analyzer binary in development mode.'
execute: fn (_ cli.Command) ! {
build(.dev, false)
}
})
// release builds the v-analyzer binary in release mode.
// This is the recommended mode for production use.
// It is about 30-40% faster than debug mode.
cmd.add_command(cli.Command{
name: 'release'
description: 'Builds the v-analyzer binary in release mode.'
execute: fn (_ cli.Command) ! {
build(.release, false)
}
})
cmd.parse(os.args)
================================================
FILE: editors/code/.editorconfig
================================================
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = tab
max_line_length = 90
insert_final_newline = true
trim_trailing_whitespace = true
[*.{yml,yaml}]
indent_style = space
indent_size = 2
================================================
FILE: editors/code/.eslintignore
================================================
node_modules/
dist/
scripts/
================================================
FILE: editors/code/.eslintrc.json
================================================
{
"root": true,
"parser": "@typescript-eslint/parser",
"parserOptions": {
"tsconfigRootDir": ".",
"project": ["./tsconfig.json"]
},
"plugins": [
"@typescript-eslint"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking"
],
"rules": {
"prefer-const": "error",
"no-array-constructor": "error",
"no-new-object": "error",
"no-shadow": "error",
"no-undef-init": "error",
"no-var": "error",
"object-shorthand": "error",
"prefer-template": "error",
// Stylisitc rules
"array-bracket-spacing": "error",
"brace-style": "error",
"block-spacing": "error",
"camelcase": "error",
"comma-spacing": "error",
"eol-last": "error",
"func-call-spacing": "error",
"quotes": ["error", "single"],
"semi": "error"
}
}
================================================
FILE: editors/code/.gitignore
================================================
.vscode/ipch
.idea/
*.code-workspace
node_modules/
dist/
*.exe
*.tmp.json
*.vsix
.DS_Store
package-lock.json
================================================
FILE: editors/code/.vscodeignore
================================================
.vscode/**
.vscode-test/**
docs/
node_modules/
scripts/
src/
images/
**/tests/
**/tslint.json
**/*.map
**/*.tmp.json
.eslintignore
.gitignore
tsconfig.json
package-lock.json
================================================
FILE: editors/code/CHANGELOG.md
================================================
# v-analyzer VS Code Extension Changelog
## [0.0.1] - 03.07.2023
First public release.
Please note that this is a beta version, so it may contain any bugs.
================================================
FILE: editors/code/LICENSE
================================================
MIT License
Copyright (c) 2023 V Open Source Community Association (VOSCA)
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: editors/code/README.md
================================================
# v-analyzer support for Visual Studio Code
[](https://marketplace.visualstudio.com/items?itemName=VOSCA.vscode-v-analyzer)
[](https://github.com/vlang/v-analyzer/actions/workflows/vscode_extension_tests.yml)
Provides
[V programming language](https://vlang.io)
and
[`v-analyzer`](https://github.com/vlang/v-analyzer)
support for Visual Studio Code.
It is recommended over and replaces
[V extension](https://marketplace.visualstudio.com/items?itemName=vlanguage.vscode-vlang).
For most of its functionality, the extension uses
[`v-analyzer`](https://github.com/vlang/v-analyzer),
which we will refer to as the server to avoid confusion.
## Features
- syntax highlighting
- code completion
- go to definition, type definition
- find all references, document symbol, symbol renaming
- types and documentation on hover
- inlay hints for types and some construction like `or` block
- semantic syntax highlighting
- formatting
- signature help
## Getting started
Welcome! 👋🏻
Let's get started setting up **v-analyzer** in VS Code!
1. First of all, make sure you have the latest version of V installed.
If you are unsure, run `v up` to update.
2. Now let's install VS Code **v-analyzer** extension:
1. Open the command palette with `Ctrl+Shift+P` or `Cmd+Shift+P`
2. Select `Install Extensions` and choose `v-analyzer`.
You can also install the extension manually:
1. Select `Install from VSIX...`
2. Choose pre-built VSIX file from this folder or build it yourself
After installation, restart VS Code.
3. Open any project that contains files with `.v` extension.
The extension should automatically activate.
Upon activation, the extension will try to find `v-analyzer` server, which
is the heart of the extension and provides all the smart features.
4. Since `v-analyzer` server is not installed
(unless you installed it in advance and added it to PATH, in which case you
can skip this step), the extension will prompt you to install it.
Click `Install` and wait for the installation to complete.
5. After installing `v-analyzer` server, the extension will prompt you to
restart the `v-analyzer` server.
Click `Yes` and wait for the restart to complete.
6. When `v-analyzer` server is successfully restarted,
it will start to analyze your project as well as the V standard library.
7. Note that if `v-analyzer` server cannot find where the V standard library
is stored, an error will be shown.
In this case, follow the instructions in the error and specify the path to
the V source code folder in the `custom_vroot` field.
> **Note**
> You need to specify the folder where all the V sources are stored
> (e.g. `C:\v\` or `/home/user/v/` and not the folder with the standard library
> (e.g. `C:\v\vlib` or `/home/user/v/vlib`)!
> After making changes, restart `v-analyzer` using the `v-analyzer: Restart server`
> command in the command palette.
8. If the server was able to find all the necessary things, then after a while the
indexing will end, and you will be able to use all the features of `v-analyzer`.
> **Note**
> Indexing can take up to 30 seconds on weak machines, but this is only
> done on the first run; then the indexes will be loaded from the cache.
You are ready to code in V! 🎉
## Manual Setup
You can install ``v-analyzer`` server manually:
Clone the
[`v-analyzer`](https://github.com/vlang/v-analyzer)
repository, build it and specify the path to the compiled binary.
```json
{
"v-analyzer.serverPath": "path/to/v-analyzer"
}
```
## Auto save
`v-analyzer` uses `v` compiler to analyze code.
It calls it every time a file is saved, so you can set up auto-save to get real-time
feedback.
```json
{
"files.autoSave": "afterDelay",
"files.autoSaveDelay": 300
}
```
## Semantic tokens
With highlighting based on TextMate grammar, v-analyzer provides semantic
highlighting, which allows you to highlight fields, variables, parameters and other
elements as different entities.
To enable semantic highlighting, make sure the `editor.semanticHighlighting.enabled`
setting is set to `true` in the VS Code settings.
In the settings, you can also specify colors for each entity type:
```json
{
"editor.semanticTokenColorCustomizations": {
"[Theme Name]": {
"rules": {
"namespace": "#AFBF7E",
"parameter": "#B189F5",
"decorator": "#DEBC7E",
"typeParameter": "#B189F5",
"enumMember": "#72CFD6",
"*.global": "#A9B7C6",
"function": "#FFC66D",
"*.mutable": {
"underline": true
}
}
}
}
}
```
See
[all available entity types](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#semanticTokenTypes)
in the LSP specification.
## Building from source
```bash
npm install
npm run package
```
## License
This project is under the **MIT License**.
See the
[LICENSE](https://github.com/vlang/v-analyzer/blob/main/editors/code/LICENSE)
file for the full license text.
================================================
FILE: editors/code/docs/troubleshooting.md
================================================
# Troubleshooting
If you suspect that the V extension is not working correctly, please follow the
troubleshooting steps below.
TODO
================================================
FILE: editors/code/languages/v-language-configuration.json
================================================
{
"comments": {
"lineComment": "//",
"blockComment": ["/*", "*/"]
},
"folding": {
"markers": {
"start": "^\\s*//\\s*#?region\\b",
"end": "^\\s*//\\s*#?endregion\\b"
}
},
"brackets": [
["{", "}"],
["[", "]"],
["(", ")"]
],
"autoClosingPairs": [
["{", "}"],
["[", "]"],
["(", ")"],
["\"", "\""],
["'", "'"]
],
"surroundingPairs": [
["{", "}"],
["[", "]"],
["(", ")"],
["\"", "\""],
["'", "'"]
]
}
================================================
FILE: editors/code/languages/vmod-language-configuration.json
================================================
{
"comments": {
"lineComment": "//",
"blockComment": ["/*", "*/"]
},
"folding": {
"markers": {
"start": "^\\s*//\\s*#?region\\b",
"end": "^\\s*//\\s*#?endregion\\b"
}
},
"brackets": [
["{", "}"],
["[", "]"],
["(", ")"]
],
"autoClosingPairs": [
["{", "}"],
["[", "]"],
["(", ")"],
["\"", "\""],
["'", "'"]
],
"surroundingPairs": [
["{", "}"],
["[", "]"],
["(", ")"],
["\"", "\""],
["'", "'"]
]
}
================================================
FILE: editors/code/media/welcome.css
================================================
/*---------------------------------------------------------
* Copyright 2020 The Go Authors. All rights reserved.
* Licensed under the MIT License. See LICENSE in the project root for license information.
*--------------------------------------------------------*/
.Content {
max-width: 60rem;
font-size: 1rem;
line-height: 1.5rem;
margin: auto;
}
.Header {
align-items: center;
display: flex;
border-bottom: 0.0625rem solid #ccc;
margin-bottom: 2rem;
padding-bottom: 1.25rem;
}
.Header-logo {
width: 12rem;
margin-right: 3rem;
}
.Header-title {
font-size: 1.75rem;
}
.Announcement {
display: flex;
flex-direction: row;
align-items: center;
font-style: italic;
padding: 1rem;
}
.Announcement-image {
height: 2rem;
flex: 0;
margin-right: 1.5rem;
}
.Cards {
display: flex;
flex-direction: column;
flex-wrap: wrap;
}
.Card {
max-width: 40rem;
flex: 1;
}
.Card-inner {
display: flex;
flex: 1;
flex-direction: column;
padding: 0rem;
}
.Card-title {
font-size: 1.5rem;
font-weight: 500;
}
.Card-content {
margin: 0;
}
a:link,
a:visited {
text-decoration: none;
}
.Command:hover,
a:hover {
text-decoration: underline;
}
@media (min-width: 40rem) {
.Header-links {
list-style: none;
padding: 0;
}
.Header-links li {
float: left;
}
.Header-links li:not(:first-child):before {
content: "|";
color: #ccc;
padding: 0 1rem;
}
.Cards {
flex-direction: row;
justify-content: space-between;
}
.Card:not(:last-child) {
margin-right: 4rem;
}
}
================================================
FILE: editors/code/package.json
================================================
{
"name": "vscode-v-analyzer",
"displayName": "v-analyzer",
"description": "V language support (syntax highlighting, formatter, language server) for Visual Studio Code.",
"publisher": "VOSCA",
"icon": "icons/icon.png",
"version": "0.0.6",
"engines": {
"vscode": "^1.66.0"
},
"homepage": "https://github.com/vlang/v-analyzer",
"license": "MIT",
"bugs": {
"url": "https://github.com/vlang/v-analyzer/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/vlang/v-analyzer"
},
"keywords": [
"V",
"v",
"v language",
"vlang"
],
"scripts": {
"compile": "node ./scripts/build.js",
"compile-dev": "node ./scripts/build.js --dev",
"compile-watch": "node ./scripts/build.js --watch",
"test": "npm run testgrammar",
"lint": "eslint .",
"lintmd": "markdownlint *.md -i CHANGELOG.md",
"vscode:prepublish": "node ./scripts/minify_json.js && npm run compile",
"watch": "tsc -watch -p ./",
"package": "npx vsce package",
"postpackage": "node ./scripts/minify_json.js --restore",
"testgrammar": "vscode-tmgrammar-test -g syntaxes/v.tmLanguage.json \"syntaxes/tests/*.vv\"",
"format": "prettier --write \"scripts/**/*.js\" \"src/**/*.ts\""
},
"main": "dist/extension.js",
"dependencies": {
"axios": "^1.6.8",
"semver": "^7.6.0",
"vscode-languageclient": "^9.0.1"
},
"devDependencies": {
"@types/node": "^20.11.28",
"@types/semver": "^7.5.8",
"@types/vscode": "~1.66.0",
"@typescript-eslint/eslint-plugin": "^7.2.0",
"@typescript-eslint/parser": "^7.2.0",
"@vscode/vsce": "^2.24.0",
"esbuild": "^0.20.2",
"eslint": "^8.57.0",
"markdownlint-cli": "^0.39.0",
"prettier": "^3.2.5",
"typescript": "^5.4.2",
"vscode-tmgrammar-test": "^0.1.3"
},
"activationEvents": [
"workspaceContains:**/*.v",
"workspaceContains:**/*.vv",
"workspaceContains:**/*.vsh",
"workspaceContains:**/v.mod",
"onCommand:v.runWorkspace",
"onCommand:v.runFile",
"onCommand:v.runTests",
"onCommand:v.version",
"onCommand:v.serverVersion"
],
"categories": [
"Programming Languages"
],
"extensionPack": [
"ms-vscode.cpptools"
],
"contributes": {
"languages": [
{
"id": "v",
"aliases": [
"V"
],
"extensions": [
".v",
".vsh",
".vv"
],
"configuration": "languages/v-language-configuration.json"
},
{
"id": "v.mod",
"language": "v.mod",
"scopeName": "source.v.mod",
"extensions": [
".mod"
],
"configuration": "languages/vmod-language-configuration.json"
},
{
"id": "stree",
"scopeName": "source.stree",
"extensions": [
".stree"
]
}
],
"grammars": [
{
"language": "v",
"scopeName": "source.v",
"path": "syntaxes/v.tmLanguage.json"
},
{
"language": "v.mod",
"scopeName": "source.v.mod",
"path": "syntaxes/v.mod.tmLanguage.json"
},
{
"language": "stree",
"scopeName": "source.stree",
"path": "syntaxes/stree.tmGrammar.json"
}
],
"configuration": {
"title": "v-analyzer",
"properties": {
"v.executablePath": {
"scope": "resource",
"type": "string",
"description": "Custom path to the V compiler executable (`v`).\nNOTE: Setting this won't change the VROOT path in v-analyzer."
},
"v-analyzer.customVrootPath": {
"scope": "resource",
"type": "string",
"default": "",
"description": "Custom path to the V installation directory (VROOT).\nNOTE: Setting this won't change the V compiler executable to be used."
},
"v-analyzer.serverPath": {
"scope": "resource",
"type": "string",
"default": "",
"description": "Custom path to the v-analyzer executable (if empty uses `v-analyzer` executable from PATH)."
},
"v-analyzer.connectionMode": {
"scope": "resource",
"type": "string",
"default": "stdio",
"enum": [
"stdio",
"tcp"
],
"description": "Specify the mode to be used when connecting to v-analyzer.",
"enumDescriptions": [
"Connects to the language server via standard input/output. (Default)",
"Connects to the language server via TCP (you need to run analyzer manually with `--socket` flag, debug only!)."
]
},
"v-analyzer.tcpMode.port": {
"scope": "resource",
"type": "number",
"default": 5007,
"description": "Port to be used when connecting to the language server. (Only in TCP mode)"
},
"v-analyzer.customArgs": {
"scope": "resource",
"type": "string",
"description": "Custom arguments to be passed to the v-analyzer executable."
}
}
},
"configurationDefaults": {
"[v]": {
"editor.insertSpaces": false
}
},
"keybindings": [
{
"command": "v.fmt",
"key": "ctrl+i ctrl+i"
}
],
"commands": [
{
"command": "v.run",
"title": "Run current directory",
"category": "V"
},
{
"command": "v.fmt",
"title": "Format current file",
"category": "V"
},
{
"command": "v.prod",
"title": "Build an optimized executable from current file",
"category": "V"
},
{
"command": "v-analyzer.version",
"title": "Show language version",
"category": "V"
},
{
"command": "v-analyzer.serverVersion",
"title": "Show v-analyzer server version",
"category": "v-analyzer"
},
{
"command": "v-analyzer.startServer",
"title": "Start server",
"category": "v-analyzer"
},
{
"command": "v-analyzer.stopServer",
"title": "Stop server",
"category": "v-analyzer"
},
{
"command": "v-analyzer.restartServer",
"title": "Restart server",
"category": "v-analyzer"
},
{
"command": "v-analyzer.showReferences",
"title": "Show References",
"category": "v-analyzer"
},
{
"command": "v-analyzer.viewStubTree",
"title": "View Stub Tree",
"category": "v-analyzer"
},
{
"command": "v-analyzer.uploadToPlayground",
"title": "Upload to V Playground",
"category": "v-analyzer"
},
{
"command": "v-analyzer.showWelcome",
"title": "Show Welcome",
"description": "Open the welcome page for the v-analyzer extension.",
"category": "v-analyzer"
},
{
"command": "v-analyzer.openGlobalConfig",
"title": "Open Global Config",
"description": "Open the global config file for the v-analyzer server.",
"category": "v-analyzer"
}
],
"menus": {
"commandPalette": [
{
"command": "v.run",
"when": "inVlangProject"
},
{
"command": "v.fmt",
"when": "inVlangProject"
},
{
"command": "v.prod",
"when": "inVlangProject"
},
{
"command": "v-analyzer.serverVersion",
"when": "inVlangProject"
},
{
"command": "v-analyzer.stopServer",
"when": "inVlangProject"
},
{
"command": "v-analyzer.restartServer",
"when": "inVlangProject"
},
{
"command": "v-analyzer.uploadToPlayground",
"when": "inVlangProject"
}
]
},
"breakpoints": [
{
"language": "v"
}
],
"semanticTokenModifiers": [
{
"id": "mutable",
"description": "Style for mutable variables/parameters/receivers"
},
{
"id": "global",
"description": "Style for global variables"
}
],
"semanticTokenScopes": [
{
"language": "v",
"scopes": {
"*.mutable": [
"markup.underline"
],
"*.global": [
"markup.bold"
]
}
}
]
}
}
================================================
FILE: editors/code/scripts/build.js
================================================
#!/usr/bin/env node
//@ts-check
"use strict";
const esbuild = require("esbuild");
const isWatch = process.argv.includes("--watch");
const isDev = process.argv.includes("--dev");
esbuild
.context({
platform: "node",
entryPoints: ["./src/extension.ts"],
outdir: "./dist",
external: ["vscode"],
format: "cjs",
sourcemap: "external",
bundle: true,
minify: !isDev,
})
.then((context) => {
if (isWatch) {
context.watch();
} else {
context.rebuild().then(() => context.dispose());
}
})
.catch(() => process.exit(1));
================================================
FILE: editors/code/scripts/minify_json.js
================================================
#!/usr/bin/env node
// @ts-check
"use strict";
const { exec } = require("child_process");
const { writeFileSync, copyFileSync, renameSync, existsSync } = require("fs");
const { resolve } = require("path");
const jsonFiles = [
"../syntaxes/v.tmLanguage.json",
"../syntaxes/v.mod.tmLanguage.json",
"../languages/v-language-configuration.json",
"../languages/vmod-language-configuration.json",
];
const shouldRestore = process.argv.includes("--restore");
jsonFiles.forEach((jsonFile) => {
const absolutePath = resolve(__dirname, jsonFile);
const tmpFile = resolve(__dirname, jsonFile.replace(".json", ".tmp.json"));
if (shouldRestore) {
renameSync(tmpFile, absolutePath);
} else {
if (!existsSync(tmpFile)) {
copyFileSync(absolutePath, tmpFile);
}
exec(`npx json-minify ${absolutePath}`, (error, stdout) => {
if (error) throw error;
writeFileSync(absolutePath, stdout);
});
}
});
================================================
FILE: editors/code/src/bootstrap.ts
================================================
import cp from "child_process";
import os from "os";
import fs from "fs";
import { log } from "./log";
import { getWorkspaceConfig } from "./utils";
import { AnalyzerNotInstalledError } from "./ctx";
/**
* bootstrap returns the path to the v-analyzer binary.
* It will throw an error if the binary is not available.
*
* @returns {Promise<string>} The path to the v-analyzer binary.
*/
export async function bootstrap(): Promise<string> {
const path = getAnalyzerPath();
if (!isAnalyzerExecutableValid(path)) {
const config = getWorkspaceConfig();
const explicitPath = config.get<string>("serverPath");
if (explicitPath) {
throw new Error(`Failed to execute ${path} -v. \`config.serverPath\`has been set explicitly.\
Consider removing this config or making a valid server binary available at that path.`);
}
throw new AnalyzerNotInstalledError(
`Failed to execute ${path} -v, make sure the v-analyzer is installed and available in the PATH`,
);
}
log.info("Server binary path:", path);
return path;
}
function getAnalyzerPath(): string {
const config = getWorkspaceConfig();
const explicitPath = config.get<string>("serverPath");
const path = explicitPath ? explicitPath : "v-analyzer";
if (path.startsWith("~/") || path.startsWith("~\\")) {
return path.replace("~", os.homedir());
}
return path;
}
function isAnalyzerExecutableValid(path: string): boolean {
const location = path === "v-analyzer" ? "PATH" : path;
log.debug("Checking availability of a binary at", location);
const res = cp.spawnSync(`${path}`, ["-v"]);
return res.status === 0;
}
================================================
FILE: editors/code/src/client.ts
================================================
import * as lc from "vscode-languageclient/node";
import vscode, { window, workspace } from "vscode";
let crashCount = 0;
export async function createClient(
outputChannel: vscode.OutputChannel,
serverOptions: lc.ServerOptions,
): Promise<lc.LanguageClient> {
const clientOptions: lc.LanguageClientOptions = {
documentSelector: [{ scheme: "file", language: "v" }],
synchronize: {
fileEvents: workspace.createFileSystemWatcher("**/*.v"),
},
outputChannel: outputChannel,
errorHandler: {
error: (error: Error, _: lc.Message, count: number) => {
// taken from: https://github.com/golang/vscode-go/blob/HEAD/src/goLanguageServer.ts#L533-L539
if (count < 5) {
return {
message: "", // suppresses error popups
action: lc.ErrorAction.Continue,
};
}
void window.showErrorMessage(
`v-analyzer: Error communicating with the language server: ${error}: ${error}.`,
);
return {
action: lc.ErrorAction.Shutdown,
};
},
closed: () => {
crashCount++;
if (crashCount < 5) {
return {
message: "", // suppresses error popups
action: lc.CloseAction.Restart,
};
}
return {
action: lc.CloseAction.DoNotRestart,
};
},
},
markdown: {
isTrusted: true,
supportHtml: true,
},
};
const client = new lc.LanguageClient(
"v-analyzer",
"V Language Server",
serverOptions,
clientOptions,
true,
);
client.registerFeature(new ExperimentalFeatures());
return client;
}
class ExperimentalFeatures implements lc.StaticFeature {
fillInitializeParams?: (params: lc.InitializeParams) => void;
preInitialize?: (
capabilities: lc.ServerCapabilities<any>,
documentSelector: lc.DocumentSelector,
) => void;
clear(): void {}
getState(): lc.FeatureState {
return { kind: "static" };
}
fillClientCapabilities(capabilities: lc.ClientCapabilities): void {
capabilities.experimental = {
serverStatusNotification: true,
viewStubTree: true,
...capabilities.experimental,
};
}
initialize(
_capabilities: lc.ServerCapabilities,
_documentSelector: lc.DocumentSelector | undefined,
): void {}
dispose(): void {}
}
================================================
FILE: editors/code/src/commands.ts
================================================
import * as vscode from "vscode";
import * as path from "path";
import * as lc from "vscode-languageclient";
import * as ra from "./lsp_ext";
import * as os from "os";
import { runVCommand, runVCommandCallback } from "./exec";
import { Command, ContextInit } from "./ctx";
import { LanguageClient } from "vscode-languageclient/node";
import { spawnSync } from "child_process";
import { isVlangDocument, isVlangEditor, sleep } from "./utils";
import { log } from "./log";
import axios from "axios";
import FormData from "form-data";
/**
* Run current directory.
*/
export function runWorkspace(_: ContextInit): Command {
return async () => {
const document = vscode.window.activeTextEditor.document;
await document.save();
const dir = path.parse(document.fileName).dir;
runVCommand(["run", dir]);
};
}
export function runFile(_: ContextInit): Command {
return async () => {
const document = vscode.window.activeTextEditor.document;
await document.save();
const fileName = document.fileName;
runVCommand(["run", fileName]);
};
}
export function runTests(_: ContextInit): Command {
return async (uri: string, name?: string) => {
const args = ["test", uri];
if (name) {
args.push("-run-only", `"*.${name}"`);
}
runVCommand(args);
};
}
/**
* Show version info.
*/
export function version(_: ContextInit): Command {
return () => {
runVCommandCallback(["-version"], (err, stdout) => {
if (err) {
void vscode.window.showErrorMessage(
"Unable to get the version number. Is V installed correctly?",
);
return;
}
void vscode.window.showInformationMessage(stdout);
});
};
}
export function serverVersion(ctx: ContextInit): Command {
return async () => {
if (!ctx.serverPath) {
void vscode.window.showWarningMessage(`v-analyzer server is not running`);
return;
}
const { stdout } = spawnSync(ctx.serverPath, ["--version"], { encoding: "utf8" });
const versionString = stdout.slice(`v-analyzer version`.length).trim();
void vscode.window.showInformationMessage(`v-analyzer version: ${versionString}`);
};
}
export function showReferences(ctx: ContextInit): Command {
return async (uri: string, positionData: string, locationData: string) => {
const locations = JSON.parse(locationData);
const position = JSON.parse(positionData);
await showReferencesImpl(ctx.client, uri, position, locations);
};
}
export async function showReferencesImpl(
client: LanguageClient | undefined,
uri: string,
position: lc.Position,
locations: lc.Location[],
) {
if (!client) return;
await vscode.commands.executeCommand(
"editor.action.showReferences",
vscode.Uri.parse(uri),
client.protocol2CodeConverter.asPosition(position),
locations.map(client.protocol2CodeConverter.asLocation),
);
}
export function viewStubTree(ctx: ContextInit): Command {
const tdcp = new (class implements vscode.TextDocumentContentProvider {
readonly uri = vscode.Uri.parse(
"v-analyzer-file-stub-tree://viewStubTree/file.stree",
);
readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
constructor() {
vscode.workspace.onDidChangeTextDocument(
this.onDidChangeTextDocument,
this,
ctx.subscriptions,
);
vscode.window.onDidChangeActiveTextEditor(
this.onDidChangeActiveTextEditor,
this,
ctx.subscriptions,
);
}
private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
if (isVlangDocument(event.document)) {
// We need to order this after language server updates, but there's no API for that.
// Hence, good old sleep().
void sleep(10).then(() => this.eventEmitter.fire(this.uri));
}
}
private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) {
if (editor && isVlangEditor(editor)) {
this.eventEmitter.fire(this.uri);
}
}
async provideTextDocumentContent(
_uri: vscode.Uri,
ct: vscode.CancellationToken,
): Promise<string> {
const rustEditor = ctx.activeVlangEditor;
if (!rustEditor) return "";
const client = ctx.client;
const params = client.code2ProtocolConverter.asTextDocumentIdentifier(
rustEditor.document,
);
return client.sendRequest(ra.viewStubTree, params, ct);
}
get onDidChange(): vscode.Event<vscode.Uri> {
return this.eventEmitter.event;
}
})();
ctx.pushExtCleanup(
vscode.workspace.registerTextDocumentContentProvider(
"v-analyzer-file-stub-tree",
tdcp,
),
);
return async () => {
const document = await vscode.workspace.openTextDocument(tdcp.uri);
tdcp.eventEmitter.fire(tdcp.uri);
void (await vscode.window.showTextDocument(document, {
viewColumn: vscode.ViewColumn.Two,
preserveFocus: true,
}));
};
}
export function uploadToPlayground(_: ContextInit): Command {
return async () => {
const editor = vscode.window.activeTextEditor;
if (!editor) {
vscode.window.showInformationMessage("No editor is active.");
return;
}
log.info("Uploading to playground...");
const selection = editor.selection;
const code = selection.isEmpty
? editor.document.getText()
: editor.document.getText(selection);
const form = new FormData();
form.append("code", code);
const response = await axios.post("https://play.vlang.io/share", form);
const json = await response.data;
const hash = json["hash"];
const error = json["error"];
if (error) {
vscode.window.showErrorMessage(`V Playground: ${error}`);
return;
}
const url = `https://play.vlang.io/p/${hash}`;
const open = await vscode.window.showInformationMessage(
"Successfully uploaded to V playground. Open in browser?",
"Open",
"Copy URL",
);
if (open === "Open") {
vscode.env.openExternal(vscode.Uri.parse(url));
} else if (open === "Copy URL") {
vscode.env.clipboard.writeText(url);
}
};
}
export function openGlobalConfig(_: ContextInit): Command {
return async () => {
const configPath = "~/.config/v-analyzer/config.toml";
const home = os.homedir();
const configPathWithHome = configPath.replace("~", home);
const uri = vscode.Uri.file(configPathWithHome);
const doc = await vscode.workspace.openTextDocument(uri);
await vscode.window.showTextDocument(doc);
};
}
================================================
FILE: editors/code/src/ctx.ts
================================================
import * as lc from "vscode-languageclient/node";
import * as ra from "./lsp_ext";
import * as vscode from "vscode";
import * as fs from "fs";
import * as https from "https";
import * as path from "path";
import * as cp from "child_process";
import {
getWorkspaceConfig,
getWorkspaceFolder,
isVlangDocument,
isVlangEditor,
VlangEditor,
} from "./utils";
import { Progress } from "vscode";
import { createClient } from "./client";
import { bootstrap } from "./bootstrap";
import { connectAnalyzerViaTcp } from "./tcp";
import { log } from "./log";
import { runVCommandCallback } from "./exec";
// Most of the file taken from `rust-analyzer/editors/code/src/ctx.ts` <3
export type Workspace =
| { kind: "Empty" }
| { kind: "Workspace Folder" }
| { kind: "Detached Files"; files: vscode.TextDocument[] };
export function fetchWorkspace(): Workspace {
const folders = (vscode.workspace.workspaceFolders || []).filter(
(folder) => folder.uri.scheme === "file",
);
const vlangDocuments = vscode.workspace.textDocuments.filter((document) =>
isVlangDocument(document),
);
if (folders.length !== 0) {
return { kind: "Workspace Folder" };
}
if (vlangDocuments.length === 0) {
return { kind: "Empty" };
}
return { kind: "Detached Files", files: vlangDocuments };
}
export type Command = (...args: any[]) => unknown;
export type CommandFactory = {
enabled: (ctx: ContextInit) => Command;
disabled?: (ctx: Context) => Command;
};
export type ContextInit = Context & {
readonly client: lc.LanguageClient;
};
export class AnalyzerNotInstalledError implements Error {
constructor(additionalMessage?: string) {
this.message = "v-analyzer is not installed";
if (additionalMessage) {
this.message += `: ${additionalMessage}`;
}
this.name = "AnalyzerNotInstalled";
}
message: string;
name: string;
}
export class Context {
readonly statusBar: vscode.StatusBarItem;
readonly langStatusBar: vscode.StatusBarItem;
private _client: lc.LanguageClient | undefined;
private _serverPath: string | undefined;
private outputChannel: vscode.OutputChannel | undefined;
private clientSubscriptions: Disposable[];
private commandDisposables: Disposable[];
get client() {
return this._client;
}
get subscriptions(): Disposable[] {
return this.extCtx.subscriptions;
}
get serverPath(): string | undefined {
return this._serverPath;
}
get activeVlangEditor(): VlangEditor | undefined {
const editor = vscode.window.activeTextEditor;
return editor && isVlangEditor(editor) ? editor : undefined;
}
constructor(
readonly extCtx: vscode.ExtensionContext,
readonly commandFactories: Record<string, CommandFactory>,
readonly workspace: Workspace,
) {
extCtx.subscriptions.push(this);
this.statusBar = vscode.window.createStatusBarItem(
"v-analyzer-status",
vscode.StatusBarAlignment.Left,
50,
);
this.langStatusBar = vscode.window.createStatusBarItem(
"v-version",
vscode.StatusBarAlignment.Left,
60,
);
this.clientSubscriptions = [];
this.commandDisposables = [];
this.showLanguageStatusBar();
this.updateCommands("disable");
this.setServerStatus({
health: "stopped",
});
}
dispose() {
this.statusBar.dispose();
this.langStatusBar.dispose();
void this.disposeClient();
this.commandDisposables.forEach((disposable) => disposable.dispose());
}
async start() {
log.info("Starting language client");
const client = await this.getOrCreateClient();
if (!client) {
return;
}
await client.start();
this.updateCommands();
}
private async getOrCreateClient() {
if (this.workspace.kind === "Empty") {
return undefined;
}
if (!this.outputChannel) {
this.outputChannel = vscode.window.createOutputChannel(
"V Analyzer Language Server",
);
this.pushExtCleanup(this.outputChannel);
}
if (!this._client) {
this._serverPath = await bootstrap().catch((err) => {
if (err instanceof AnalyzerNotInstalledError) {
log.info("v-analyzer is not installed");
const msg = "v-analyzer is not installed. Do you want to install it?";
void vscode.window
.showInformationMessage(msg, "Yes", "No")
.then((selected) => {
if (selected == "Yes") {
this.installAnalyzerWithProgress();
}
});
}
throw new AnalyzerNotInstalledError();
});
const newEnv = Object.assign({}, process.env);
const folder = getWorkspaceFolder();
log.debug("cwd: ", folder.uri.fsPath);
const run: lc.Executable = {
command: this._serverPath,
options: { env: newEnv, cwd: folder.uri.fsPath },
};
const config = getWorkspaceConfig();
const connMode = config.get<string>("connectionMode");
const tcpPort = config.get<number>("tcpMode.port");
if (connMode === "tcp") {
log.info(`Connecting to analyzer via TCP on port ${tcpPort}`);
log.info("Make sure to start the analyzer with the --socket flag");
log.info("Use it only for debugging purposes!");
}
const serverOptions =
connMode === "tcp"
? () => connectAnalyzerViaTcp(tcpPort)
: {
run,
debug: run,
};
this._client = await createClient(this.outputChannel, serverOptions);
this.pushClientCleanup(
this._client.onNotification(ra.serverStatus, (params) =>
this.setServerStatus(params),
),
);
}
return this._client;
}
async restart() {
await this.stopAndDispose();
await this.start();
}
async stopAndDispose() {
if (!this._client) {
return;
}
log.info("Disposing language client");
this.updateCommands("disable");
await this.disposeClient();
}
private async disposeClient() {
this.clientSubscriptions?.forEach((disposable) => disposable.dispose());
this.clientSubscriptions = [];
try {
await this._client?.dispose();
} catch (e) {
// for some reasons dispose() always throws an error
// when restarting analyzer, ignore for now
// log.error('client stop error', e)
}
this._serverPath = undefined;
this._client = undefined;
}
private updateCommands(forceDisable?: "disable") {
this.commandDisposables.forEach((disposable) => disposable.dispose());
this.commandDisposables = [];
const clientRunning = (!forceDisable && this._client?.isRunning()) ?? false;
const isClientRunning = (_ctx: Context): _ctx is ContextInit => {
return clientRunning;
};
for (const [name, factory] of Object.entries(this.commandFactories)) {
const fullName = `v-analyzer.${name}`;
let callback;
if (isClientRunning(this)) {
// we asserted that `client` is defined
callback = factory.enabled(this);
} else if (factory.disabled) {
callback = factory.disabled(this);
} else {
callback = () =>
vscode.window.showErrorMessage(
`command ${fullName} failed: v-analyzer server is not running`,
);
}
this.commandDisposables.push(
vscode.commands.registerCommand(fullName, callback),
);
}
}
showLanguageStatusBar() {
const statusBar = this.langStatusBar;
statusBar.text = "V";
statusBar.show();
runVCommandCallback(["-version"], (err, stdout) => {
if (err) {
return;
}
const version = stdout.trim().replace("V ", "");
statusBar.text = `V ${version}`;
});
}
setServerStatus(status: ra.ServerStatusParams | { health: "stopped" }) {
if (status.health === "error" && status.message) {
const msg = status.message ?? "v-analyzer server error";
const openConfig = "Open Config";
vscode.window.showErrorMessage(msg, openConfig).then((selected) => {
if (selected === openConfig) {
vscode.commands.executeCommand("v-analyzer.openGlobalConfig");
}
});
}
let icon = "";
const statusBar = this.statusBar;
statusBar.show();
statusBar.tooltip = new vscode.MarkdownString("", true);
statusBar.tooltip.isTrusted = true;
switch (status.health) {
case "ok":
statusBar.tooltip.appendText(status.message ?? "Ready");
statusBar.color = undefined;
statusBar.backgroundColor = undefined;
statusBar.command = "v-analyzer.stopServer";
icon = "$(zap) ";
break;
case "warning":
if (status.message) {
statusBar.tooltip.appendText(status.message);
}
statusBar.color = new vscode.ThemeColor(
"statusBarItem.warningForeground",
);
statusBar.backgroundColor = new vscode.ThemeColor(
"statusBarItem.warningBackground",
);
statusBar.command = "v-analyzer.openLogs";
icon = "$(warning) ";
break;
case "error":
if (status.message) {
statusBar.tooltip.appendText(status.message);
}
statusBar.color = new vscode.ThemeColor("statusBarItem.errorForeground");
statusBar.backgroundColor = new vscode.ThemeColor(
"statusBarItem.errorBackground",
);
statusBar.command = "v-analyzer.openGlobalConfig";
icon = "$(error) ";
break;
case "stopped":
statusBar.tooltip.appendText("Server is stopped");
statusBar.tooltip.appendMarkdown(
"\n\n[Start server](command:v-analyzer.startServer)",
);
statusBar.color = undefined;
statusBar.backgroundColor = undefined;
statusBar.command = "v-analyzer.startServer";
statusBar.text = `$(stop-circle) v-analyzer`;
return;
}
if (statusBar.tooltip.value) {
statusBar.tooltip.appendText("\n\n");
}
statusBar.tooltip.appendMarkdown(
"\n\n[Restart server](command:v-analyzer.restartServer)",
);
statusBar.tooltip.appendMarkdown(
"\n\n[Stop server](command:v-analyzer.stopServer)",
);
if (!status.quiescent) icon = "$(sync~spin) ";
statusBar.text = `${icon}v-analyzer`;
}
pushExtCleanup(d: Disposable) {
this.extCtx.subscriptions.push(d);
}
private pushClientCleanup(d: Disposable) {
this.clientSubscriptions.push(d);
}
private getScriptPath(): string {
const globalFolder = this.extCtx.globalStorageUri.fsPath;
if (!fs.existsSync(globalFolder)) {
fs.mkdirSync(globalFolder);
}
return path.join(globalFolder, "install.vsh");
}
private installAnalyzerWithProgress() {
return vscode.window.withProgress(
{
title: "Installing v-analyzer...",
location: vscode.ProgressLocation.Notification,
},
async (progress) => {
return this.installAnalyzer(progress);
},
);
}
private cleanOutput(value: string): string {
if (!value) {
return "";
}
return value.replace(/\u001b\[[0-9;]*m/g, "");
}
private async startInstallation(
progress: Progress<{
message?: string;
increment?: number;
}>,
fromSources: boolean,
) {
const progressMessage = fromSources
? "Installing v-analyzer from sources..."
: "Installing v-analyzer binary...";
progress.report({ message: progressMessage });
const scriptPath = this.getScriptPath();
const buf = cp.spawnSync(`v`, ["run", scriptPath, "--no-interaction"], {
encoding: "utf-8",
});
log.debug(this.cleanOutput(buf.stdout));
log.debug(this.cleanOutput(buf.stderr));
if (buf.error) {
log.error(buf.error);
return;
}
void vscode.window.showInformationMessage(
"v-analyzer binary has been installed successfully",
);
progress.report({ message: "v-analyzer binary has been installed successfully" });
const config = vscode.workspace.getConfiguration();
config
.update(
"v-analyzer.serverPath",
"~/.config/v-analyzer/bin/v-analyzer",
vscode.ConfigurationTarget.Global,
)
.then(() => {
log.info(
"v-analyzer.serverPath has been updated to ~/.config/v-analyzer/bin/v-analyzer",
);
});
}
private async installAnalyzer(
progress: Progress<{
message?: string;
increment?: number;
}>,
) {
await this.downloadScriptIfNeeded();
progress.report({ message: "Check prebuilt binary availability..." });
const scriptPath = this.getScriptPath();
const result = cp.execSync(`v run '${scriptPath}' check-availability`, {
encoding: "utf-8",
});
if (result.trim().includes("v-analyzer binary is available for your platform")) {
progress.report({
message:
"Found prebuilt binary for your platform, starting downloading...",
});
return this.startInstallation(progress, false);
}
return vscode.window
.showInformationMessage(
"v-analyzer binary is not available for your platform. Do you want to build it from source?",
"Yes",
"No",
)
.then(async (selected) => {
if (selected == "Yes") {
await this.startInstallation(progress, true);
}
});
}
private async downloadScriptIfNeeded() {
return new Promise<void>((resolve, reject) => {
const destinationPath = this.getScriptPath();
if (fs.existsSync(destinationPath)) {
// Do nothing if a script already exists
resolve();
return;
}
log.info("Downloading install script...");
const file = fs.createWriteStream(destinationPath);
https
.get(
"https://raw.githubusercontent.com/vlang/v-analyzer/main/install.vsh",
(response) => {
response.pipe(file);
file.on("finish", () => {
file.close();
log.info("Install script has been downloaded successfully");
resolve();
});
},
)
.on("error", (err) => {
fs.unlink(destinationPath, () => {
log.error(`Failed to download install script: ${err}`);
reject(err);
});
});
});
}
}
export interface Disposable {
dispose(): void;
}
================================================
FILE: editors/code/src/exec.ts
================================================
import { Terminal, window } from "vscode";
import { getVExecCommand } from "./utils";
import cp, { exec, ExecException } from "child_process";
type ExecCallback = (error: ExecException | null, stdout: string, stderr: string) => void;
let runTerminal: Terminal = null!;
function outputTerminal(): Terminal {
if (!runTerminal) {
runTerminal = window.createTerminal("V");
}
return runTerminal;
}
function buildCommand(args: string[]): string {
const vexe = getVExecCommand();
return `${vexe} ${args.join(" ")}`;
}
/**
* Run V command in V terminal inside VS Code.
*/
export function runVCommand(args: string[]): void {
const cmd = buildCommand(args);
const term = outputTerminal();
term.show();
term.sendText(cmd);
}
/**
* Run V command in background.
*/
export function runVCommandInBackground(args: string[]): void {
const cmd = buildCommand(args);
cp.exec(cmd);
}
/**
* Run V command in background and call callback when done.
*/
export function runVCommandCallback(args: string[], callback: ExecCallback): void {
const cmd = buildCommand(args);
exec(cmd, callback);
}
================================================
FILE: editors/code/src/extension.ts
================================================
import * as vscode from "vscode";
import * as commands from "./commands";
import {
AnalyzerNotInstalledError,
CommandFactory,
Context,
fetchWorkspace,
} from "./ctx";
import { WelcomePanel } from "./welcome";
import { setContextValue } from "./utils";
import { setGlobalState } from "./stateUtils";
const V_PROJECT_CONTEXT_NAME = "inVlangProject";
/**
* This method is called when the extension is activated.
* @param context The extension context
*/
export async function activate(context: vscode.ExtensionContext): Promise<Context> {
if (vscode.extensions.getExtension("vlanguage.vscode-vlang")) {
vscode.window
.showWarningMessage(
"You have both the v-analyzer and V plugins enabled." +
"These are known to conflict and cause various functions of " +
"both plugins to not work correctly. " +
"v-analyzer provides all the features of the V plugin and more. " +
"Disable the V plugin to avoid conflicts.",
"Got it",
)
.then(() => {}, console.error);
}
const ctx = new Context(context, createCommands(), fetchWorkspace());
setGlobalState(context.globalState);
WelcomePanel.activate(ctx);
const api = await activateServer(ctx).catch((err) => {
if (!(err instanceof AnalyzerNotInstalledError)) {
void vscode.window.showErrorMessage(
`Cannot activate v-analyzer extension: ${err.message}`,
);
throw err;
}
// If v-analyzer is not installed, we still want to activate the extension.
return ctx;
});
// Set the context variable inVlangProject which can be referenced when configuring,
// for example, shortcuts or other things in package.json.
// See https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts
setContextValue(V_PROJECT_CONTEXT_NAME, true);
return api;
}
export function deactivate(): void {
setContextValue(V_PROJECT_CONTEXT_NAME, undefined);
}
async function activateServer(ctx: Context): Promise<Context> {
vscode.workspace.onDidChangeConfiguration(
(e: vscode.ConfigurationChangeEvent) => {
if (!e.affectsConfiguration("v-analyzer")) return;
void vscode.window
.showInformationMessage(
"v-analyzer: Restart is required for changes to take effect. Would you like to proceed?",
"Yes",
"No",
)
.then((selected) => {
if (selected == "Yes") {
void vscode.commands.executeCommand("v-analyzer.restartServer");
}
});
},
null,
ctx.subscriptions,
);
await ctx.start();
return ctx;
}
function createCommands(): Record<string, CommandFactory> {
return {
restartServer: {
enabled: (ctx) => async () => {
await ctx.restart();
},
disabled: (ctx) => async () => {
await ctx.start();
},
},
startServer: {
enabled: (ctx) => async () => {
await ctx.start();
},
disabled: (ctx) => async () => {
await ctx.start();
},
},
stopServer: {
enabled: (ctx) => async () => {
await ctx.stopAndDispose();
ctx.setServerStatus({
health: "stopped",
});
},
disabled: (_) => async () => {},
},
runWorkspace: { enabled: commands.runWorkspace },
runFile: { enabled: commands.runFile },
runTests: { enabled: commands.runTests },
version: { enabled: commands.version },
serverVersion: { enabled: commands.serverVersion },
showReferences: { enabled: commands.showReferences },
viewStubTree: { enabled: commands.viewStubTree },
uploadToPlayground: { enabled: commands.uploadToPlayground },
showWelcome: {
enabled: WelcomePanel.showWelcome,
disabled: WelcomePanel.showWelcome,
},
openGlobalConfig: {
enabled: commands.openGlobalConfig,
disabled: commands.openGlobalConfig,
},
};
}
================================================
FILE: editors/code/src/log.ts
================================================
import vscode from "vscode";
import { inspect } from "util";
export const log = new (class {
private enabled = true;
private readonly output = vscode.window.createOutputChannel("V Analyzer Client");
setEnabled(yes: boolean): void {
log.enabled = yes;
}
// Hint: the type [T, ...T[]] means a non-empty array
debug(...msg: [unknown, ...unknown[]]): void {
if (!log.enabled) return;
log.write("DEBUG", ...msg);
}
info(...msg: [unknown, ...unknown[]]): void {
log.write("INFO", ...msg);
}
warn(...msg: [unknown, ...unknown[]]): void {
debugger;
log.write("WARN", ...msg);
}
error(...msg: [unknown, ...unknown[]]): void {
debugger;
log.write("ERROR", ...msg);
log.output.show(true);
}
private write(label: string, ...messageParts: unknown[]): void {
const message = messageParts.map(log.stringify).join(" ");
const dateTime = new Date().toLocaleString();
log.output.appendLine(`${label} [${dateTime}]: ${message}`);
}
private stringify(val: unknown): string {
if (typeof val === "string") return val;
return inspect(val, {
colors: false,
depth: 6, // heuristic
});
}
})();
================================================
FILE: editors/code/src/lsp_ext.ts
================================================
import * as lc from "vscode-languageclient";
export const serverStatus = new lc.NotificationType<ServerStatusParams>(
"experimental/serverStatus",
);
export type ServerStatusParams = {
health: "ok" | "warning" | "error";
quiescent: boolean;
message?: string;
};
export const viewStubTree = new lc.RequestType<lc.TextDocumentIdentifier, string, void>(
"v-analyzer/viewStubTree",
);
================================================
FILE: editors/code/src/stateUtils.ts
================================================
import vscode from "vscode";
let globalState: vscode.Memento;
export function getFromGlobalState(key: string, defaultValue?: any): any {
if (!globalState) {
return defaultValue;
}
return globalState.get(key, defaultValue);
}
export function updateGlobalState(key: string, value: any) {
if (!globalState) {
return Promise.resolve();
}
return globalState.update(key, value);
}
export function setGlobalState(state: vscode.Memento) {
globalState = state;
}
================================================
FILE: editors/code/src/tcp.ts
================================================
import { StreamInfo } from "vscode-languageclient/node";
import * as net from "net";
export function connectAnalyzerViaTcp(port: number): Promise<StreamInfo> {
const socket = net.connect({ port });
const result: StreamInfo = {
writer: socket,
reader: socket,
};
return Promise.resolve(result);
}
================================================
FILE: editors/code/src/utils.ts
================================================
import * as vscode from "vscode";
/**
* Get V executable command.
* Will get from user setting configuration first.
* If user don't specify it, then get default command
*/
export function getVExecCommand(): string {
const config = getWorkspaceConfig();
return config.get("v.executablePath", "v");
}
/**
* Get v-analyzer configuration.
*/
export function getWorkspaceConfig(): vscode.WorkspaceConfiguration {
const currentWorkspaceFolder = getWorkspaceFolder();
const uri = currentWorkspaceFolder ? currentWorkspaceFolder.uri : null;
return vscode.workspace.getConfiguration("v-analyzer", uri);
}
/**
* Get the workspace of a current document.
* @param uri The URI of document
*/
export function getWorkspaceFolder(uri?: vscode.Uri): vscode.WorkspaceFolder {
if (uri) {
return vscode.workspace.getWorkspaceFolder(uri)!;
}
if (
vscode.workspace.workspaceFolders &&
vscode.workspace.workspaceFolders.length > 0
) {
return vscode.workspace.workspaceFolders[0];
}
if (vscode.window.activeTextEditor && vscode.window.activeTextEditor.document) {
return vscode.workspace.getWorkspaceFolder(
vscode.window.activeTextEditor.document.uri,
)!;
}
return null!;
}
/**
* Sets ['when'](https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts)
* clause contexts
*/
export function setContextValue(key: string, value: any): Thenable<void> {
return vscode.commands.executeCommand("setContext", key, value);
}
export type VlangDocument = vscode.TextDocument & { languageId: "v" };
export type VlangEditor = vscode.TextEditor & { document: VlangDocument };
export function isVlangDocument(
document: vscode.TextDocument,
): document is VlangDocument {
return document.languageId === "v" && document.uri.scheme === "file";
}
export function isVlangEditor(editor: vscode.TextEditor): editor is VlangEditor {
return isVlangDocument(editor.document);
}
export function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
================================================
FILE: editors/code/src/welcome.ts
================================================
import vscode from "vscode";
import path from "path";
import semver from "semver";
import { Command, ContextInit } from "./ctx";
import { getFromGlobalState, updateGlobalState } from "./stateUtils";
// Most of this code is copied from the Go extension's welcome.ts file. <3
export class WelcomePanel {
public static showWelcome(ctx: ContextInit): Command {
return WelcomePanel.createOrShow(ctx);
}
public static activate(ctx: ContextInit) {
if (vscode.window.registerWebviewPanelSerializer) {
// Make sure we register a serializer in activation event
vscode.window.registerWebviewPanelSerializer(WelcomePanel.viewType, {
async deserializeWebviewPanel(webviewPanel: vscode.WebviewPanel) {
WelcomePanel.revive(webviewPanel, ctx.extCtx.extensionUri);
},
});
}
showGoWelcomePage();
}
public static currentPanel: WelcomePanel | undefined;
public static readonly viewType = "welcomeV";
public static createOrShow(ctx: ContextInit) {
return () => {
const extensionUri = ctx.extCtx.extensionUri;
const column = vscode.window.activeTextEditor
? vscode.window.activeTextEditor.viewColumn
: undefined;
// If we already have a panel, show it.
if (WelcomePanel.currentPanel) {
WelcomePanel.currentPanel.panel.reveal(column);
return;
}
// Otherwise, create a new panel.
const panel = vscode.window.createWebviewPanel(
WelcomePanel.viewType,
"V for VS Code",
column || vscode.ViewColumn.One,
{
// And restrict the webview to only loading content from our extension's directory.
localResourceRoots: [joinPath(extensionUri)],
},
);
panel.iconPath = joinPath(extensionUri, "media", "logo.png");
WelcomePanel.currentPanel = new WelcomePanel(panel, extensionUri);
};
}
public static revive(panel: vscode.WebviewPanel, extensionUri: vscode.Uri) {
WelcomePanel.currentPanel = new WelcomePanel(panel, extensionUri);
}
public readonly dataroot: vscode.Uri; // exported for testing.
private readonly panel: vscode.WebviewPanel;
private readonly extensionUri: vscode.Uri;
private disposables: vscode.Disposable[] = [];
private constructor(panel: vscode.WebviewPanel, extensionUri: vscode.Uri) {
this.panel = panel;
this.extensionUri = extensionUri;
this.dataroot = joinPath(this.extensionUri, "media");
// Set the webview's initial html content
this.update();
// Listen for when the panel is disposed
// This happens when the user closes the panel or when the panel is closed programmatically
this.panel.onDidDispose(() => this.dispose(), null, this.disposables);
}
public dispose() {
WelcomePanel.currentPanel = undefined;
// Clean up our resources
this.panel.dispose();
while (this.disposables.length) {
const x = this.disposables.pop();
if (x) {
x.dispose();
}
}
}
private update() {
const webview = this.panel.webview;
this.panel.webview.html = this.getHtmlForWebview(webview);
}
private getHtmlForWebview(webview: vscode.Webview) {
const vAnalyzerExtension = vscode.extensions.getExtension(
"VOSCA.vscode-v-analyzer",
)!;
const vAnalyzerExtensionVersion = vAnalyzerExtension.packageJSON.version;
const stylePath = joinPath(this.dataroot, "welcome.css");
const logoPath = joinPath(this.dataroot, "logo.png");
const stylesURI = webview.asWebviewUri(stylePath);
const logoURI = webview.asWebviewUri(logoPath);
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!--
Use a content security policy to only allow loading images from https or from our extension directory,
and only allow scripts that have a specific nonce.
-->
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src ${webview.cspSource}; img-src ${webview.cspSource} https:;">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="${stylesURI}" rel="stylesheet">
<title>V for VS Code</title>
</head>
<body>
<main class="Content">
<div class="Header">
<img src="${logoURI}" alt="v-analyzer logo" class="Header-logo"/>
<div class="Header-details">
<h1 class="Header-title">V for VS Code v${vAnalyzerExtensionVersion}</h1>
<p>The v-analyzer extension for Visual Studio Code, providing rich language support for V projects.</p>
<ul class="Header-links">
<li><a href="https://github.com/vlang/v-analyzer/blob/main/editors/code/CHANGELOG.md">Release notes</a></li>
<li><a href="https://github.com/vlang/v-analyzer">GitHub</a></li>
<li><a href="https://discord.gg/vlang">Discord</a></li>
</ul>
</div>
</div>
<div class="Cards">
<div class="Card">
<div class="Card-inner">
<p class="Card-title">Getting started</p>
<p class="Card-content">Learn about the v-analyzer extension in
<a href="https://github.com/vlang/v-analyzer/tree/main/editors/code/README.md">README</a>.
</p>
</div>
</div>
<div class="Card">
<div class="Card-inner">
<p class="Card-title">Learning V</p>
<p class="Card-content">If you're new to the V programming language,
<a href="https://github.com/vlang/v/blob/master/doc/docs.md</a> is a great place to get started.</a>
</p>
</div>
</div>
<div class="Card">
<div class="Card-inner">
<p class="Card-title">Troubleshooting</p>
<p class="Card-content">Experiencing problems? Start with
<a href="https://github.com/vlang/v-analyzer/blob/main/editors/code/docs/troubleshooting.md">troubleshooting guide</a>.
</p>
</div>
</div>
</div>
</main>
</body>
</html>`;
}
}
function joinPath(uri: vscode.Uri, ...pathFragment: string[]): vscode.Uri {
// Reimplementation of
// https://github.com/microsoft/vscode/blob/b251bd952b84a3bdf68dad0141c37137dac55d64/src/vs/base/common/uri.ts#L346-L357
// with Node.JS path. This is a temporary workaround for https://github.com/eclipse-theia/theia/issues/8752.
if (!uri.path) {
throw new Error("[UriError]: cannot call joinPaths on URI without path");
}
return uri.with({
path: vscode.Uri.file(path.join(uri.fsPath, ...pathFragment)).path,
});
}
function showGoWelcomePage() {
// Update this list of versions when there is a new version where we want to
// show the welcome page on update.
const showVersions: string[] = ["0.0.2"];
let vExtensionVersion = "0.0.2";
let vExtensionVersionKey = "v-analyzer.extensionVersion111";
const savedVExtensionVersion = getFromGlobalState(vExtensionVersionKey, "0.0.0");
if (
shouldShowGoWelcomePage(showVersions, vExtensionVersion, savedVExtensionVersion)
) {
vscode.commands.executeCommand("v-analyzer.showWelcome");
}
if (vExtensionVersion !== savedVExtensionVersion) {
updateGlobalState(vExtensionVersionKey, vExtensionVersion);
}
}
export function shouldShowGoWelcomePage(
showVersions: string[],
newVersion: string,
oldVersion: string,
): boolean {
if (newVersion === oldVersion) {
return false;
}
const coercedNew = semver.coerce(newVersion);
const coercedOld = semver.coerce(oldVersion);
if (!coercedNew || !coercedOld) {
return true;
}
// Both semver.coerce(0.22.0) and semver.coerce(0.22.0-rc.1) will be 0.22.0.
return (
semver.gte(coercedNew, coercedOld) && showVersions.includes(coercedNew.toString())
);
}
================================================
FILE: editors/code/syntaxes/stree.tmGrammar.json
================================================
{
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
"scopeName": "source.stree",
"patterns": [
{
"include": "#node_type"
},
{
"include": "#node_range_index"
},
{
"include": "#token_text"
}
],
"repository": {
"node_type": {
"match": "^\\s*([a-z_][a-z_0-9]*?) (at)",
"captures": {
"1": {
"name": "entity.name.function"
},
"2": {
"name": "keyword"
}
}
},
"node_range_index": {
"match": "\\d+",
"name": "constant.numeric"
},
"token_text": {
"match": "\".+\"",
"name": "string"
}
},
"fileTypes": [
"stree"
]
}
================================================
FILE: editors/code/syntaxes/tests/accessor.vv
================================================
// SYNTAX TEST "source.v" "method accessor"
hello.world
// ^ punctuation.accessor.v
================================================
FILE: editors/code/syntaxes/tests/comma.vv
================================================
// SYNTAX TEST "source.v" "comma"
[1, 2, 3, 4]
// ^ punctuation.separator.comma.v
// ^ punctuation.separator.comma.v
// ^ punctuation.separator.comma.v
================================================
FILE: editors/code/syntaxes/tests/comment.vv
================================================
// SYNTAX TEST "source.v" "comment"
#!/usr/bin/env -S v
// ^^ punctuation.definition.comment.shebang.v
// ^^^^^^^^^^^^^^^^^ meta.shebang.v
// ^^^^^^^^^^^^^^^^^^^ comment.line.number-sign.v
================================================
FILE: editors/code/syntaxes/tests/comparison.vv
================================================
// SYNTAX TEST "source.v" "comparison"
0 == 0
//^^ keyword.operator.relation.v
// ^ constant.numeric.integer.v
================================================
FILE: editors/code/syntaxes/tests/dot.int.vv
================================================
// SYNTAX TEST "source.v"
hello.int()
// ^^^ entity.name.function.v
================================================
FILE: editors/code/syntaxes/tests/escape.vv
================================================
// SYNTAX TEST "source.v"
@type
// ^^^^^ source.v - keyword.type.v
================================================
FILE: editors/code/syntaxes/tests/hashtag.vv
================================================
// SYNTAX TEST "source.v" "hashtag"
#include <test>
// ^^^^^^^^^^^^^^^ markup.bold.v
#define foo bar
// ^^^^^^^^^^^^ markup.bold.v
#some javascript line
// ^^^^^^^^^^^^^^^^^^ markup.bold.v
================================================
FILE: editors/code/syntaxes/tests/method.vv
================================================
// SYNTAX TEST "source.v" "method accessor"
hello.method()
// ^^^^^^ entity.name.function.v
================================================
FILE: editors/code/syntaxes/tests/numbers.vv
================================================
// SYNTAX TEST "source.v" "numbers"
_ := 1_000_000
// ^^^^^^^^^ constant.numeric.integer.v
_ := 3_122.55
// ^^^^^^^^ constant.numeric.float.v
_ := 3.14e11
// ^^^^^^^ constant.numeric.exponential.v
_ := 0xF_F
// ^^^^^ constant.numeric.hex.v
_ := 0o17_3
// ^^^^^^ constant.numeric.octal.v
_ := 0b0_11
// ^^^^^^ constant.numeric.binary.v
================================================
FILE: editors/code/syntaxes/tests/optional.vv
================================================
// SYNTAX TEST "source.v" "optional"
fn f(url string) ?string {
// ^ keyword.operator.optional.v
================================================
FILE: editors/code/syntaxes/tests/pubfn.vv
================================================
// SYNTAX TEST "source.v"
pubfn
// ^^^ - storage.modifier.v
// ^^ - keyword.fn.v
pub fn
// ^^^ storage.modifier.v
// ^^ keyword.fn.v
pub fn
// test
// ^^ comment.line.double-slash.v
================================================
FILE: editors/code/syntaxes/tests/string.vv
================================================
// SYNTAX TEST "source.v" "string"
_ := 'test'
// ^^^^ string.quoted.v
a := 1
_ := '${a}'
// ^^ variable.other.interpolated.v
_ := '\\'
// ^^ constant.character.escape.v
_ := c'test'
// ^ storage.type.string.v
_ := `r`
// ^^^ string.quoted.rune.v
_ := r'\'
// ^ storage.type.string.v
// ^^^ string.quoted.raw.v
_ := r'\'
// ^ storage.type.string.v
// ^^^ string.quoted.raw.v
================================================
FILE: editors/code/syntaxes/tests/type.vv
================================================
// SYNTAX TEST "source.v"
struct Foo {}
// ^^^ entity.name.type.v
union Foo {}
// ^^^ entity.name.type.v
pub interface Foo {}
// ^^^ storage.modifier.pub.v
// ^^^^^^^^^ storage.type.interface.v
// ^^^ entity.name.type.v
type Foo = int
// ^^^ entity.name.type.v
================================================
FILE: editors/code/syntaxes/tests/variable.vv
================================================
// SYNTAX TEST "source.v"
abc := ''
// ^^^ variable.other.assignment.v
mut abc := ''
// ^^^ variable.other.assignment.v
abc = ''
// ^^^ variable.other.assignment.v
abc, foo := '', ''
// ^^^ variable.other.assignment.v
// ^^ - variable.other.assignment.v
// ^^^ variable.other.assignment.v
variable2 := 2
// ^^^^^^^^^ variable.other.assignment.v
// ^ - constant.numeric.integer.v
================================================
FILE: editors/code/syntaxes/v.mod.tmLanguage.json
================================================
{
"scopeName": "source.v.mod",
"patterns": [
{
"include": "#module-decl"
},
{
"include": "#brackets"
}
],
"repository": {
"module-decl": {
"name": "keyword.module.v.mod",
"match": "\\bModule\\b"
},
"brackets": {
"patterns": [
{
"begin": "{",
"beginCaptures": {
"0": {
"name": "punctuation.definition.bracket.curly.begin.v.mod"
}
},
"patterns": [
{
"include": "#field"
},
{
"include": "#string"
}
],
"end": "}",
"endCaptures": {
"0": {
"name": "punctuation.definition.bracket.curly.end.v.mod"
}
}
}
]
},
"field": {
"name": "meta.definition.field.v.mod",
"match": "\\b(\\w+):"
},
"string": {
"name": "string.v.mod",
"begin": "'|\"",
"beginCaptures": {
"0": {
"name": "string.v.mod"
}
},
"end": "'|\"",
"endCaptures": {
"0": {
"name": "string.v.mod"
}
}
}
}
}
================================================
FILE: editors/code/syntaxes/v.tmLanguage.json
================================================
{
"name": "V",
"scopeName": "source.v",
"fileTypes": [
".v",
".vsh",
".vv"
],
"patterns": [
{
"include": "#comments"
},
{
"include": "#as-is"
},
{
"include": "#attributes"
},
{
"include": "#assignment"
},
{
"include": "#module-decl"
},
{
"include": "#import-decl"
},
{
"include": "#hash-decl"
},
{
"include": "#brackets"
},
{
"include": "#builtin-fix"
},
{
"include": "#escaped-fix"
},
{
"include": "#operators"
},
{
"include": "#function-exist"
},
{
"include": "#generic"
},
{
"include": "#constants"
},
{
"include": "#type"
},
{
"include": "#enum"
},
{
"include": "#interface"
},
{
"include": "#struct"
},
{
"include": "#keywords"
},
{
"include": "#storage"
},
{
"include": "#numbers"
},
{
"include": "#strings"
},
{
"include": "#types"
},
{
"include": "#punctuations"
},
{
"include": "#variable-assign"
}
],
"repository": {
"as-is": {
"begin": "\\s+(as|is)\\s+",
"beginCaptures": {
"1": {
"name": "keyword.$1.v"
}
},
"end": "([\\w.]*)",
"endCaptures": {
"1": {
"name": "entity.name.alias.v"
}
}
},
"assignment": {
"name": "meta.definition.variable.v",
"match": "\\s+((?:\\:|\\+|\\-|\\*|/|\\%|\\&|\\||\\^)?=)\\s+",
"captures": {
"1": {
"patterns": [
{
"include": "#operators"
}
]
}
}
},
"attributes": {
"name": "meta.definition.attribute.v",
"match": "^\\s*((\\[)(deprecated|unsafe|console|heap|manualfree|typedef|live|inline|flag|ref_only|direct_array_access|callconv)(\\]))",
"captures": {
"1": {
"name": "meta.function.attribute.v"
},
"2": {
"name": "punctuation.definition.begin.bracket.square.v"
},
"3": {
"name": "storage.modifier.attribute.v"
},
"4": {
"name": "punctuation.definition.end.bracket.square.v"
}
}
},
"variable-assign": {
"match": "[a-zA-Z_]\\w*(?:,\\s*[a-zA-Z_]\\w*)*(?=\\s*(?:=|:=))",
"captures": {
"0": {
"patterns": [
{
"match": "[a-zA-Z_]\\w*",
"name": "variable.other.assignment.v"
},
{
"include": "#punctuation"
}
]
}
}
},
"module-decl": {
"name": "meta.module.v",
"begin": "^\\s*(module)\\s+",
"beginCaptures": {
"1": {
"name": "keyword.module.v"
}
},
"end": "([\\w.]+)",
"endCaptures": {
"1": {
"name": "entity.name.module.v"
}
}
},
"import-decl": {
"name": "meta.import.v",
"begin": "^\\s*(import)\\s+",
"beginCaptures": {
"1": {
"name": "keyword.import.v"
}
},
"end": "([\\w.]+)",
"endCaptures": {
"1": {
"name": "entity.name.import.v"
}
}
},
"hash-decl": {
"name": "markup.bold.v",
"begin": "^\\s*(#)",
"end": "$"
},
"brackets": {
"patterns": [
{
"begin": "{",
"beginCaptures": {
"0": {
"name": "punctuation.definition.bracket.curly.begin.v"
}
},
"end": "}",
"endCaptures": {
"0": {
"name": "punctuation.definition.bracket.curly.end.v"
}
},
"patterns": [
{
"include": "$self"
}
]
},
{
"begin": "\\(",
"beginCaptures": {
"0": {
"name": "punctuation.definition.bracket.round.begin.v"
}
},
"end": "\\)",
"endCaptures": {
"0": {
"name": "punctuation.definition.bracket.round.end.v"
}
},
"patterns": [
{
"include": "$self"
}
]
},
{
"begin": "\\[",
"beginCaptures": {
"0": {
"name": "punctuation.definition.bracket.square.begin.v"
}
},
"end": "\\]",
"endCaptures": {
"0": {
"name": "punctuation.definition.bracket.square.end.v"
}
},
"patterns": [
{
"include": "$self"
}
]
}
]
},
"builtin-fix": {
"patterns": [
{
"patterns": [
{
"name": "storage.modifier.v",
"match": "(const)(?=\\s*\\()"
},
{
"name": "keyword.$1.v",
"match": "\\b(fn|type|enum|struct|union|interface|map|assert|sizeof|typeof|__offsetof)\\b(?=\\s*\\()"
}
]
},
{
"patterns": [
{
"name": "keyword.control.v",
"match": "(\\$if|\\$else)(?=\\s*\\()"
},
{
"name": "keyword.control.v",
"match": "\\b(as|in|is|or|break|continue|unsafe|match|if|else|for|go|spawn|goto|defer|return|shared|select|rlock|lock|atomic|asm)\\b(?=\\s*\\()"
}
]
},
{
"patterns": [
{
"match": "(?<!.)(i?(?:8|16|nt|64|128)|u?(?:16|32|64|128)|f?(?:32|64))(?=\\s*\\()",
"captures": {
"1": {
"name": "storage.type.numeric.v"
}
},
"name": "meta.expr.numeric.cast.v"
},
{
"match": "(bool|byte|byteptr|charptr|voidptr|string|rune|size_t|[ui]size)(?=\\s*\\()",
"captures": {
"1": {
"name": "storage.type.$1.v"
}
},
"name": "meta.expr.bool.cast.v"
}
]
}
]
},
"comments": {
"patterns": [
{
"name": "comment.block.documentation.v",
"begin": "/\\*",
"beginCaptures": {
"0": {
"name": "punctuation.definition.comment.begin.v"
}
},
"end": "\\*/",
"endCaptures": {
"0": {
"name": "punctuation.definition.comment.end.v"
}
},
"patterns": [
{
"include": "#comments"
}
]
},
{
"name": "comment.line.double-slash.v",
"begin": "//",
"beginCaptures": {
"0": {
"name": "punctuation.definition.comment.begin.v"
}
},
"end": "$"
},
{
"name": "comment.line.number-sign.v",
"match": "(?:^|\\s*)(?:((#!).*))",
"captures": {
"1": {
"name": "meta.shebang.v"
},
"2": {
"name": "punctuation.definition.comment.shebang.v"
}
}
}
]
},
"constants": {
"name": "constant.language.v",
"match": "\\b(true|false|none)\\b"
},
"generic": {
"patterns": [
{
"name": "meta.definition.generic.v",
"match": "(?<=[\\w\\s+])(\\<)(\\w+)(\\>)",
"captures": {
"1": {
"name": "punctuation.definition.bracket.angle.begin.v"
},
"2": {
"patterns": [
{
"include": "#illegal-name"
},
{
"match": "\\w+",
"name": "entity.name.generic.v"
}
]
},
"3": {
"name": "punctuation.definition.bracket.angle.end.v"
}
}
}
]
},
"function-exist": {
"name": "meta.support.function.v",
"match": "(\\w+)((?<=[\\w\\s+])(\\[)([\\w, ]+)(\\]))?(?=\\s*\\()",
"captures": {
"0": {
"name": "meta.function.call.v"
},
"1": {
"patterns": [
{
"include": "#illegal-name"
},
{
"match": "\\w+",
"name": "entity.name.function.v"
}
]
},
"2": {
"patterns": [
{
"include": "#generic"
}
]
}
}
},
"type": {
"name": "meta.definition.type.v",
"match": "^\\s*(?:(pub)?\\s+)?(type)\\s+(\\w*)\\s+(?:\\w+\\.+)?(\\w*)",
"captures": {
"1": {
"name": "storage.modifier.$1.v"
},
"2": {
"name": "storage.type.type.v"
},
"3": {
"patterns": [
{
"include": "#illegal-name"
},
{
"include": "#types"
},
{
"name": "entity.name.type.v",
"match": "\\w+"
}
]
},
"4": {
"patterns": [
{
"include": "#illegal-name"
},
{
"include": "#types"
},
{
"name": "entity.name.type.v",
"match": "\\w+"
}
]
}
}
},
"enum": {
"name": "meta.definition.enum.v",
"match": "^\\s*(?:(pub)?\\s+)?(enum)\\s+(?:\\w+\\.)?(\\w*)",
"captures": {
"1": {
"name": "storage.modifier.$1.v"
},
"2": {
"name": "storage.type.enum.v"
},
"3": {
"name": "entity.name.enum.v"
}
}
},
"interface": {
"name": "meta.definition.interface.v",
"match": "^\\s*(?:(pub)?\\s+)?(interface)\\s+(\\w*)",
"captures": {
"1": {
"name": "storage.modifier.$1.v"
},
"2": {
"name": "storage.type.interface.v"
},
"3": {
"patterns": [
{
"include": "#illegal-name"
},
{
"name": "entity.name.type.v",
"match": "\\w+"
}
]
}
}
},
"struct": {
"patterns": [
{
"name": "meta.definition.struct.v",
"begin": "^\\s*(?:(mut|pub(?:\\s+mut)?|__global)\\s+)?(struct|union)\\s+([\\w.]+)\\s*|({)",
"beginCaptures": {
"1": {
"name": "storage.modifier.$1.v"
},
"2": {
"name": "storage.type.struct.v"
},
"3": {
"name": "entity.name.type.v"
},
"4": {
"name": "punctuation.definition.bracket.curly.begin.v"
}
},
"end": "\\s*|(})",
"endCaptures": {
"1": {
"name": "punctuation.definition.bracket.curly.end.v"
}
},
"patterns": [
{
"include": "#struct-access-modifier"
},
{
"match": "\\b(\\w+)\\s+([\\w\\[\\]\\*&.]+)(?:\\s*(=)\\s*((?:.(?=$|//|/\\*))*+))?",
"captures": {
"1": {
"name": "variable.other.property.v"
},
"2": {
"patterns": [
{
"include": "#numbers"
},
{
"include": "#brackets"
},
{
"include": "#types"
},
{
"match": "\\w+",
"name": "storage.type.other.v"
}
]
},
"3": {
"name": "keyword.operator.assignment.v"
},
"4": {
"patterns": [
{
"include": "$self"
}
]
}
}
},
{
"include": "#types"
},
{
"include": "$self"
}
]
},
{
"name": "meta.definition.struct.v",
"match": "^\\s*(?:(mut|pub(?:\\s+mut)?|__global))\\s+?(struct)\\s+(?:\\s+([\\w.]+))?",
"captures": {
"1": {
"name": "storage.modifier.$1.v"
},
"2": {
"name": "storage.type.struct.v"
},
"3": {
"name": "entity.name.struct.v"
}
}
}
]
},
"struct-access-modifier": {
"match": "(?<=\\s|^)(mut|pub(?:\\s+mut)?|__global)(:|\\b)",
"captures": {
"1": {
"name": "storage.modifier.$1.v"
},
"2": {
"name": "punctuation.separator.struct.key-value.v"
}
}
},
"punctuation": {
"patterns": [
{
"name": "punctuation.delimiter.period.dot.v",
"match": "\\."
},
{
"name": "punctuation.delimiter.comma.v",
"match": ","
},
{
"name": "punctuation.separator.key-value.colon.v",
"match": ":"
},
{
"name": "punctuation.definition.other.semicolon.v",
"match": ";"
},
{
"name": "punctuation.definition.other.questionmark.v",
"match": "\\?"
},
{
"name": "punctuation.hash.v",
"match": "#"
}
]
},
"keywords": {
"patterns": [
{
"name": "keyword.control.v",
"match": "(\\$if|\\$else|\\$for)"
},
{
"name": "keyword.control.v",
"match": "(?<!@)\\b(as|is|in|or|break|continue|unsafe|match|if|else|for|go|spawn|goto|defer|return|shared|select|rlock|lock|atomic|asm|__global|nil)\\b"
},
{
"name": "keyword.$1.v",
"match": "(?<!@)\\b(fn|type|typeof|enum|struct|implements|interface|map|assert|sizeof|__offsetof)\\b"
}
]
},
"storage": {
"name": "storage.modifier.v",
"match": "\\b(const|mut|pub)\\b"
},
"types": {
"patterns": [
{
"name": "storage.type.numeric.v",
"match": "(?<!\\.)\\b(i(8|16|nt|64|128)|u(8|16|32|64|128)|f(32|64))\\b"
},
{
"name": "storage.type.$1.v",
"match": "(?<!\\.)\\b(bool|byte|byteptr|charptr|voidptr|string|ustring|rune)\\b"
}
]
},
"operators": {
"patterns": [
{
"name": "keyword.operator.arithmetic.v",
"match": "(\\+|\\-|\\*|\\/|\\%|\\+\\+|\\-\\-|\\>\\>|\\<\\<)"
},
{
"name": "keyword.operator.relation.v",
"match": "(\\=\\=|\\!\\=|\\>|\\<|\\>\\=|\\<\\=)"
},
{
"name": "keyword.operator.assignment.v",
"match": "(\\:\\=|\\=|\\+\\=|\\-\\=|\\*\\=|\\/\\=|\\%\\=|\\&\\=|\\|\\=|\\^\\=|\\~\\=|\\&\\&\\=|\\|\\|\\=|\\>\\>\\=|\\<\\<\\=)"
},
{
"name": "keyword.operator.bitwise.v",
"match": "(\\&|\\||\\^|\\~|<(?!<)|>(?!>))"
},
{
"name": "keyword.operator.logical.v",
"match": "(\\&\\&|\\|\\||\\!)"
},
{
"name": "keyword.operator.optional.v",
"match": "\\?"
}
]
},
"numbers": {
"patterns": [
{
"name": "constant.numeric.exponential.v",
"match": "(?<!\\w)([0-9]+(_?))+(\\.)([0-9]+[eE][-+]?[0-9]+)"
},
{
"name": "constant.numeric.float.v",
"match": "(?<!\\w)([0-9]+(_?))+(\\.)([0-9]+)"
},
{
"name": "constant.numeric.binary.v",
"match": "(?<!\\w)(?:0b)(?:(?:[0-1]+)(?:_?))+"
},
{
"name": "constant.numeric.octal.v",
"match": "(?<!\\w)(?:0o)(?:(?:[0-7]+)(?:_?))+"
},
{
"name": "constant.numeric.hex.v",
"match": "(?<!\\w)(?:0x)(?:(?:[0-9a-fA-F]+)(?:_?))+"
},
{
"name": "constant.numeric.integer.v",
"match": "(?<!\\w)(?:(?:[0-9]+)(?:[_]?))+"
}
]
},
"punctuations": {
"patterns": [
{
"name": "punctuation.accessor.v",
"match": "(?:\\.)"
},
{
"name": "punctuation.separator.comma.v",
"match": "(?:,)"
}
]
},
"strings": {
"patterns": [
{
"begin": "`",
"end": "`",
"name": "string.quoted.rune.v",
"patterns": [
{
"include": "#string-escaped-char"
},
{
"include": "#string-interpolation"
},
{
"include": "#string-placeholder"
}
]
},
{
"begin": "(r)'",
"beginCaptures": {
"1": {
"name": "storage.type.string.v"
}
},
"end": "'",
"name": "string.quoted.raw.v",
"patterns": [
{
"include": "#string-interpolation"
},
{
"include": "#string-placeholder"
}
]
},
{
"begin": "(r)\"",
"beginCaptures": {
"1": {
"name": "storage.type.string.v"
}
},
"end": "\"",
"name": "string.quoted.raw.v",
"patterns": [
{
"include": "#string-interpolation"
},
{
"include": "#string-placeholder"
}
]
},
{
"begin": "(c?)'",
"beginCaptures": {
"1": {
"name": "storage.type.string.v"
}
},
"end": "'",
"name": "string.quoted.v",
"patterns": [
{
"include": "#string-escaped-char"
},
{
"include": "#string-interpolation"
},
{
"include": "#string-placeholder"
}
]
},
{
"begin": "(c?)\"",
"beginCaptures": {
"1": {
"name": "storage.type.string.v"
}
},
"end": "\"",
"name": "string.quoted.v",
"patterns": [
{
"include": "#string-escaped-char"
},
{
"include": "#string-interpolation"
},
{
"include": "#string-placeholder"
}
]
}
]
},
"string-escaped-char": {
"patterns": [
{
"name": "constant.character.escape.v",
"match": "\\\\([0-7]{3}|[\\$abfnrtv\\\\'\"]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})"
},
{
"name": "invalid.illegal.unknown-escape.v",
"match": "\\\\[^0-7\\$xuUabfnrtv\\'\"]"
}
]
},
"string-interpolation": {
"name": "meta.string.interpolation.v",
"match": "(\\$(\\{.*?\\}))",
"captures": {
"1": {
"patterns": [
{
"name": "variable.other.interpolated.v",
"match": "\\$(\\{.*?\\})"
}
]
}
}
},
"string-placeholder": {
"match": "%(\\[\\d+\\])?([\\+#\\-0\\x20]{,2}((\\d+|\\*)?(\\.?(\\d+|\\*|(\\[\\d+\\])\\*?)?(\\[\\d+\\])?)?))?[vT%tbcdoqxXUbeEfFgGsp]",
"name": "constant.other.placeholder.v"
},
"illegal-name": {
"match": "\\d\\w+",
"name": "invalid.illegal.v"
}
}
}
================================================
FILE: editors/code/tsconfig.json
================================================
{
"compilerOptions": {
"module": "CommonJS",
"moduleResolution": "node",
"target": "ES6",
"lib": [
"ES2022",
"DOM"
],
"baseUrl": "src",
"outDir": "dist",
"rootDir": "src",
"allowJs": true,
"diagnostics": false,
"esModuleInterop": true,
"incremental": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUncheckedIndexedAccess": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"sourceMap": false,
"stripInternal": true
},
"include": [
"src/**/*.ts"
],
"exclude": [
"node_modules",
"scripts"
]
}
================================================
FILE: install.vsh
================================================
#!/usr/bin/env -S v
// This script is used to install and update v-analyzer.
import os
import json
import term
import time
import compress.szip
import cli
import net.http
const installer_version = '0.0.6'
const analyzer_config_dir_path = join_path(home_dir(), '.config', 'v-analyzer')
const analyzer_sources_dir_path = join_path(analyzer_config_dir_path, 'sources')
const analyzer_bin_dir_path = join_path(analyzer_config_dir_path, 'bin')
const analyzer_bin_file_path = join_path(analyzer_bin_dir_path, 'v-analyzer')
const is_github_job = os.getenv('GITHUB_JOB') != ''
struct ReleaseAsset {
tag_name string @[json: '-']
browser_download_url string
}
fn (a ReleaseAsset) name() string {
return a.browser_download_url
.all_after_last('/')
.trim_string_right('.zip')
}
fn (a ReleaseAsset) os_arch() string {
name := a.name()
parts := name.split('-')
return parts[2..].join('-')
}
struct ReleaseInfo {
tag_name string
assets []ReleaseAsset
}
fn current_version() !string {
version_res := os.execute('v-analyzer --version')
if version_res.exit_code != 0 {
return error('Failed to get current version: ${version_res.output}')
}
return version_res.output.trim_string_left('v-analyzer version ').trim_space()
}
fn check_updates(release_type string) ! {
asset := find_latest_asset(release_type) or {
if err.msg().starts_with('Unsupported') {
update_from_sources(true, false)!
return
}
errorln('Failed to find latest release: ${err}')
return
}
cur_version := current_version() or {
errorln('${err}')
return
}
local_version := stable_version(cur_version)
asset_version := stable_version(asset.tag_name)
if local_version == asset_version {
println('You already have the latest version of ${term.bold('v-analyzer')}: ${cur_version}')
return
}
println('New version of ${term.bold('v-analyzer')} is available: ${term.bold(asset.tag_name)}')
}
fn stable_version(ver string) string {
return ver.split('.')#[..4].join('.')
}
fn update(nightly bool, release_type string) ! {
if nightly {
println('Installing latest nightly version...')
update_from_sources(true, true)!
return
}
println('Checking for updates...')
println('Fetching latest release info from GitHub...')
asset := find_latest_asset(release_type) or {
if err.msg().starts_with('Unsupported') {
update_from_sources(true, false)!
return
}
errorln('Failed to find latest release: ${err}')
return
}
cur_version := current_version() or {
errorln('${err}')
return
}
asset_version := asset.tag_name
if cur_version.trim_string_left('v') == asset_version.trim_string_left('v') {
println('You already have the latest version of ${term.bold('v-analyzer')}: ${cur_version}')
return
}
println('Found new version of ${term.bold('v-analyzer')}: ${asset_version}')
install_from_binary(asset, true)!
}
fn install(no_interaction bool, release_type string) ! {
println('Downloading ${term.bold('v-analyzer')}...')
println('Fetching latest release info from GitHub...')
asset := find_latest_asset(release_type) or {
install_from_sources(no_interaction)!
return
}
println('Found ${term.bold('v-analyzer')} binary for your platform: ${asset.os_arch()}')
install_from_binary(asset, false)!
}
fn install_from_binary(asset ReleaseAsset, update bool) ! {
println('> Download from url: ${asset.browser_download_url} ...')
print('Downloading ${term.bold('v-analyzer')} archive')
os.flush()
archive_temp_dir := os.join_path(os.temp_dir(), 'v-analyzer', 'archive')
os.mkdir_all(archive_temp_dir) or {
println('Failed to create temp directory for archive: ${archive_temp_dir}')
return
}
archive_temp_path := os.join_path(archive_temp_dir, 'v-analyzer.zip')
download_file_with_progress(asset.browser_download_url, archive_temp_path)
println('${term.green('✓')} Successfully downloaded ${term.bold('v-analyzer')} archive')
println('Extracting ${term.bold('v-analyzer')} archive...')
os.mkdir_all(analyzer_bin_dir_path) or {
println('Failed to create directory: ${analyzer_bin_dir_path}')
return
}
szip.extract_zip_to_dir(archive_temp_path, analyzer_bin_dir_path) or {
println('Failed to extract archive: ${err}')
return
}
println('${term.green('✓')} Successfully extracted ${term.bold('v-analyzer')} archive')
if update {
println('${term.green('✓')} ${term.bold('v-analyzer')} successfully updated to ${term.bold(asset.tag_name)}')
}
show_info_about_binary(analyzer_bin_file_path)
if !update {
show_hint_about_path_if_needed(analyzer_bin_file_path)
}
os.mkdir_all(analyzer_sources_dir_path) or {
println('Failed to create directory: ${analyzer_sources_dir_path}')
return
}
}
fn find_latest_asset(release_type string) !ReleaseAsset {
text := http.get_text('https://api.github.com/repos/vlang/v-analyzer/releases/latest')
res := json.decode(ReleaseInfo, text) or {
errorln('Failed to decode JSON response from GitHub: ${err}')
return error('Failed to decode JSON response from GitHub: ${err}')
}
os_ := os_name() or { return error('Unsupported OS') }
arch := arch_name() or { return error('Unsupported architecture') }
mut filename := build_os_arch(os_, arch)
if release_type != '' {
filename += '-${release_type}'
}
asset := res.assets.filter(it.os_arch() == filename)[0] or {
return error('Unsupported OS or architecture')
}
return ReleaseAsset{
...asset
tag_name: res.tag_name
}
}
// download_file downloads file from the given URL to the given path.
// Returns channel that will be closed when the download is finished.
// If the download fails, the channel will be closed with false value.
fn download_file(path string, to string) chan bool {
ch := chan bool{}
spawn fn [ch, path, to] () {
http.download_file(path, to) or {
println('Failed to download file: ${err}')
ch <- false
ch.close()
return
}
ch <- true
ch.close()
}()
return ch
}
fn download_file_with_progress(path string, to string) {
ch := download_file(path, to)
for {
select {
_ := <-ch {
println('')
break
}
500 * time.millisecond {
print('.')
os.flush()
}
}
}
}
fn build_os_arch(os_name string, arch string) string {
return '${os_name}-${arch}'
}
fn update_from_sources(update bool, nightly bool) ! {
mut need_pull := true
if !already_cloned() {
clone_repository()!
need_pull = false
}
if need_pull {
println('Updating ${term.bold('v-analyzer')} sources...')
res := os.execute('git -C ${analyzer_sources_dir_path} pull')
if res.exit_code != 0 {
errorln('Failed to update sources: ${res.output}')
return
}
println('${term.green('✓')} Successfully updated ${term.bold('v-analyzer')} sources')
}
build_from_sources()!
if update {
hash := get_latest_commit_hash() or {
errorln(err.str())
return
}
updated_version := if nightly {
'nightly (${hash})'
} else {
hash
}
println('${term.green('✓')} ${term.bold('v-analyzer')} successfully updated to ${updated_version}')
}
show_info_about_binary(analyzer_bin_file_path)
return
}
fn show_info_about_binary(analyzer_bin_file_path string) {
println('Path to the binary: ${term.bold(analyzer_bin_file_path)}')
println('Size of the binary: ${term.bold(os.file_size(analyzer_bin_file_path).str())}')
bversion := os.execute('${os.quoted_path(analyzer_bin_file_path)} version')
println('Binary version: ${term.bold(bversion.output.trim_space())}')
}
fn get_latest_commit_hash() !string {
hash_res := os.execute('git -C ${analyzer_sources_dir_path} log -1 --format=%H')
if hash_res.exit_code != 0 {
return error('Failed to get hash of the latest commit: ${hash_res.output}')
}
return hash_res.output.trim_space()
}
const git_clone_options = '--filter=blob:none --recursive --shallow-submodules'
fn install_from_sources(no_interaction bool) ! {
println('${term.yellow('[WARNING]')} Currently ${term.bold('v-analyzer')} has no prebuilt binaries for your platform')
// Used primarily for VS Code extension
if !(is_github_job || no_interaction) {
mut answer := os.input('Do you want to build it from sources? (y/n) ')
if answer != 'y' {
println('')
println('Ending the update process')
warnln('${term.bold('v-analyzer')} is not installed!')
println('')
println('${term.bold('[NOTE]')} If you want to build it from sources manually, run the following commands:')
println('git clone ${git_clone_options} https://github.com/vlang/v-analyzer.git')
println('cd v-analyzer')
println('v build.vsh')
println(term.gray('# Optionally you can move the binary to the standard location:'))
println('mkdir -p ${analyzer_bin_dir_path}')
println('cp ./bin/v-analyzer ${analyzer_bin_dir_path}')
return
}
}
println('... building from source ...')
if already_cloned() {
println('... removing already cloned folder ...')
os.rmdir_all(analyzer_sources_dir_path) or {
errorln('Failed to remove directory: ${analyzer_sources_dir_path}: ${err}')
return
}
}
clone_repository()!
build_from_sources()!
show_info_about_binary(analyzer_bin_file_path)
show_hint_about_path_if_needed(analyzer_bin_file_path)
}
fn clone_repository() ! {
println('Cloning ${term.bold('v-analyzer')} repository...')
exit_code := run_command('git clone ${git_clone_options} https://github.com/vlang/v-analyzer.git ${analyzer_sources_dir_path} 2>&1') or {
errorln('Failed to clone v-analyzer repository: ${err}')
return
}
if exit_code != 0 {
errorln('Failed to clone v-analyzer repository')
return
}
println('${term.green('✓')} ${term.bold('v-analyzer')} repository cloned successfully')
}
fn build_from_sources() ! {
println('Building ${term.bold('v-analyzer')}...')
chdir(analyzer_sources_dir_path)!
install_deps_cmd := os.execute('v install')
if install_deps_cmd.exit_code != 0 {
errorln('Failed to install dependencies for ${term.bold('v-analyzer')}')
eprintln(install_deps_cmd.output)
return
}
println('${term.green('✓')} Dependencies for ${term.bold('v-analyzer')} installed successfully')
chdir(analyzer_sources_dir_path)!
exit_code := run_command('v build.vsh 1>/dev/null') or {
errorln('Failed to build ${term.bold('v-analyzer')}: ${err}')
return
}
if exit_code != 0 {
errorln('Failed to build ${term.bold('v-analyzer')}')
return
}
println('Moving ${term.bold('v-analyzer')} binary to the standard location...')
os.mkdir_all(analyzer_bin_dir_path) or {
println('Failed to create directory: ${analyzer_bin_dir_path}')
return
}
os.cp_all('${analyzer_sources_dir_path}/bin/v-analyzer' + $if windows { '.exe' } $else { '' },
analyzer_bin_dir_path, true) or {
println('Failed to copy ${term.bold('v-analyzer')} binary to ${analyzer_bin_dir_path}: ${err}')
return
}
println('${term.green('✓')} Successfully moved ${term.bold('v-analyzer')} binary to ${analyzer_bin_dir_path}')
println('${term.green('✓')} ${term.bold('v-analyzer')} built successfully')
}
fn already_cloned() bool {
if !os.exists(analyzer_sources_dir_path) {
return false
}
files := os.ls(analyzer_sources_dir_path) or { return false }
return files.len > 0
}
fn show_hint_about_path_if_needed(abs_path string) {
if !need_show_hint_about_path(abs_path) {
return
}
quoted_abs_path := '"${abs_path}"'
print('Add it to your ${term.bold('PATH')} to use it from anywhere or ')
println('specify the full path to the binary in your editor settings')
println('')
print('For example in VS Code ')
println(term.bold('settings.json:'))
println('${term.bold('{')}')
println(' ${term.yellow('"v-analyzer.serverPath"')}: ${term.green(quoted_abs_path)}')
println('${term.bold('}')}')
}
fn need_show_hint_about_path(abs_path string) bool {
dir := os.dir(abs_path)
path := os.getenv('PATH')
paths := path.split(os.path_delimiter)
return paths.filter(it == dir).len == 0
}
fn os_name() ?string {
$if macos {
return 'darwin'
}
name := os.user_os()
if name == 'unknown' {
return none
}
return name
}
fn arch_name() ?string {
$if arm64 {
return 'arm64'
}
$if amd64 || x64 {
return 'x86_64'
}
return none
}
fn run_command(cmd string) !int {
$if windows {
fixed_command := cmd
.trim_string_right('2>&1')
.trim_string_right('1>/dev/null')
res := os.execute(fixed_command)
println(res.output)
return res.exit_code
}
mut command := os.Command{
path: cmd
redirect_stdout: true
}
command.start()!
for !command.eof {
println(command.read_line())
}
command.close()!
return command.exit_code
}
pub fn errorln(msg string) {
eprintln('${term.red('[ERROR]')} ${msg}')
}
pub fn warnln(msg string) {
println('${term.yellow('[WARNING]')} ${msg}')
}
pub fn get_release_type(cmd cli.Command) string {
return cmd.flags.get_string('debug') or {
return cmd.flags.get_string('dev') or {
if cmd.flags.get_string('release') or { return '' } != '' {
return ''
}
return ''
}
}
}
fn main() {
println('Installer version: ${term.bold(installer_version)}')
mut cmd := cli.Command{
name: 'v-analyzer-installer-updated'
version: installer_version
description: 'Install and update v-analyzer'
posix_mode: true
execute: fn (cmd cli.Command) ! {
no_interaction := cmd.flags.get_bool('no-interaction') or { is_github_job }
release_type := get_release_type(cmd)
install(no_interaction, release_type)!
}
flags: [
cli.Flag{
flag: .bool
name: 'no-interaction' // Used primarily for VS Code extension, to install v-analyzer from sources
description: 'Do not ask any questions, use default values'
},
]
}
cmd.add_command(cli.Command{
name: 'up'
description: 'Update v-analyzer to the latest version'
posix_mode: true
execute: fn (cmd cli.Command) ! {
nightly := cmd.flags.get_bool('nightly') or { false }
release_type := get_release_type(cmd)
update(nightly, release_type)!
}
flags: [
cli.Flag{
flag: .bool
name: 'nightly'
description: 'Install the latest nightly build'
},
]
})
cmd.add_command(cli.Command{
name: 'check-availability'
description: 'Check if v-analyzer binary is available for the current platform (service command for editors)'
posix_mode: true
execute: fn (cmd cli.Command) ! {
release_type := get_release_type(cmd)
find_latest_asset(release_type) or {
println('Prebuild v-analyzer binary is not available for your platform')
return
}
println('${term.green('✓')} Prebuild v-analyzer binary is available for your platform')
}
})
cmd.add_command(cli.Command{
name: 'check-updates'
description: 'Checks for v-analyzer updates.'
posix_mode: true
execute: fn (cmd cli.Command) ! {
release_type := get_release_type(cmd)
check_updates(release_type)!
}
})
cmd.parse(os.args)
}
================================================
FILE: src/analyzer/Indexer.v
================================================
module analyzer
import os
import time
import loglib
import analyzer.index
// IndexingRootsStatus describes the indexing status of all roots.
pub enum IndexingRootsStatus {
all_indexed
needs_ensure_indexed // when at least one of the indexes was taken from the cache
}
// Indexer encapsulates the indexing logic and provides an interface for working with the index.
pub struct Indexer {
pub mut:
roots []&index.IndexingRoot
no_save bool
}
pub fn new_indexer() &Indexer {
return &Indexer{}
}
pub fn (mut i Indexer) set_no_save(value bool) {
i.no_save = value
for mut root in i.roots {
root.no_save = value
}
}
pub fn (i Indexer) count_roots() int {
return i.roots.len
}
pub fn (mut i Indexer) add_indexing_root(root string, kind index.IndexingRootKind, cache_dir string) {
loglib.with_fields({
'root': root
}).info('Adding indexing root')
i.roots << index.new_indexing_root(root, kind, cache_dir)
}
pub fn (mut i Indexer) index(on_start fn (root index.IndexingRoot, index int)) IndexingRootsStatus {
now := time.now()
loglib.info('Indexing ${i.roots.len} roots')
mut need_ensure_indexed := false
for index, mut indexing_root in i.roots {
on_start(*indexing_root, index + 1)
status := indexing_root.index()
if status == .from_cache {
// If at least one of the indexes was taken from the cache,
// then we need to make sure that all indexes are up to date.
need_ensure_indexed = true
}
}
loglib.with_duration(time.since(now)).info('Indexing all roots')
return if need_ensure_indexed {
.needs_ensure_indexed
} else {
.all_indexed
}
}
pub fn (mut i Indexer) ensure_indexed() {
now := time.now()
loglib.info('Ensure indexed of ${i.roots.len} roots')
for mut indexing_root in i.roots {
indexing_root.ensure_indexed()
}
loglib.with_duration(time.since(now)).info('Ensure indexed of all roots')
}
pub fn (mut i Indexer) save_indexes() ! {
if i.no_save {
return
}
for mut indexing_root in i.roots {
indexing_root.save_index() or {
loglib.with_fields({
'root': indexing_root.root
'err': err.str()
}).error('Failed to save index')
return err
}
}
}
pub fn (mut i Indexer) mark_as_dirty(filepath string, new_content string) ! {
for mut indexing_root in i.roots {
indexing_root.mark_as_dirty(filepath, new_content)!
}
}
pub fn (mut i Indexer) add_file(path string) ?index.FileIndex {
content := os.read_file(path) or {
loglib.with_fields({
'path': path
'err': err.str()
}).error('Failed to read new file')
return none
}
for mut root in i.roots {
if root.contains(path) {
return root.add_file(path, content) or {
loglib.with_fields({
'root': root.root
'path': path
'err': err.str()
}).error('Failed to add new file')
return none
}
}
}
return none
}
pub fn (mut i Indexer) rename_file(old_path string, new_path string) ?index.FileIndex {
for mut root in i.roots {
if root.contains(old_path) {
return root.rename_file(old_path, new_path) or {
loglib.with_fields({
'root': root.root
'old_path': old_path
'new_path': new_path
'err': err.str()
}).error('Failed to rename file')
return none
}
}
}
return none
}
pub fn (mut i Indexer) remove_file(path string) ?index.FileIndex {
for mut root in i.roots {
if root.contains(path) {
return root.remove_file(path) or {
loglib.with_fields({
'root': root.root
'path': path
'err': err.str()
}).error('Failed to remove file')
return none
}
}
}
return none
}
================================================
FILE: src/analyzer/IndexingManager.v
================================================
module analyzer
import analyzer.psi
pub struct IndexingManager {
pub mut:
indexer &Indexer = unsafe { nil }
stub_index psi.StubIndex
}
pub fn IndexingManager.new() &IndexingManager {
indexer := new_indexer()
return &IndexingManager{
indexer: indexer
}
}
pub fn (mut a IndexingManager) setup_empty_indexes() {
a.stub_index = psi.new_stubs_index([])
stubs_index = a.stub_index
}
pub fn (mut a IndexingManager) setup_stub_indexes() {
mut sinks := a.all_sinks()
a.stub_index = psi.new_stubs_index(sinks)
stubs_index = a.stub_index
}
pub fn (mut a IndexingManager) update_stub_indexes_from_sinks(changed_sinks []psi.StubIndexSink) {
all_sinks := a.all_sinks()
stubs_index.update_stubs_index(changed_sinks, all_sinks)
}
pub fn (mut a IndexingManager) update_stub_indexes(changed_files []&psi.PsiFile) {
all_sinks := a.all_sinks()
mut changed_sinks := []psi.StubIndexSink{cap: changed_files.len}
for root in a.indexer.roots {
for file in changed_files {
file_cache := root.index.per_file.data[file.path] or { continue }
changed_sinks << file_cache.sink
}
}
stubs_index.update_stubs_index(changed_sinks, all_sinks)
}
fn (mut a IndexingManager) all_sinks() []psi.StubIndexSink {
mut sinks := []psi.StubIndexSink{cap: a.indexer.roots.len * 30}
for root in a.indexer.roots {
sinks << root.index.per_file.get_sinks()
}
return sinks
}
================================================
FILE: src/analyzer/OpenedFile.v
================================================
module analyzer
import lsp
import utils
import analyzer.psi
pub struct OpenedFile {
pub mut:
uri lsp.DocumentUri
version int
psi_file &psi.PsiFile
}
pub fn (f OpenedFile) find_offset(pos lsp.Position) u32 {
return u32(utils.compute_offset(f.psi_file.text(), pos.line, pos.character))
}
================================================
FILE: src/analyzer/README.md
================================================
# Description
`analyzer` module describes all the functionality related to code analysis.
`server` module uses the `analyzer` module to implement all the features related to code
analysis.
================================================
FILE: src/analyzer/index/FileIndex.v
================================================
module index
import analyzer.psi
// FileIndex describes the cache of a single file.
// By splitting the cache into files, we can index files in parallel
// without the need for synchronization.
@[heap]
pub struct FileIndex {
pub mut:
kind IndexingRootKind // root where the file is located
// file_last_modified stores the time the file was last modified
//
// Thanks to it, while checking the cache, we can understand whether the
// file has been changed or not.
// If the file has been modified, then we reindex the file.
file_last_modified i64
// stub_list is a list of all stubs in the file.
// Storing stubs as a table makes it easy and compact to save them to disk and load them back.
stub_list &psi.StubList = unsafe { nil }
// sink describes the indexed stubs of the current file.
// So, for example, by the '.functions' key, you can get the stubs of all functions defined inside the current file.
// See also 'StubIndexKey'.
sink &psi.StubIndexSink = unsafe { nil }
}
pub fn (f &FileIndex) path() string {
if f.stub_list == unsafe { nil } {
return ''
}
return f.stub_list.path
}
================================================
FILE: src/analyzer/index/Index.v
================================================
module index
import time
// IndexNotFoundError is returned if the index is not found.
pub struct IndexNotFoundError {
Error
}
// NeedReindexedError is returned if the index needs to be rebuilt.
pub struct NeedReindexedError {
Error
}
// IndexVersionMismatchError is returned if the index version does not match the latest.
pub struct IndexVersionMismatchError {
Error
}
// Index encapsulates the index storage logic.
pub struct Index {
pub:
version string = '33'
pub mut:
updated_at time.Time // time of last index update
per_file PerFileIndex
}
// decode encapsulates the index decoding logic.
// If the index was corrupted and could not be decoded, an error is returned.
// If the index version does not match the latest, an `IndexVersionMismatchError` is returned.
pub fn (mut i Index) decode(data []u8) ! {
mut d := new_index_deserializer(data)
index := d.deserialize_index(i.version)!
i.per_file = index.per_file
}
// encode encapsulates the logic for encoding an index.
pub fn (i &Index) encode() []u8 {
mut s := IndexSerializer{}
s.serialize_index(i)
return s.s.data
}
================================================
FILE: src/analyzer/index/IndexDeserializer.v
================================================
module index
import analyzer.psi
import bytes
import time
pub struct IndexDeserializer {
mut:
d bytes.Deserializer
}
pub fn new_index_deserializer(data []u8) IndexDeserializer {
return IndexDeserializer{
d: bytes.new_deserializer(data)
}
}
pub fn (mut d IndexDeserializer) deserialize_index(expected_version string) !Index {
version := d.d.read_string()
if version != expected_version {
// Due to the fact that the structure of the index can change, we cannot simply
// restore the index if the version does not match, therefore, if there is a mismatch,
// we stop the decoding of the index immediately.
return IndexVersionMismatchError{}
}
updated_at_unix := d.d.read_i64()
file_indexes := d.deserialize_file_indexes()
return Index{
version: version
updated_at: time.unix(updated_at_unix)
per_file: PerFileIndex{
data: file_indexes
}
}
}
pub fn (mut d IndexDeserializer) deserialize_file_indexes() map[string]FileIndex {
len := d.d.read_int()
mut file_indexes := map[string]FileIndex{}
for _ in 0 .. len {
file_index := d.deserialize_file_index()
file_indexes[file_index.path()] = file_index
}
return file_indexes
}
pub fn (mut d IndexDeserializer) deserialize_file_index() FileIndex {
kind := unsafe { IndexingRootKind(d.d.read_u8()) }
file_last_modified := d.d.read_i64()
stub_list := d.deserialize_stub_list()
stub_index_sink := d.deserialize_stub_index_sink(stub_list, kind)
return FileIndex{
kind: kind
file_last_modified: file_last_modified
stub_list: stub_list
sink: stub_index_sink
}
}
pub fn (mut d IndexDeserializer) deserialize_stub_index_sink(stub_list &psi.StubList, kind IndexingRootKind) &psi.StubIndexSink {
len := d.d.read_int()
mut sink := &psi.StubIndexSink{
stub_list: stub_list
kind: unsafe { psi.StubIndexLocationKind(u8(kind)) }
}
for _ in 0 .. len {
key := d.d.read_int()
mut sink_map := d.deserialize_stub_index_sink_map()
sink.data[key] = sink_map.move()
}
count_imported_modules := d.d.read_int()
mut imported_modules := []string{cap: count_imported_modules}
for _ in 0 .. count_imported_modules {
imported_modules << d.d.read_string()
}
sink.imported_modules = imported_modules
return sink
}
pub fn (mut d IndexDeserializer) deserialize_stub_index_sink_map() map[string][]psi.StubId {
len := d.d.read_int()
mut sink_map := map[string][]psi.StubId{}
for _ in 0 .. len {
key := d.d.read_string()
stub_ids_len := d.d.read_int()
mut stub_ids := []psi.StubId{cap: stub_ids_len}
for _ in 0 .. stub_ids_len {
stub_ids << d.d.read_int()
}
sink_map[key] = stub_ids
}
return sink_map
}
pub fn (mut d IndexDeserializer) deserialize_stub_list() &psi.StubList {
filepath := d.d.read_string()
module_fqn := d.d.read_string()
mut child_map := map[psi.StubId][]int{}
len := d.d.read_int()
for _ in 0 .. len {
id := d.d.read_int()
children_len := d.d.read_int()
mut children := []int{cap: children_len}
for _ in 0 .. children_len {
children << d.d.read_int()
}
child_map[id] = children
}
stubs_count := d.d.read_int()
mut stubs := []&psi.StubBase{cap: stubs_count}
for _ in 0 .. stubs_count {
stubs << d.deserialize_stub()
}
mut index_map := map[psi.StubId]&psi.StubBase{}
for stub in stubs {
index_map[stub.id] = stub
}
mut list := &psi.StubList{}
list.module_fqn = module_fqn
list.path = filepath
list.index_map = index_map.move()
list.child_map = child_map.move()
for _, mut stub in list.index_map {
stub.stub_list = list
}
return list
}
pub fn (mut d IndexDeserializer) deserialize_stub() &psi.StubBase {
text := d.d.read_string()
comment := d.d.read_string()
receiver := d.d.read_string()
additional := d.d.read_string()
name := d.d.read_string()
identifier_line := d.d.read_int()
identifier_column := d.d.read_int()
identifier_end_line := d.d.read_int()
identifier_end_column := d.d.read_int()
line := d.d.read_int()
column := d.d.read_int()
end_line := d.d.read_int()
end_column := d.d.read_int()
parent_id := d.d.read_int()
stub_type := unsafe { psi.StubType(d.d.read_u8()) }
id := d.d.read_int()
return &psi.StubBase{
text: text
comment: comment
receiver: receiver
additional: additional
name: name
identifier_text_range: psi.TextRange{
line: identifier_line
column: identifier_column
end_line: identifier_end_line
end_column: identifier_end_column
}
text_range: psi.TextRange{
line: line
column: column
end_line: end_line
end_column: end_column
}
parent_id: parent_id
stub_list: unsafe { nil } // will be set later
stub_type: stub_type
id: id
}
}
================================================
FILE: src/analyzer/index/IndexSerializer.v
================================================
module index
import analyzer.psi
import bytes
pub struct IndexSerializer {
mut:
s bytes.Serializer
}
pub fn (mut s IndexSerializer) serialize_index(index Index) {
s.s.write_string(index.version)
s.s.write_i64(index.updated_at.unix())
s.serialize_file_indexes(index.per_file.data)
}
pub fn (mut s IndexSerializer) serialize_file_indexes(indexes map[string]FileIndex) {
s.s.write_int(indexes.len)
for _, index in indexes {
s.serialize_file_index(index)
}
}
pub fn (mut s IndexSerializer) serialize_file_index(index FileIndex) {
s.s.write_u8(u8(index.kind))
s.s.write_i64(index.file_last_modified)
s.serialize_stub_list(index.stub_list)
s.serialize_stub_index_sink(index.sink)
}
pub fn (mut s IndexSerializer) serialize_stub_index_sink(sink &psi.StubIndexSink) {
s.s.write_int(sink.data.len)
for key, datum in sink.data {
s.s.write_int(key)
s.serialize_stub_index_sink_map(datum)
}
s.s.write_int(sink.imported_modules.len)
for module_ in sink.imported_modules {
s.s.write_string(module_)
}
}
pub fn (mut s IndexSerializer) serialize_stub_index_sink_map(sink_map map[string][]psi.StubId) {
s.s.write_int(sink_map.len)
for key, stub_ids in sink_map {
s.s.write_string(key)
s.s.write_int(stub_ids.len)
for id in stub_ids {
s.s.write_int(id)
}
}
}
pub fn (mut s IndexSerializer) serialize_stub_list(list psi.StubList) {
s.s.write_string(list.path)
s.s.write_string(list.module_fqn)
s.s.write_int(list.child_map.len)
for id, children in list.child_map {
s.s.write_int(id)
s.s.write_int(children.len)
for child in children {
s.s.write_int(child)
}
}
// serialize stubs as array
s.s.write_int(list.index_map.len)
for stub in list.index_map.values() {
s.serialize_stub(stub)
}
}
pub fn (mut s IndexSerializer) serialize_stub(stub psi.StubBase) {
s.s.write_string(stub.text)
s.s.write_string(stub.comment)
s.s.write_string(stub.receiver)
s.s.write_string(stub.additional)
s.s.write_string(stub.name)
s.s.write_int(stub.identifier_text_range.line)
s.s.write_int(stub.identifier_text_range.column)
s.s.write_int(stub.identifier_text_range.end_line)
s.s.write_int(stub.identifier_text_range.end_column)
s.s.write_int(stub.text_range.line)
s.s.write_int(stub.text_range.column)
s.s.write_int(stub.text_range.end_line)
s.s.write_int(stub.text_range.end_column)
s.s.write_int(stub.parent_id)
s.s.write_u8(u8(stub.stub_type))
s.s.write_int(stub.id)
}
================================================
FILE: src/analyzer/index/IndexingRoot.v
================================================
module index
import time
import os
import sync
import runtime
import math
import loglib
import lsp
import crypto.md5
import analyzer.psi
import analyzer.parser
// BuiltIndexStatus describes the status of the built index.
pub enum BuiltIndexStatus {
from_cache // index was loaded from cache
from_scratch // index was built from scratch
}
// IndexingRootKind describes the type of root that is being indexed.
// Same as `StubIndexKind`.
pub enum IndexingRootKind as u8 {
standard_library
modules
stubs
workspace
}
pub fn (k IndexingRootKind) readable_name() string {
return match k {
.standard_library { 'Standard Library' }
.modules { 'Modules' }
.stubs { 'Stubs' }
.workspace { 'Workspace' }
}
}
// IndexingRoot encapsulates the logic of indexing/reindexing a particular root of the file system.
//
// Separation into separate roots is necessary in order to process the standard library and user code separately.
@[noinit]
pub struct IndexingRoot {
pub:
root string // root that is indexed
kind IndexingRootKind // type of root that is indexed
pub mut:
cache_dir string // path to the directory where the index is stored
updated_at time.Time // when the index was last updated
index Index // index itself
cache_file string // path to the file where the index is stored
need_save bool // whether the index needs to be saved
no_save bool // for tests
}
// new_indexing_root creates a new indexing root with the given root and kind.
pub fn new_indexing_root(root string, kind IndexingRootKind, cache_dir string) &IndexingRoot {
cache_file := 'v_analyzer_index_${md5.hexhash(root)}'
return &IndexingRoot{
root: root
kind: kind
cache_dir: cache_dir
cache_file: cache_file
}
}
fn (mut i IndexingRoot) cache_file() string {
return os.join_path(i.cache_dir, i.cache_file)
}
pub fn (mut i IndexingRoot) load_index() ! {
now := time.now()
if !os.exists(i.cache_file()) {
loglib.with_fields({
'root': i.root
}).info('Index not found, start indexing')
return IndexNotFoundError{}
}
data := os.read_bytes(i.cache_file()) or {
loglib.with_fields({
'file': i.cache_file()
'error': err.str()
}).error('Failed to read index')
return IndexNotFoundError{}
}
i.index.decode(data) or {
if err is IndexVersionMismatchError {
loglib.info('Index version mismatch')
} else {
loglib.with_fields({
'file': i.cache_file()
'error': err.str()
}).error('Error load index')
}
return NeedReindexedError{}
}
loglib.info('Loaded index in ${time.since(now)}')
}
pub fn (mut i IndexingRoot) save_index() ! {
if !i.need_save || i.no_save {
return
}
i.need_save = false
data := i.index.encode()
os.write_file_array(i.cache_file(), data) or {
loglib.with_fields({
'file': i.cache_file()
'error': err.str()
}).error('Failed to write analyzer index file')
return err
}
}
// need_index returns true if the file needs to be indexed.
//
// We deliberately do not index some of test files to speed up the indexing and searching process.
fn (mut _ IndexingRoot) need_index(path string) bool {
if path.ends_with('.vsh') {
return true
}
if !path.ends_with('.v') {
return false
}
return !path.contains('/tests/') && !path.contains('/slow_tests/')
&& !path.contains('/.vmodules/cache/')
&& !path.contains('/builtin/wasm/') // TODO: index this folder too
&& !path.contains('/builtin/js/') // TODO: index this folder too
&& !path.contains('/builtin/linux_bare/') // TODO: index this folder too
&& !path.ends_with('.js.v') && !path.contains('/.git/') && !path.ends_with('_test.v')
}
pub fn (mut i IndexingRoot) index() BuiltIndexStatus {
now := time.now()
loglib.with_fields({
'root': i.root
}).info('Indexing root')
if _ := i.load_index() {
loglib.with_duration(time.since(now)).info('Index loaded from cache')
return .from_cache
}
file_chan := chan string{cap: 1000}
cache_chan := chan FileIndex{cap: 1000}
spawn fn [mut i, file_chan] () {
path := i.root
os.walk(path, fn [mut i, file_chan] (path string) {
if i.need_index(path) {
file_chan <- path
}
})
file_chan.close()
}()
spawn i.spawn_indexing_workers(cache_chan, file_chan)
mut caches := []FileIndex{cap: 100}
for {
cache := <-cache_chan or { break }
caches << cache
}
for cache in caches {
i.index.per_file.data[cache.path()] = cache
}
i.updated_at = time.now()
i.need_save = true
loglib.with_duration(time.since(now)).info('Indexing finished')
return .from_scratch
}
pub fn (mut i IndexingRoot) index_file(path string, content string, mut p parser.Parser) !FileIndex {
last_modified := os.file_last_mod_unix(path)
res := p.parse_code(content)
psi_file := psi.new_psi_file(path, res.tree, content)
module_fqn := psi.module_qualified_name(psi_file, i.root)
mut cache := FileIndex{
kind: i.kind
file_last_modified: last_modified
sink: &psi.StubIndexSink{
kind: unsafe { psi.StubIndexLocationKind(u8(i.kind)) }
stub_list: unsafe { nil }
}
stub_list: unsafe { nil }
}
stub_tree := build_stub_tree(psi_file, i.root)
stub_type := psi.StubbedElementType{}
mut stub_list := stub_tree.root.stub_list
stub_list.module_fqn = module_fqn
stub_list.path = path
cache.sink.imported_modules = stub_tree.get_imported_modules()
stubs := stub_list.index_map.values()
for stub in stubs {
cache.sink.stub_id = stub.id
cache.sink.stub_list = stub.stub_list
stub_type.index_stub(stub, mut cache.sink)
}
cache.stub_list = stub_list
unsafe { res.tree.free() }
return cache
}
pub fn (mut i IndexingRoot) spawn_indexing_workers(cache_chan chan FileIndex, file_chan chan string) {
mut wg := sync.new_waitgroup()
cpus := runtime.nr_cpus()
workers := math.max(cpus - 4, 1)
wg.add(workers)
for j := 0; j < workers; j++ {
spawn fn [file_chan, mut wg, mut i, cache_chan] () {
mut p := parser.Parser.new()
defer { p.free() }
for {
filepath := <-file_chan or { break }
content := os.read_file(filepath) or {
loglib.with_fields({
'uri': lsp.document_uri_from_path(filepath).str()
'error': err.str()
}).error('Error reading file for index')
continue
}
cache_chan <- i.index_file(filepath, content, mut p) or {
loglib.with_fields({
'uri': lsp.document_uri_from_path(filepath).str()
'error': err.str()
}).error('Error indexing file')
continue
}
}
wg.done()
}()
}
wg.wait()
cache_chan.close()
}
// ensure_indexed checks the index for freshness and re-indexes files if they have changed since the last indexing.
pub fn (mut i IndexingRoot) ensure_indexed() {
now := time.now()
loglib.with_fields({
'root': i.root
}).info('Ensuring indexed root')
reindex_files_chan := chan string{cap: 1000}
cache_chan := chan FileIndex{cap: 1000}
spawn fn [reindex_files_chan, mut i] () {
for filepath, datum in i.index.per_file.data {
last_modified := os.file_last_mod_unix(filepath)
if last_modified > datum.file_last_modified {
loglib.with_fields({
'uri': lsp.document_uri_from_path(filepath).str()
}).info('File was modified, reindexing')
i.index.per_file.data.delete(filepath)
reindex_files_chan <- filepath
}
}
reindex_files_chan.close()
}()
spawn i.spawn_indexing_workers(cache_chan, reindex_files_chan)
mut caches := []FileIndex{cap: 100}
for {
cache := <-cache_chan or { break }
caches << cache
}
for cache in caches {
i.index.per_file.data[cache.path()] = cache
}
if caches.len > 0 {
i.index.updated_at = time.now()
i.need_save = true
}
loglib.with_duration(time.since(now)).info('Reindexing finished')
}
pub fn (mut i IndexingRoot) mark_as_dirty(filepath string, new_content string) ! {
if filepath !in i.index.per_file.data {
// file does not belong to this index
return
}
loglib.with_fields({
'uri': lsp.document_uri_from_path(filepath).str()
}).info('Marking document as dirty')
i.index.per_file.data.delete(filepath)
mut p := parser.Parser.new()
defer { p.free() }
res := i.index_file(filepath, new_content, mut p) or {
return error('Error indexing dirty ${filepath}: ${err}')
}
i.index.per_file.data[filepath] = res
i.index.updated_at = time.now()
i.need_save = true
i.save_index() or { return err }
loglib.with_fields({
'uri': lsp.document_uri_from_path(filepath).str()
}).info('Finished reindexing document')
}
pub fn (mut i IndexingRoot) add_file(filepath string, content string) !FileIndex {
loglib.with_fields({
'uri': lsp.document_uri_from_path(filepath).str()
}).info('Adding new document')
mut p := parser.Parser.new()
defer { p.free() }
res := i.index_file(filepath, content, mut p) or {
return error('Error indexing added ${filepath}: ${err}')
}
i.index.per_file.data[filepath] = res
i.index.updated_at = time.now()
i.need_save = true
i.save_index() or { return err }
loglib.with_fields({
'uri': lsp.document_uri_from_path(filepath).str()
}).info('Finished indexing added document')
if isnil(res.sink) {
return error('Sink of added file is nil')
}
return res
}
pub fn (mut i IndexingRoot) rename_file(old string, new string) !FileIndex {
cache := i.index.per_file.rename_file(old, new) or {
return error('cannot find file index after rename, most likely rename was failed')
}
i.need_save = true
i.save_index() or { return err }
if isnil(cache.sink) {
return error('Sink of renamed file is nil')
}
return cache
}
pub fn (mut i IndexingRoot) remove_file(path string) !FileIndex {
cache := i.index.per_file.remove_file(path) or {
return error('cannot find file index after remove, most likely remove was failed')
}
i.need_save = true
i.save_index() or { return err }
if isnil(cache.sink) {
return error('Sink of removed file is nil')
}
return cache
}
pub fn (i &IndexingRoot) contains(path string) bool {
return path.starts_with(i.root)
}
================================================
FILE: src/analyzer/index/IndexingRoot_test.v
================================================
module index
fn test_git_files_do_not_need_indexed() {
mut ir := new_indexing_root('.', .workspace, '/tmp')
assert !ir.need_index('./.git/some_file.v')
}
fn test_v_test_files_do_not_need_indexed() {
mut ir := new_indexing_root('.', .workspace, '/tmp')
assert !ir.need_index('some_file_test.v')
}
================================================
FILE: src/analyzer/index/PerFileIndex.v
================================================
module index
import analyzer.psi
// PerFileIndex describes the cache of a group of files in the index.
pub struct PerFileIndex {
pub mut:
data map[string]FileIndex
}
pub fn (p &PerFileIndex) get_sinks() []psi.StubIndexSink {
mut res := []psi.StubIndexSink{cap: p.data.len}
for _, cache in p.data {
if !isnil(cache.sink) {
res << *cache.sink
}
}
return res
}
pub fn (mut p PerFileIndex) rename_file(old string, new string) ?FileIndex {
if old == new {
return none
}
if mut cache := p.data[old] {
cache.stub_list.path = new
p.data[new] = cache
p.data.delete(old)
return cache
}
return none
}
pub fn (mut p PerFileIndex) remove_file(path string) ?FileIndex {
if mut cache := p.data[path] {
p.data.delete(path)
return cache
}
return none
}
================================================
FILE: src/analyzer/index/README.md
================================================
## Description
`index` module describes index and indexing operations.
================================================
FILE: src/analyzer/index/StubTree.v
================================================
module index
import strings
import loglib
import analyzer.psi
// StubTree represents a tree of stubs for a file.
// This tree, unlike the AST, contains the nodes whose data we want to serialize to
// speed up the startup of the server.
// Such nodes implement the `psi.StubBasedPsiElement` interface.
//
// Unlike AST, `StubTree` trees are quite small, so they can be easily saved and fully loaded
// into RAM without taking up a lot of space.
//
// With the help of `StubTree`, stub indexes are also built, which allow us to quickly find
// the necessary elements in the workspace or standard library.
// See `StubbedElementType.index_stub()`.
pub struct StubTree {
root &psi.StubBase
}
pub fn (tree &StubTree) print() {
mut sb := strings.new_builder(100)
mut p := StubTreePrinter{
sb: &sb
}
p.print_stub(tree.root, 0)
loglib.trace(sb.str())
}
pub fn (tree &StubTree) print_to(mut sb strings.Builder) {
mut p := StubTreePrinter{
sb: unsafe { &sb }
}
p.print_stub(tree.root, 0)
}
pub fn (tree &StubTree) get_imported_modules() []string {
mut result := []string{}
children := tree.root.children_stubs()
for child in children {
if child.stub_type() == .import_list {
declarations := child.children_stubs()
for declaration in declarations {
import_spec := declaration.first_child() or { continue }
import_path := import_spec.first_child() or { continue }
if import_path.stub_type() == .import_path {
result << import_path.text()
}
}
}
}
return result
}
pub fn build_stub_tree(file &psi.PsiFile, indexing_root string) &StubTree {
mut walker := psi.new_tree_walker(file.tree.root_node())
defer { walker.free() }
stub_root := psi.new_root_stub(file.path())
module_fqn := psi.module_qualified_name(file, indexing_root)
if walker.current_node() != none {
build_stub_tree_recurse(mut walker, file, stub_root, module_fqn, false)
}
return &StubTree{
root: stub_root
}
}
fn build_stub_tree_recurse(mut tw psi.TreeWalker, file &psi.PsiFile, parent &psi.StubBase, module_fqn string, build_for_all_children bool) {
node := tw.current_node() or { return }
node_type := node.type_name
stub_type := psi.node_type_to_stub_type(node_type)
is_stubbable := stub_type != .root || psi.node_is_type(node_type)
mut effective_parent := unsafe { parent }
mut should_traverse_children := true
mut pass_down_build_all := false
if is_stubbable || build_for_all_children {
psi_element := psi.create_element(node, file)
element_type := psi.StubbedElementType{}
if stub := element_type.create_stub(psi_element, parent, module_fqn) {
effective_parent = unsafe { stub }
if node_type == .qualified_type {
pass_down_build_all = true
}
}
}
if should_traverse_children {
if tw.to_first_child() {
for {
build_stub_tree_recurse(mut tw, file, effective_parent, module_fqn,
build_for_all_children || pass_down_build_all)
if !tw.next_sibling() {
break
}
}
tw.to_parent()
}
}
}
struct NodeInfo {
node psi.PsiElement
parent &psi.StubBase
}
pub fn build_stub_tree_iterative(file &psi.PsiFile, mut nodes []NodeInfo) &StubTree {
root := file.root()
stub_root := psi.new_root_stub(file.path())
nodes = nodes[..0].clone()
nodes << NodeInfo{
node: root
parent: stub_root
}
element_type := psi.StubbedElementType{}
for nodes.len > 0 {
node := nodes.pop()
this_parent_stub := node.parent
parent_stub := if node.node is psi.StubBasedPsiElement {
if stub := element_type.create_stub(node.node as psi.PsiElement, this_parent_stub, '') {
stub
} else {
this_parent_stub
}
} else {
this_parent_stub
}
for child in node.node.children() {
nodes << NodeInfo{
node: child
parent: parent_stub
}
}
}
return &StubTree{
root: stub_root
}
}
pub struct StubTreePrinter {
mut:
sb &strings.Builder
}
pub fn (mut p StubTreePrinter) print_stub(stub psi.StubElement, indent int) {
for i := 0; i < indent; i++ {
p.sb.write_string(' ')
}
p.sb.write_string(stub.stub_type().str())
text_range := stub.text_range()
p.sb.write_string(' at ')
p.sb.write_string((text_range.line + 1).str())
text := stub.text()
if text.len != 0 {
p.sb.write_string(' ')
p.sb.write_string('"')
p.sb.write_string(text)
p.sb.write_string('"')
}
p.sb.write_string('\n')
for child in stub.children_stubs() {
p.print_stub(child, indent + 1)
}
}
================================================
FILE: src/analyzer/lang/utils.v
================================================
module lang
import analyzer.psi
import analyzer.psi.types
pub fn get_zero_value_for(typ types.Type) string {
return match typ {
types.PrimitiveType {
match typ.name {
'bool' { 'false' }
'rune' { '`0`' }
'char', 'u8' { '0' }
'voidptr', 'byteptr', 'charptr', 'nil' { 'unsafe { nil }' }
'f32', 'f64' { '0.0' }
else { '0' }
}
}
types.StructType {
match typ.name() {
'string' { "''" }
else { typ.readable_name() + '{}' }
}
}
types.ArrayType {
return '[]'
}
types.FixedArrayType {
return '[]!'
}
types.MapType {
return '{}'
}
types.ChannelType {
return 'chan ${typ.inner.readable_name()}{}'
}
types.FunctionType {
return '${typ.readable_name()} {}'
}
types.AliasType {
return get_zero_value_for(typ.inner)
}
types.GenericInstantiationType {
return get_zero_value_for(typ.inner)
}
types.InterfaceType, types.PointerType {
return 'unsafe { nil }'
}
types.OptionType {
return 'none'
}
types.ResultType {
if !typ.no_inner {
return get_zero_value_for(typ.inner)
}
return "error('')"
}
else {
return '0'
}
}
}
pub fn is_same_module(context psi.PsiElement, element psi.PsiElement) bool {
context_file := context.containing_file() or { return false }
element_file := element.containing_file() or { return false }
context_module_fqn := context_file.module_fqn()
element_module_fqn := element_file.module_fqn()
return context_module_fqn == element_module_fqn
}
================================================
FILE: src/analyzer/parser/README.md
================================================
## Description
`parser` module provides way to parse V code to AST.
Input may be provided in a variety of forms (see the various `parser_*` functions)
Output is an abstract syntax tree (AST) representing the V source.
The parser accepts a larger language than is syntactically permitted by the V spec,
for simplicity, and for improved robustness in the presence of syntax errors.
================================================
FILE: src/analyzer/parser/batch.v
================================================
module parser
import sync
pub fn parse_batch_files(files []string, count_workers int) []ParseResult {
effective_workers := if files.len < count_workers {
files.len
} else {
count_workers
}
file_chan := chan string{cap: 1000}
result_chan := chan ParseResult{cap: 1000}
spawn fn [file_chan, files] () {
for file in files {
file_chan <- file
}
file_chan.close()
}()
spawn spawn_parser_workers(result_chan, file_chan, effective_workers)
mut results := []ParseResult{cap: 100}
for {
result := <-result_chan or { break }
results << result
}
return results
}
fn spawn_parser_workers(result_chan chan ParseResult, file_chan chan string, count_workers int) {
mut wg := sync.new_waitgroup()
wg.add(count_workers)
for i := 0; i < count_workers; i++ {
spawn fn [file_chan, mut wg, result_chan] () {
mut p := Parser.new()
defer { p.free() }
for {
filepath := <-file_chan or { break }
mut result := p.parse_file(filepath) or { continue }
result.path = filepath
result_chan <- result
}
wg.done()
}()
}
wg.wait()
result_chan.close()
}
================================================
FILE: src/analyzer/parser/parser.v
================================================
module parser
import tree_sitter_v.bindings
import os
// ParseResult represents the result of a parsing operation.
pub struct ParseResult {
pub:
tree &bindings.Tree[bindings.NodeType] = unsafe { nil } // Resulting tree or nil if the source could not be parsed.
source_text string // Source code.
pub mut:
path string // Path of the file that was parsed.
}
// Source represent the possible types of V source code to parse.
type Source = []u8 | string
// Parser is a wrapper around the Tree-sitter V parser.
pub struct Parser {
mut:
binding_parser &bindings.Parser[bindings.NodeType] = unsafe { nil }
}
// new creates a new Parser instance.
pub fn Parser.new() &Parser {
mut bp := bindings.new_parser[bindings.NodeType](bindings.type_factory)
bp.set_language(bindings.language)
return &Parser{
binding_parser: bp
}
}
// free frees the Tree-sitter parser.
pub fn (p &Parser) free() {
unsafe {
p.binding_parser.free()
}
}
// parse_file parses a V source file and returns the corresponding `tree_sitter.Tree` and `Rope`.
// If the file could not be read, an error is returned.
// If the file was read successfully, but could not be parsed, the result
// is a partially AST.
//
// Example:
// ```
// import parser
//
// fn main() {
// mut p := parser.Parser.new()
// res := p.parse_file('foo.v') or {
// eprintln('Error: could not parse file: ${err}')
// return
// }
// println(res.tree)
// }
// ```
pub fn (mut p Parser) parse_file(filename string) !ParseResult {
content := os.read_file(filename) or { return error('could not read file ${filename}: ${err}') }
mut res := p.parse_source(content)
res.path = filename
return res
}
// parse_source parses a V code and returns the corresponding `tree_sitter.Tree` and `Rope`.
// Unlike `parse_file`, `parse_source` uses the source directly, without reading it from a file.
// See `parser.Source` for the possible types of `source`.
//
// Example:
// ```
// import parser
//
// fn main() {
// mut p := parser.Parser.new()
// res := p.parse_source('fn main() { println("Hello, World!") }') or {
// eprintln('Error: could not parse source: ${err}')
// return
// }
// println(res.tree)
// }
// ```
pub fn (mut p Parser) parse_source(source Source) ParseResult {
code := match source {
string {
source
}
[]u8 {
source.str()
}
}
return p.parse_code(code)
}
// parse_code parses a V code and returns the corresponding `tree_sitter.Tree` and `Rope`.
// Unlike `parse_file` and `parse_source`, `parse_code` don't return an error since
// the source is always valid.
pub fn (mut p Parser) parse_code(code string) ParseResult {
tree := p.binding_parser.parse_string(source: code)
return ParseResult{
tree: tree
source_text: code
}
}
// parse_code_with_tree parses a V code and returns the corresponding `tree_sitter.Tree` and `Rope`.
// This tree can be used to reparse the code with a some changes.
// This is useful for incremental parsing.
//
// Unlike `parse_file` and `parse_source`, `parse_code` don't return an error since
// the source is always valid.
//
// Example:
// ```
// import parser
//
// fn main() {
// mut p := parser.Parser.new()
// code := 'fn main() { println("Hello, World!") }'
// res := p.parse_code_with_tree(code, unsafe { nil })
// println(res.tree)
// // some changes in code
// code2 := 'fn foo() { println("Hello, World!") }'
// res2 = p.parse_code_with_tree(code2, res.tree)
// println(res2.tree
// }
pub fn (mut p Parser) parse_code_with_tree(code string, old_tree &bindings.Tree[bindings.NodeType]) ParseResult {
raw_tree := if isnil(old_tree) { unsafe { nil } } else { old_tree.raw_tree }
tree := p.binding_parser.parse_string(source: code, tree: raw_tree)
return ParseResult{
tree: tree
source_text: code
}
}
================================================
FILE: src/analyzer/psi/Argument.v
================================================
module psi
pub struct Argument {
PsiElementImpl
}
================================================
FILE: src/analyzer/psi/ArrayCreation.v
================================================
module psi
pub struct ArrayCreation {
PsiElementImpl
is_fixed bool
}
pub fn (n ArrayCreation) expressions() []PsiElement {
children := n.children()
return children.filter(it.element_type() != .unknown)
}
================================================
FILE: src/analyzer/psi/AstNode.v
================================================
module psi
import tree_sitter_v.bindings
pub fn (node AstNode) parent_of_type(typ bindings.NodeType) ?AstNode {
mut res := node
for {
res = res.parent()?
if res.type_name == typ {
return res
}
}
return none
}
================================================
FILE: src/analyzer/psi/Attribute.v
================================================
module psi
pub struct Attribute {
PsiElementImpl
}
fn (_ &Attribute) stub() {}
pub fn (n Attribute) expressions() []src.analyzer.psi.PsiElement {
if stub := n.get_stub() {
return stub.get_children_by_type(.attribute_expression).get_psi()
}
return n.find_children_by_type(.attribute_expression)
}
pub fn (n Attribute) keys() []string {
expressions := n.expressions()
if expressions.len == 0 {
return []
}
return expressions.map(fn (expr PsiElement) string {
if expr is AttributeExpression {
return expr.value()
}
return ''
}).filter(it != '')
}
================================================
FILE: src/analyzer/psi/AttributeExpression.v
================================================
module psi
pub struct AttributeExpression {
PsiElementImpl
}
pub fn (n &AttributeExpression) value() string {
if stub := n.get_stub() {
if first_child := stub.first_child() {
return first_child.text()
}
return ''
}
if first_child := n.first_child() {
if first_child is ValueAttribute {
return first_child.value()
}
}
return ''
}
fn (_ &AttributeExpression) stub() {}
================================================
FILE: src/analyzer/psi/Attributes.v
================================================
module psi
pub struct Attributes {
PsiElementImpl
}
fn (_ &Attributes) stub() {}
pub fn (n Attributes) attributes() []PsiElement {
return n.find_children_by_type_or_stub(.attribute)
}
================================================
FILE: src/analyzer/psi/AttributesOwner.v
================================================
module psi
pub interface AttributesOwner {
attributes() []PsiElement
}
================================================
FILE: src/analyzer/psi/BinaryExpression.v
================================================
module psi
pub struct BinaryExpression {
PsiElementImpl
}
pub fn (n BinaryExpression) operator() string {
operator_element := n.find_child_by_name('operator') or { return '' }
return operator_element.get_text()
}
pub fn (n BinaryExpression) left() ?PsiElement {
return n.find_child_by_name('left')
}
pub fn (n BinaryExpression) right() ?PsiElement {
return n.find_child_by_name('right')
}
================================================
FILE: src/analyzer/psi/Block.v
================================================
module psi
pub struct Block {
PsiElementImpl
}
pub fn (b Block) last_expression() ?PsiElement {
statements := b.find_children_by_type(.simple_statement)
if statements.len == 0 {
return none
}
return statements.last().first_child()
}
pub fn (b Block) process_declarations(mut processor PsiScopeProcessor, last_parent PsiElement) bool {
statements := b.find_children_by_type(.simple_statement)
for statement in statements {
if statement.is_equal(last_parent) {
return true
}
first_child := statement.first_child() or { continue }
if first_child is VarDeclaration {
for var in first_child.vars() {
if !processor.execute(var) {
return false
}
}
}
}
return true
}
================================================
FILE: src/analyzer/psi/CallExpression.v
================================================
module psi
import analyzer.psi.types
pub struct CallExpression {
PsiElementImpl
}
fn (c &CallExpression) get_type() types.Type {
return infer_type(c)
}
fn (c &CallExpression) caller_type() types.Type {
ref_expression := c.ref_expression() or { return types.unknown_type }
if qualifier := ref_expression.qualifier() {
return infer_type(qualifier)
}
return types.unknown_type
}
pub fn (c CallExpression) expression() ?PsiElement {
return c.first_child()
}
pub fn (c CallExpression) ref_expression() ?ReferenceExpressionBase {
if selector_expr := c.find_child_by_type(.selector_expression) {
if selector_expr is ReferenceExpressionBase {
return selector_expr
}
} else if ref_expr := c.find_child_by_type(.reference_expression) {
if ref_expr is ReferenceExpressionBase {
return ref_expr
}
}
return none
}
pub fn (c CallExpression) resolve() ?PsiElement {
expr := c.ref_expression()?
if expr is ReferenceExpressionBase {
resolved := expr.resolve()?
return resolved
}
return none
}
pub fn (c CallExpression) parameter_index_on_offset(offset u32) int {
argument_list := c.find_child_by_type(.argument_list) or { return -1 }
commas := argument_list.children().filter(it.get_text() == ',')
count_commas_before := commas.filter(it.node().start_byte() < offset).len
return count_commas_before
}
pub fn (c CallExpression) arguments() []PsiElement {
argument_list := c.find_child_by_type(.argument_list) or { return [] }
arguments := argument_list.find_children_by_type(.argument)
mut exprs := []PsiElement{cap: arguments.len}
for argument in arguments {
exprs << argument.first_child() or { continue }
}
return exprs
}
pub fn (c CallExpression) is_json_decode() bool {
return c.has_child_of_type(.special_argument_list)
}
pub fn (c &CallExpression) get_json_decode_type() types.Type {
list := c.find_child_by_type(.special_argument_list) or { return types.unknown_type }
typ := list.find_child_by_type(.plain_type) or { return types.unknown_type }
mut visited := map[string]types.Type{}
return TypeInferer{}.convert_type(typ, mut visited)
}
fn (c &CallExpression) type_arguments() ?&GenericTypeArguments {
type_parameters := c.find_child_by_name('type_parameters')?
if type_parameters is GenericTypeArguments {
return type_parameters
}
return none
}
================================================
FILE: src/analyzer/psi/Comment.v
================================================
module psi
pub struct LineComment {
PsiElementImpl
}
pub struct BlockComment {
PsiElementImpl
}
================================================
FILE: src/analyzer/psi/CompileTimeIfExpression.v
================================================
module psi
pub struct CompileTimeIfExpression {
PsiElementImpl
}
pub fn (n CompileTimeIfExpression) block() ?&Block {
block := n.find_child_by_type(.block)?
if block is Block {
return block
}
return none
}
pub fn (n CompileTimeIfExpression) else_branch() ?PsiElement {
return n.find_child_by_type(.else_branch)?.last_child()
}
================================================
FILE: src/analyzer/psi/ConstantDeclaration.v
================================================
module psi
pub struct ConstantDeclaration {
PsiElementImpl
}
pub fn (n ConstantDeclaration) constants() []PsiElement {
return n.find_children_by_type(.const_definition)
}
================================================
FILE: src/analyzer/psi/ConstantDefinition.v
================================================
module psi
import analyzer.parser
import analyzer.psi.types
pub struct ConstantDefinition {
PsiElementImpl
}
pub fn (c &ConstantDefinition) is_public() bool {
modifiers := c.visibility_modifiers() or { return false }
return modifiers.is_public()
}
pub fn (c &ConstantDefinition) get_type() types.Type {
expr := c.expression() or { return types.unknown_type }
res := infer_type(expr)
if c.stub_based() {
if mut file := expr.containing_file() {
file.free()
}
}
return res
}
fn (c &ConstantDefinition) identifier() ?PsiElement {
return c.find_child_by_type(.identifier)
}
pub fn (c ConstantDefinition) identifier_text_range() TextRange {
if stub := c.get_stub() {
return stub.identifier_text_range
}
identifier := c.identifier() or { return TextRange{} }
return identifier.text_range()
}
pub fn (c ConstantDefinition) name() string {
if stub := c.get_stub() {
return stub.name
}
identifier := c.identifier() or { return '' }
return identifier.get_text()
}
pub fn (c ConstantDefinition) doc_comment() string {
if stub := c.get_stub() {
return stub.comment
}
parent := c.parent() or { return '' }
return extract_doc_comment(parent)
}
pub fn (c ConstantDefinition) visibility_modifiers() ?&VisibilityModifiers {
if c.stub_based() {
modifiers := c.prev_sibling_of_type(.visibility_modifiers)?
if modifiers is VisibilityModifiers {
return modifiers
}
return none
}
decl := c.parent()?
modifiers := decl.find_child_by_type_or_stub(.visibility_modifiers)?
if modifiers is VisibilityModifiers {
return modifiers
}
return none
}
pub fn (c &ConstantDefinition) expression() ?PsiElement {
if stub := c.get_stub() {
file := c.containing_file() or { return none }
// pretty hacky but it works
mut p := parser.Parser.new()
defer { p.free() }
res := p.parse_code(stub.additional)
root := res.tree.root_node()
first_child := root.first_child()?
next_first_child := first_child.first_child()?
synthetic_file := new_psi_file(file.path, res.tree, res.source_text)
return create_element(AstNode(next_first_child), synthetic_file)
}
return c.last_child()
}
pub fn (_ ConstantDefinition) stub() {}
================================================
FILE: src/analyzer/psi/EmbeddedDefinition.v
================================================
module psi
import analyzer.psi.types
pub struct EmbeddedDefinition {
PsiElementImpl
}
pub fn (n &EmbeddedDefinition) owner() ?PsiElement {
if struct_ := n.parent_of_type(.struct_declaration) {
return struct_
}
return n.parent_of_type(.in
gitextract_69osocjt/ ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug-report.yml │ │ ├── config.yml │ │ └── feature-request.yml │ └── workflows/ │ ├── analyzer_tests.yml │ ├── build_ci.yml │ ├── install_ci.yml │ ├── lint.yml │ ├── release.yml │ ├── tree_sitter_v.yml │ ├── version_test.vv │ └── vscode_extension_tests.yml ├── .gitignore ├── .gitmodules ├── .v-analyzer/ │ └── config.toml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── build.vsh ├── editors/ │ └── code/ │ ├── .editorconfig │ ├── .eslintignore │ ├── .eslintrc.json │ ├── .gitignore │ ├── .vscodeignore │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── docs/ │ │ └── troubleshooting.md │ ├── languages/ │ │ ├── v-language-configuration.json │ │ └── vmod-language-configuration.json │ ├── media/ │ │ └── welcome.css │ ├── package.json │ ├── scripts/ │ │ ├── build.js │ │ └── minify_json.js │ ├── src/ │ │ ├── bootstrap.ts │ │ ├── client.ts │ │ ├── commands.ts │ │ ├── ctx.ts │ │ ├── exec.ts │ │ ├── extension.ts │ │ ├── log.ts │ │ ├── lsp_ext.ts │ │ ├── stateUtils.ts │ │ ├── tcp.ts │ │ ├── utils.ts │ │ └── welcome.ts │ ├── syntaxes/ │ │ ├── stree.tmGrammar.json │ │ ├── tests/ │ │ │ ├── accessor.vv │ │ │ ├── comma.vv │ │ │ ├── comment.vv │ │ │ ├── comparison.vv │ │ │ ├── dot.int.vv │ │ │ ├── escape.vv │ │ │ ├── hashtag.vv │ │ │ ├── method.vv │ │ │ ├── numbers.vv │ │ │ ├── optional.vv │ │ │ ├── pubfn.vv │ │ │ ├── string.vv │ │ │ ├── type.vv │ │ │ └── variable.vv │ │ ├── v.mod.tmLanguage.json │ │ └── v.tmLanguage.json │ └── tsconfig.json ├── install.vsh ├── src/ │ ├── analyzer/ │ │ ├── Indexer.v │ │ ├── IndexingManager.v │ │ ├── OpenedFile.v │ │ ├── README.md │ │ ├── index/ │ │ │ ├── FileIndex.v │ │ │ ├── Index.v │ │ │ ├── IndexDeserializer.v │ │ │ ├── IndexSerializer.v │ │ │ ├── IndexingRoot.v │ │ │ ├── IndexingRoot_test.v │ │ │ ├── PerFileIndex.v │ │ │ ├── README.md │ │ │ └── StubTree.v │ │ ├── lang/ │ │ │ └── utils.v │ │ ├── parser/ │ │ │ ├── README.md │ │ │ ├── batch.v │ │ │ └── parser.v │ │ └── psi/ │ │ ├── Argument.v │ │ ├── ArrayCreation.v │ │ ├── AstNode.v │ │ ├── Attribute.v │ │ ├── AttributeExpression.v │ │ ├── Attributes.v │ │ ├── AttributesOwner.v │ │ ├── BinaryExpression.v │ │ ├── Block.v │ │ ├── CallExpression.v │ │ ├── Comment.v │ │ ├── CompileTimeIfExpression.v │ │ ├── ConstantDeclaration.v │ │ ├── ConstantDefinition.v │ │ ├── EmbeddedDefinition.v │ │ ├── EnumDeclaration.v │ │ ├── EnumFieldDeclaration.v │ │ ├── FieldDeclaration.v │ │ ├── FieldName.v │ │ ├── ForStatement.v │ │ ├── FunctionLiteral.v │ │ ├── FunctionOrMethodDeclaration.v │ │ ├── GenericArgumentsOwner.v │ │ ├── GenericParameter.v │ │ ├── GenericParameters.v │ │ ├── GenericParametersOwner.v │ │ ├── GenericTypeArguments.v │ │ ├── GenericTypeInferer.v │ │ ├── GenericTypeReifier.v │ │ ├── GlobalVarDefinition.v │ │ ├── Identifier.v │ │ ├── IfExpression.v │ │ ├── ImportAlias.v │ │ ├── ImportDeclaration.v │ │ ├── ImportList.v │ │ ├── ImportName.v │ │ ├── ImportPath.v │ │ ├── ImportSpec.v │ │ ├── IndexExpression.v │ │ ├── InterfaceDeclaration.v │ │ ├── InterfaceMethodDeclaration.v │ │ ├── KeyedElement.v │ │ ├── Literal.v │ │ ├── MapInitExpression.v │ │ ├── MapKeyedElement.v │ │ ├── MatchExpression.v │ │ ├── ModuleClause.v │ │ ├── MutExpression.v │ │ ├── MutabilityModifiers.v │ │ ├── MutabilityOwner.v │ │ ├── OptionPropagationExpression.v │ │ ├── OrBlockExpression.v │ │ ├── ParameterDeclaration.v │ │ ├── ParameterList.v │ │ ├── PlainType.v │ │ ├── Position.v │ │ ├── PrinterVisitor.v │ │ ├── PsiDocCommentOwner.v │ │ ├── PsiElement.v │ │ ├── PsiElementImpl.v │ │ ├── PsiElementVisitor.v │ │ ├── PsiFile.v │ │ ├── PsiNamedElement.v │ │ ├── PsiReference.v │ │ ├── PsiScopeProcessor.v │ │ ├── PsiTreeWalker.v │ │ ├── PsiTypedElement.v │ │ ├── QualifiedType.v │ │ ├── README.md │ │ ├── Range.v │ │ ├── Receiver.v │ │ ├── RecursiveVisitor.v │ │ ├── ReferenceExpression.v │ │ ├── ReferenceExpressionBase.v │ │ ├── ReferenceImpl.v │ │ ├── ResolveCache.v │ │ ├── ResultPropagationExpression.v │ │ ├── SelectiveImportList.v │ │ ├── SelectorExpression.v │ │ ├── Signature.v │ │ ├── SignatureOwner.v │ │ ├── SliceExpression.v │ │ ├── SourceFile.v │ │ ├── StaticMethodDeclaration.v │ │ ├── StaticReceiver.v │ │ ├── StringLiteral.v │ │ ├── StructDeclaration.v │ │ ├── StructFieldScope.v │ │ ├── StubBase.v │ │ ├── StubBasedPsiElement.v │ │ ├── StubElement.v │ │ ├── StubIndex.v │ │ ├── StubIndexSink.v │ │ ├── StubList.v │ │ ├── StubbedElementTypeImpl.v │ │ ├── TextRange.v │ │ ├── TreeWalker.v │ │ ├── TypeAliasDeclaration.v │ │ ├── TypeCache.v │ │ ├── TypeInferer.v │ │ ├── TypeInitializer.v │ │ ├── TypeReferenceExpression.v │ │ ├── UnaryExpression.v │ │ ├── UnsafeExpression.v │ │ ├── ValueAttribute.v │ │ ├── VarDeclaration.v │ │ ├── VarDefinition.v │ │ ├── VisibilityModifiers.v │ │ ├── doc_comment_extractor.v │ │ ├── element_factory.v │ │ ├── search/ │ │ │ ├── ReferencesSearch.v │ │ │ ├── common.v │ │ │ ├── implementations.v │ │ │ ├── implmenttion_methods.v │ │ │ ├── super_methods.v │ │ │ └── supers.v │ │ ├── types/ │ │ │ ├── AliasType.v │ │ │ ├── ArrayType.v │ │ │ ├── BaseNamedType.v │ │ │ ├── BaseType.v │ │ │ ├── ChannelType.v │ │ │ ├── EnumType.v │ │ │ ├── FixedArrayType.v │ │ │ ├── FunctionType.v │ │ │ ├── GenericInstantiationType.v │ │ │ ├── GenericType.v │ │ │ ├── InterfaceType.v │ │ │ ├── MapType.v │ │ │ ├── MultiReturnType.v │ │ │ ├── OptionType.v │ │ │ ├── PointerType.v │ │ │ ├── PrimitiveType.v │ │ │ ├── ResultType.v │ │ │ ├── StructType.v │ │ │ ├── ThreadType.v │ │ │ ├── Type.v │ │ │ ├── TypeVisitor.v │ │ │ ├── UnknownType.v │ │ │ ├── VoidPtrType.v │ │ │ └── helpers.v │ │ ├── types_util.v │ │ ├── utils.v │ │ └── walk.v │ ├── bytes/ │ │ ├── README.md │ │ ├── deserialize.v │ │ ├── serialization_test.v │ │ └── serialize.v │ ├── check-updates.v │ ├── clear-cache.v │ ├── config/ │ │ ├── EditorConfig.v │ │ └── constants.v │ ├── init.v │ ├── jsonrpc/ │ │ ├── README.md │ │ ├── jsonrpc.v │ │ ├── jsonrpc_test.v │ │ ├── server.v │ │ ├── server_test.v │ │ └── server_test_utils/ │ │ └── server_test_utils.v │ ├── loglib/ │ │ ├── ColorMode.v │ │ ├── Entry.v │ │ ├── Formatter.v │ │ ├── LogLevel.v │ │ ├── Logger.v │ │ ├── TextFormatter.v │ │ ├── log.v │ │ └── utils.v │ ├── lsp/ │ │ ├── README.md │ │ ├── capabilities.v │ │ ├── client.v │ │ ├── code_action.v │ │ ├── code_lens.v │ │ ├── color_presentation.v │ │ ├── completion.v │ │ ├── declaration.v │ │ ├── definition.v │ │ ├── diagnostics.v │ │ ├── document_color.v │ │ ├── document_highlight.v │ │ ├── document_link.v │ │ ├── document_symbol.v │ │ ├── ext.v │ │ ├── file_resource.v │ │ ├── folding_range.v │ │ ├── formatting.v │ │ ├── hover.v │ │ ├── implementation.v │ │ ├── initialize.v │ │ ├── inlay_hint.v │ │ ├── log/ │ │ │ ├── log.v │ │ │ └── log_test.v │ │ ├── lsp.v │ │ ├── lsp_test.v │ │ ├── progress.v │ │ ├── references.v │ │ ├── rename.v │ │ ├── semantic_tokens.v │ │ ├── signature_help.v │ │ ├── symbol.v │ │ ├── text_document.v │ │ ├── text_sync.v │ │ ├── window.v │ │ └── workspace.v │ ├── main.v │ ├── metadata/ │ │ ├── metadata.v │ │ └── stubs/ │ │ ├── README.md │ │ ├── arrays.v │ │ ├── attributes/ │ │ │ ├── Attribute.v │ │ │ ├── Deprecated.v │ │ │ ├── DeprecatedAfter.v │ │ │ ├── Flag.v │ │ │ ├── Heap.v │ │ │ ├── Json.v │ │ │ ├── JsonAsNumber.v │ │ │ ├── Manualfree.v │ │ │ ├── Noinit.v │ │ │ ├── Noreturn.v │ │ │ ├── Omitempty.v │ │ │ ├── Table.v │ │ │ └── Unsafe.v │ │ ├── builtin_compile_time.v │ │ ├── c_decl.v │ │ ├── channels.v │ │ ├── compile_time.v │ │ ├── compile_time_constants.v │ │ ├── compile_time_reflection.v │ │ ├── errors.v │ │ ├── implicit.v │ │ ├── primitives.v │ │ ├── threads.v │ │ └── vweb.v │ ├── project/ │ │ ├── flavors/ │ │ │ ├── MacToolchainFlavor.v │ │ │ ├── SymlinkToolchainFlavor.v │ │ │ ├── SysPathToolchainFlavor.v │ │ │ ├── ToolchainFlavor.v │ │ │ ├── UserHomeToolchainFlavor.v │ │ │ ├── VenvToolchainFlavor.v │ │ │ └── WinToolchainFlavor.v │ │ └── project.v │ ├── server/ │ │ ├── BackgroundThread.v │ │ ├── README.md │ │ ├── ResponseWriter.v │ │ ├── code_lens/ │ │ │ └── CodeLensVisitor.v │ │ ├── completion/ │ │ │ ├── CompletionContext.v │ │ │ ├── CompletionProvider.v │ │ │ ├── CompletionResultSet.v │ │ │ └── providers/ │ │ │ ├── AssertCompletionProvider.v │ │ │ ├── AttributesCompletionProvider.v │ │ │ ├── CompileTimeConstantCompletionProvider.v │ │ │ ├── FunctionLikeCompletionProvider.v │ │ │ ├── ImportsCompletionProvider.v │ │ │ ├── InitsCompletionProvider.v │ │ │ ├── JsonAttributeCompletionProvider.v │ │ │ ├── KeywordsCompletionProvider.v │ │ │ ├── LoopKeywordsCompletionProvider.v │ │ │ ├── ModuleNameCompletionProvider.v │ │ │ ├── ModulesImportProvider.v │ │ │ ├── NilKeywordCompletionProvider.v │ │ │ ├── OrBlockExpressionCompletionProvider.v │ │ │ ├── PureBlockExpressionCompletionProvider.v │ │ │ ├── PureBlockStatementCompletionProvider.v │ │ │ ├── ReferenceCompletionProcessor.v │ │ │ ├── ReferenceCompletionProvider.v │ │ │ ├── ReturnCompletionProvider.v │ │ │ ├── StructLiteralCompletion.v │ │ │ └── TopLevelCompletionProvider.v │ │ ├── diagnostics.v │ │ ├── documentation/ │ │ │ ├── keywords_provider.v │ │ │ └── provider.v │ │ ├── features_code_actions.v │ │ ├── features_code_lens.v │ │ ├── features_completion.v │ │ ├── features_definition.v │ │ ├── features_did_change.v │ │ ├── features_did_change_watched_files.v │ │ ├── features_did_close.v │ │ ├── features_did_open.v │ │ ├── features_did_save.v │ │ ├── features_document_highlight.v │ │ ├── features_document_symbol.v │ │ ├── features_execute_command.v │ │ ├── features_folding_range.v │ │ ├── features_formatting.v │ │ ├── features_formatting_test.v │ │ ├── features_hover.v │ │ ├── features_implementation.v │ │ ├── features_inlay_hints.v │ │ ├── features_prepare_rename.v │ │ ├── features_references.v │ │ ├── features_rename.v │ │ ├── features_semantic_tokens.v │ │ ├── features_signature_help.v │ │ ├── features_type_definition.v │ │ ├── features_view_stub_tree.v │ │ ├── features_workspace_symbol.v │ │ ├── file_diff/ │ │ │ └── Diff.v │ │ ├── folding/ │ │ │ └── visitor.v │ │ ├── general.v │ │ ├── hints/ │ │ │ └── InlayHintsVisitor.v │ │ ├── inspections/ │ │ │ ├── Report.v │ │ │ ├── ReportsSource.v │ │ │ └── compiler/ │ │ │ ├── CompilerReportsSource.v │ │ │ └── utils.v │ │ ├── intentions/ │ │ │ ├── AddFlagAttributeIntention.v │ │ │ ├── AddHeapAttributeIntention.v │ │ │ ├── CompilerQuickFix.v │ │ │ ├── ImportModuleQuickFix.v │ │ │ ├── Intention.v │ │ │ ├── MakeMutableQuickFix.v │ │ │ ├── MakePublicIntention.v │ │ │ └── utils.v │ │ ├── language_server.v │ │ ├── progress/ │ │ │ └── progress.v │ │ ├── protocol/ │ │ │ └── Client.v │ │ ├── semantic/ │ │ │ ├── DumbAwareSemanticVisitor.v │ │ │ ├── ResolvingSemanticVisitor.v │ │ │ ├── SemanticToken.v │ │ │ ├── constants.v │ │ │ └── encode.v │ │ ├── setup_test.v │ │ ├── tform/ │ │ │ ├── README.md │ │ │ └── tform.v │ │ └── workspace/ │ │ └── ProjectResolver.v │ ├── streams/ │ │ └── streams.v │ ├── testing/ │ │ ├── Benchmark.v │ │ ├── BenchmarkRunner.v │ │ ├── Test.v │ │ ├── TestFixture.v │ │ ├── Tester.v │ │ └── client/ │ │ └── TestClient.v │ ├── tests/ │ │ ├── analyzer_test.v │ │ ├── bench.v │ │ ├── completion.v │ │ ├── definitions.v │ │ ├── documentation.v │ │ ├── implementations.v │ │ ├── supers.v │ │ ├── testdata/ │ │ │ ├── benchmarks/ │ │ │ │ ├── checker.vv │ │ │ │ └── inlay_hints.vv │ │ │ ├── documentation/ │ │ │ │ ├── rendered.vv │ │ │ │ ├── rendered.vv.md │ │ │ │ ├── stubs.vv │ │ │ │ └── stubs.vv.md │ │ │ └── types/ │ │ │ ├── bool_operators.vv │ │ │ ├── call_expression.vv │ │ │ ├── chan_type.vv │ │ │ ├── constants.vv │ │ │ ├── fields.vv │ │ │ ├── for_loop.vv │ │ │ ├── for_loops.vv │ │ │ ├── function_literal.vv │ │ │ ├── generics.vv │ │ │ ├── if_expression.vv │ │ │ ├── json_decode.vv │ │ │ ├── literals.vv │ │ │ ├── map_init_expression.vv │ │ │ ├── match_expression.vv │ │ │ ├── parameters.vv │ │ │ ├── pointers.vv │ │ │ ├── receiver.vv │ │ │ ├── slice_and_index_expression.vv │ │ │ ├── type_initializer.vv │ │ │ └── unsafe_expression.vv │ │ └── types.v │ ├── tools/ │ │ └── project-checker.v │ ├── up.v │ ├── utils/ │ │ ├── text_utils.v │ │ └── text_utils_test.v │ └── utils.v ├── tree_sitter_v/ │ ├── .gitattributes │ ├── .gitignore │ ├── .prettierrc.js │ ├── LICENSE │ ├── README.md │ ├── bindings/ │ │ ├── bindings.c.v │ │ ├── bindings.h │ │ ├── bindings.v │ │ ├── generate_types.vsh │ │ ├── node_types.v │ │ └── simple_test.v │ ├── examples/ │ │ ├── cursor.v │ │ ├── simple.v │ │ └── with_old_tree.v │ ├── grammar.js │ ├── package.json │ ├── queries/ │ │ ├── helix.highlights.scm │ │ └── highlights.scm │ ├── src/ │ │ ├── grammar.json │ │ ├── node-types.json │ │ ├── parser.c │ │ └── tree_sitter/ │ │ ├── alloc.h │ │ ├── array.h │ │ └── parser.h │ ├── test/ │ │ └── corpus/ │ │ ├── anon_struct.txt │ │ ├── array_creation.txt │ │ ├── assert_statement.txt │ │ ├── attributes.txt │ │ ├── bitshift_left.txt │ │ ├── call_expression.txt │ │ ├── channels.txt │ │ ├── comments.txt │ │ ├── compile_time.txt │ │ ├── compile_time_selector_expression.txt │ │ ├── const_declaration.txt │ │ ├── enum_declaration.txt │ │ ├── enum_fetch.txt │ │ ├── error_propagation.txt │ │ ├── expression_list.txt │ │ ├── for_statement.txt │ │ ├── function_declaration.txt │ │ ├── function_literal.txt │ │ ├── generics.txt │ │ ├── global_var_declaration.txt │ │ ├── hash_statement.txt │ │ ├── if_expression.txt │ │ ├── imports.txt │ │ ├── in_expression.txt │ │ ├── interface_declaration.txt │ │ ├── is_as_expression.txt │ │ ├── json_call.txt │ │ ├── labeled_statement.txt │ │ ├── lock_expression.txt │ │ ├── map_init_expression.txt │ │ ├── match_expression.txt │ │ ├── method_declaration.txt │ │ ├── module_clause.txt │ │ ├── safe_access.txt │ │ ├── select_expression.txt │ │ ├── shebang.txt │ │ ├── slice_expression.txt │ │ ├── source_file.txt │ │ ├── spawn_expression.txt │ │ ├── special_call_expression.txt │ │ ├── static_method_declaration.txt │ │ ├── string_literal.txt │ │ ├── struct_declaration.txt │ │ ├── type_declaration.txt │ │ ├── type_initializer.txt │ │ ├── types.txt │ │ ├── unsafe_expression.txt │ │ └── var_declaration.txt │ └── tree-sitter.json └── v.mod
SYMBOL INDEX (125 symbols across 15 files)
FILE: editors/code/src/bootstrap.ts
function bootstrap (line 14) | async function bootstrap(): Promise<string> {
function getAnalyzerPath (line 35) | function getAnalyzerPath(): string {
function isAnalyzerExecutableValid (line 47) | function isAnalyzerExecutableValid(path: string): boolean {
FILE: editors/code/src/client.ts
function createClient (line 6) | async function createClient(
class ExperimentalFeatures (line 65) | class ExperimentalFeatures implements lc.StaticFeature {
method clear (line 71) | clear(): void {}
method getState (line 72) | getState(): lc.FeatureState {
method fillClientCapabilities (line 76) | fillClientCapabilities(capabilities: lc.ClientCapabilities): void {
method initialize (line 84) | initialize(
method dispose (line 89) | dispose(): void {}
FILE: editors/code/src/commands.ts
function runWorkspace (line 18) | function runWorkspace(_: ContextInit): Command {
function runFile (line 28) | function runFile(_: ContextInit): Command {
function runTests (line 38) | function runTests(_: ContextInit): Command {
function version (line 51) | function version(_: ContextInit): Command {
function serverVersion (line 65) | function serverVersion(ctx: ContextInit): Command {
function showReferences (line 78) | function showReferences(ctx: ContextInit): Command {
function showReferencesImpl (line 86) | async function showReferencesImpl(
function viewStubTree (line 101) | function viewStubTree(ctx: ContextInit): Command {
function uploadToPlayground (line 171) | function uploadToPlayground(_: ContextInit): Command {
function openGlobalConfig (line 215) | function openGlobalConfig(_: ContextInit): Command {
FILE: editors/code/src/ctx.ts
type Workspace (line 24) | type Workspace =
function fetchWorkspace (line 29) | function fetchWorkspace(): Workspace {
type Command (line 48) | type Command = (...args: any[]) => unknown;
type CommandFactory (line 50) | type CommandFactory = {
type ContextInit (line 55) | type ContextInit = Context & {
class AnalyzerNotInstalledError (line 59) | class AnalyzerNotInstalledError implements Error {
method constructor (line 60) | constructor(additionalMessage?: string) {
class Context (line 72) | class Context {
method client (line 81) | get client() {
method subscriptions (line 85) | get subscriptions(): Disposable[] {
method serverPath (line 89) | get serverPath(): string | undefined {
method activeVlangEditor (line 93) | get activeVlangEditor(): VlangEditor | undefined {
method constructor (line 98) | constructor(
method dispose (line 126) | dispose() {
method start (line 133) | async start() {
method getOrCreateClient (line 143) | private async getOrCreateClient() {
method restart (line 209) | async restart() {
method stopAndDispose (line 214) | async stopAndDispose() {
method disposeClient (line 223) | private async disposeClient() {
method updateCommands (line 237) | private updateCommands(forceDisable?: "disable") {
method showLanguageStatusBar (line 267) | showLanguageStatusBar() {
method setServerStatus (line 280) | setServerStatus(status: ra.ServerStatusParams | { health: "stopped" }) {
method pushExtCleanup (line 352) | pushExtCleanup(d: Disposable) {
method pushClientCleanup (line 356) | private pushClientCleanup(d: Disposable) {
method getScriptPath (line 360) | private getScriptPath(): string {
method installAnalyzerWithProgress (line 369) | private installAnalyzerWithProgress() {
method cleanOutput (line 381) | private cleanOutput(value: string): string {
method startInstallation (line 389) | private async startInstallation(
method installAnalyzer (line 434) | private async installAnalyzer(
method downloadScriptIfNeeded (line 470) | private async downloadScriptIfNeeded() {
type Disposable (line 505) | interface Disposable {
FILE: editors/code/src/exec.ts
type ExecCallback (line 5) | type ExecCallback = (error: ExecException | null, stdout: string, stderr...
function outputTerminal (line 9) | function outputTerminal(): Terminal {
function buildCommand (line 16) | function buildCommand(args: string[]): string {
function runVCommand (line 24) | function runVCommand(args: string[]): void {
function runVCommandInBackground (line 34) | function runVCommandInBackground(args: string[]): void {
function runVCommandCallback (line 42) | function runVCommandCallback(args: string[], callback: ExecCallback): vo...
FILE: editors/code/src/extension.ts
constant V_PROJECT_CONTEXT_NAME (line 13) | const V_PROJECT_CONTEXT_NAME = "inVlangProject";
function activate (line 19) | async function activate(context: vscode.ExtensionContext): Promise<Conte...
function deactivate (line 58) | function deactivate(): void {
function activateServer (line 62) | async function activateServer(ctx: Context): Promise<Context> {
function createCommands (line 87) | function createCommands(): Record<string, CommandFactory> {
FILE: editors/code/src/log.ts
method setEnabled (line 8) | setEnabled(yes: boolean): void {
method debug (line 13) | debug(...msg: [unknown, ...unknown[]]): void {
method info (line 18) | info(...msg: [unknown, ...unknown[]]): void {
method warn (line 22) | warn(...msg: [unknown, ...unknown[]]): void {
method error (line 27) | error(...msg: [unknown, ...unknown[]]): void {
method write (line 33) | private write(label: string, ...messageParts: unknown[]): void {
method stringify (line 39) | private stringify(val: unknown): string {
FILE: editors/code/src/lsp_ext.ts
type ServerStatusParams (line 7) | type ServerStatusParams = {
FILE: editors/code/src/stateUtils.ts
function getFromGlobalState (line 5) | function getFromGlobalState(key: string, defaultValue?: any): any {
function updateGlobalState (line 12) | function updateGlobalState(key: string, value: any) {
function setGlobalState (line 19) | function setGlobalState(state: vscode.Memento) {
FILE: editors/code/src/tcp.ts
function connectAnalyzerViaTcp (line 4) | function connectAnalyzerViaTcp(port: number): Promise<StreamInfo> {
FILE: editors/code/src/utils.ts
function getVExecCommand (line 8) | function getVExecCommand(): string {
function getWorkspaceConfig (line 16) | function getWorkspaceConfig(): vscode.WorkspaceConfiguration {
function getWorkspaceFolder (line 26) | function getWorkspaceFolder(uri?: vscode.Uri): vscode.WorkspaceFolder {
function setContextValue (line 51) | function setContextValue(key: string, value: any): Thenable<void> {
type VlangDocument (line 55) | type VlangDocument = vscode.TextDocument & { languageId: "v" };
type VlangEditor (line 56) | type VlangEditor = vscode.TextEditor & { document: VlangDocument };
function isVlangDocument (line 58) | function isVlangDocument(
function isVlangEditor (line 64) | function isVlangEditor(editor: vscode.TextEditor): editor is VlangEditor {
function sleep (line 68) | function sleep(ms: number) {
FILE: editors/code/src/welcome.ts
class WelcomePanel (line 9) | class WelcomePanel {
method showWelcome (line 10) | public static showWelcome(ctx: ContextInit): Command {
method activate (line 14) | public static activate(ctx: ContextInit) {
method createOrShow (line 31) | public static createOrShow(ctx: ContextInit) {
method revive (line 60) | public static revive(panel: vscode.WebviewPanel, extensionUri: vscode....
method constructor (line 69) | private constructor(panel: vscode.WebviewPanel, extensionUri: vscode.U...
method dispose (line 82) | public dispose() {
method update (line 96) | private update() {
method getHtmlForWebview (line 101) | private getHtmlForWebview(webview: vscode.Webview) {
function joinPath (line 174) | function joinPath(uri: vscode.Uri, ...pathFragment: string[]): vscode.Uri {
function showGoWelcomePage (line 186) | function showGoWelcomePage() {
function shouldShowGoWelcomePage (line 205) | function shouldShowGoWelcomePage(
FILE: tree_sitter_v/grammar.js
constant PREC (line 11) | const PREC = {
function sep (line 1259) | function sep(rule) {
function stringBody (line 1271) | function stringBody(re, $) {
function regexOr (line 1280) | function regexOr(...args) {
FILE: tree_sitter_v/src/tree_sitter/array.h
type Array (line 160) | typedef Array(void) Array;
function _array__delete (line 163) | static inline void _array__delete(Array *self) {
function _array__erase (line 173) | static inline void _array__erase(Array *self, size_t element_size,
function _array__reserve (line 183) | static inline void _array__reserve(Array *self, size_t element_size, uin...
function _array__assign (line 195) | static inline void _array__assign(Array *self, const Array *other, size_...
function _array__swap (line 202) | static inline void _array__swap(Array *self, Array *other) {
function _array__grow (line 209) | static inline void _array__grow(Array *self, uint32_t count, size_t elem...
function _array__splice (line 220) | static inline void _array__splice(Array *self, size_t element_size,
FILE: tree_sitter_v/src/tree_sitter/parser.h
type TSStateId (line 17) | typedef uint16_t TSStateId;
type TSSymbol (line 18) | typedef uint16_t TSSymbol;
type TSFieldId (line 19) | typedef uint16_t TSFieldId;
type TSLanguage (line 20) | typedef struct TSLanguage TSLanguage;
type TSLanguageMetadata (line 21) | typedef struct TSLanguageMetadata {
type TSFieldMapEntry (line 28) | typedef struct {
type TSMapSlice (line 35) | typedef struct {
type TSSymbolMetadata (line 40) | typedef struct {
type TSLexer (line 46) | typedef struct TSLexer TSLexer;
type TSLexer (line 48) | struct TSLexer {
type TSParseActionType (line 59) | typedef enum {
type TSParseAction (line 66) | typedef union {
type TSLexMode (line 83) | typedef struct {
type TSLexerMode (line 88) | typedef struct {
type TSParseActionEntry (line 94) | typedef union {
type TSCharacterRange (line 102) | typedef struct {
type TSLanguage (line 107) | struct TSLanguage {
function set_contains (line 154) | static inline bool set_contains(const TSCharacterRange *ranges, uint32_t...
Condensed preview — 525 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,934K chars).
[
{
"path": ".editorconfig",
"chars": 163,
"preview": "[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n\n[*.{v,js,json}]\nindent"
},
{
"path": ".gitattributes",
"chars": 148,
"preview": "* text=auto eol=lf\n*.bat eol=crlf\n\n**/*.v linguist-language=V\n**/*.vv linguist-language=V\n**/*.vsh linguist-language=V\n*"
},
{
"path": ".github/ISSUE_TEMPLATE/bug-report.yml",
"chars": 2725,
"preview": "---\nname: 🐛 Bug Report\ndescription: Report a bug\ntitle: (please, provide bug report summary here)\nlabels: [ bug ]\nbody:\n"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 405,
"preview": "blank_issues_enabled: false\ncontact_links:\n - name: 💬 General Questions and Answers about v-analyzer\n url: https://g"
},
{
"path": ".github/ISSUE_TEMPLATE/feature-request.yml",
"chars": 863,
"preview": "---\nname: 🚀 Feature Request\ndescription: Suggest an idea for v-analyzer project\ntitle: \"(short issue description)\"\nlabel"
},
{
"path": ".github/workflows/analyzer_tests.yml",
"chars": 1498,
"preview": "name: Analyzer CI\n\non:\n push:\n paths-ignore:\n - '.github/**'\n - '!**/analyzer_tests.yml'\n - '!**/vers"
},
{
"path": ".github/workflows/build_ci.yml",
"chars": 1102,
"preview": "name: Build CI\n\non:\n push:\n paths-ignore:\n - '.github/**'\n - '!**/build_ci.yml'\n - '**/test/**'\n "
},
{
"path": ".github/workflows/install_ci.yml",
"chars": 1487,
"preview": "name: Install CI\n\non:\n push:\n pull_request:\n\nconcurrency:\n group: ${{ github.workflow }}-${{ github.event.pull_reques"
},
{
"path": ".github/workflows/lint.yml",
"chars": 813,
"preview": "name: Lint\n\non:\n push:\n paths:\n - '**.v'\n - '**.vsh'\n - '**/lint.yml'\n pull_request:\n paths:\n "
},
{
"path": ".github/workflows/release.yml",
"chars": 5032,
"preview": "name: Release\n\non:\n workflow_dispatch:\n push:\n branches:\n - main\n tags:\n - '*'\n paths-ignore:\n "
},
{
"path": ".github/workflows/tree_sitter_v.yml",
"chars": 1375,
"preview": "name: Tree-sitter CI\n\non:\n push:\n paths:\n - 'tree_sitter_v/**'\n - '**/test_tree_sitter_v.yml'\n pull_reque"
},
{
"path": ".github/workflows/version_test.vv",
"chars": 753,
"preview": "import os\nimport v.vmod\n\nfn test_version() {\n\tif os.getenv('CI') != 'true' {\n\t\teprintln('WARNING: expecting usage in com"
},
{
"path": ".github/workflows/vscode_extension_tests.yml",
"chars": 881,
"preview": "name: VS Code Extension CI\n\non:\n push:\n paths:\n - 'editors/code/**'\n - '**/vscode_extension_tests.yml'\n p"
},
{
"path": ".gitignore",
"chars": 407,
"preview": "# Binaries for programs and plugins\nmain\nv-analyzer\n**/*.exe\n**/*.exp\n**/*.exe~\n**/*.so\n**/*.dylib\n**/*.dll\n**/*.log\n**/"
},
{
"path": ".gitmodules",
"chars": 132,
"preview": "[submodule \"tree_sitter_v/bindings/core\"]\n\tpath = tree_sitter_v/bindings/core\n\turl = https://github.com/tree-sitter/tree"
},
{
"path": ".v-analyzer/config.toml",
"chars": 3197,
"preview": "# Specifies the path to the V installation directory with `v` executable.\n# If not set, the plugin will try to find it o"
},
{
"path": "CHANGELOG.md",
"chars": 10638,
"preview": "# v-analyser Changelog\n\n## [0.0.6] - 2025/02/27\nSixth public release.\n\nThis release contains mainly installation issue f"
},
{
"path": "LICENSE",
"chars": 1100,
"preview": "MIT License\n\nCopyright (c) 2023 V Open Source Community Association (VOSCA)\n\nPermission is hereby granted, free of charg"
},
{
"path": "README.md",
"chars": 7012,
"preview": "<img width=\"200px\" src=\"https://github.com/vlang/v-analyzer/blob/2d5d12e4b82ce8d695576957145ff27a33a988c2/docs/cover-lig"
},
{
"path": "build.vsh",
"chars": 6041,
"preview": "#!/usr/bin/env -S v\n\n// This script is used to build the v-analyzer binary.\n// Usage: `v build.vsh [debug|dev|release]`\n"
},
{
"path": "editors/code/.editorconfig",
"chars": 219,
"preview": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\nindent_size = 4\nindent_style = tab\nmax_line_length = 90\ninsert_final_n"
},
{
"path": "editors/code/.eslintignore",
"chars": 29,
"preview": "node_modules/\ndist/\nscripts/\n"
},
{
"path": "editors/code/.eslintrc.json",
"chars": 842,
"preview": "{\n\t\"root\": true,\n\t\"parser\": \"@typescript-eslint/parser\",\n\t\"parserOptions\": {\n\t\t\"tsconfigRootDir\": \".\",\n\t\t\"project\": [\"./"
},
{
"path": "editors/code/.gitignore",
"chars": 109,
"preview": ".vscode/ipch\n.idea/\n*.code-workspace\nnode_modules/\ndist/\n*.exe\n*.tmp.json\n*.vsix\n.DS_Store\npackage-lock.json\n"
},
{
"path": "editors/code/.vscodeignore",
"chars": 175,
"preview": ".vscode/**\n.vscode-test/**\ndocs/\nnode_modules/\nscripts/\nsrc/\nimages/\n**/tests/\n\n**/tslint.json\n**/*.map\n**/*.tmp.json\n.e"
},
{
"path": "editors/code/CHANGELOG.md",
"chars": 159,
"preview": "# v-analyzer VS Code Extension Changelog\n\n## [0.0.1] - 03.07.2023\n\nFirst public release.\n\nPlease note that this is a bet"
},
{
"path": "editors/code/LICENSE",
"chars": 1100,
"preview": "MIT License\n\nCopyright (c) 2023 V Open Source Community Association (VOSCA)\n\nPermission is hereby granted, free of charg"
},
{
"path": "editors/code/README.md",
"chars": 5376,
"preview": "# v-analyzer support for Visual Studio Code\n\n[;\n\nconst isWatch = process.argv.include"
},
{
"path": "editors/code/scripts/minify_json.js",
"chars": 908,
"preview": "#!/usr/bin/env node\n// @ts-check\n\"use strict\";\n\nconst { exec } = require(\"child_process\");\nconst { writeFileSync, copyFi"
},
{
"path": "editors/code/src/bootstrap.ts",
"chars": 1608,
"preview": "import cp from \"child_process\";\nimport os from \"os\";\nimport fs from \"fs\";\nimport { log } from \"./log\";\nimport { getWorks"
},
{
"path": "editors/code/src/client.ts",
"chars": 2162,
"preview": "import * as lc from \"vscode-languageclient/node\";\nimport vscode, { window, workspace } from \"vscode\";\n\nlet crashCount = "
},
{
"path": "editors/code/src/commands.ts",
"chars": 6190,
"preview": "import * as vscode from \"vscode\";\nimport * as path from \"path\";\nimport * as lc from \"vscode-languageclient\";\nimport * as"
},
{
"path": "editors/code/src/ctx.ts",
"chars": 13276,
"preview": "import * as lc from \"vscode-languageclient/node\";\nimport * as ra from \"./lsp_ext\";\nimport * as vscode from \"vscode\";\nimp"
},
{
"path": "editors/code/src/exec.ts",
"chars": 1097,
"preview": "import { Terminal, window } from \"vscode\";\nimport { getVExecCommand } from \"./utils\";\nimport cp, { exec, ExecException }"
},
{
"path": "editors/code/src/extension.ts",
"chars": 3649,
"preview": "import * as vscode from \"vscode\";\nimport * as commands from \"./commands\";\nimport {\n\tAnalyzerNotInstalledError,\n\tCommandF"
},
{
"path": "editors/code/src/log.ts",
"chars": 1129,
"preview": "import vscode from \"vscode\";\nimport { inspect } from \"util\";\n\nexport const log = new (class {\n\tprivate enabled = true;\n\t"
},
{
"path": "editors/code/src/lsp_ext.ts",
"chars": 389,
"preview": "import * as lc from \"vscode-languageclient\";\n\nexport const serverStatus = new lc.NotificationType<ServerStatusParams>(\n\t"
},
{
"path": "editors/code/src/stateUtils.ts",
"chars": 469,
"preview": "import vscode from \"vscode\";\n\nlet globalState: vscode.Memento;\n\nexport function getFromGlobalState(key: string, defaultV"
},
{
"path": "editors/code/src/tcp.ts",
"chars": 305,
"preview": "import { StreamInfo } from \"vscode-languageclient/node\";\nimport * as net from \"net\";\n\nexport function connectAnalyzerVia"
},
{
"path": "editors/code/src/utils.ts",
"chars": 2005,
"preview": "import * as vscode from \"vscode\";\n\n/**\n * Get V executable command.\n * Will get from user setting configuration first.\n "
},
{
"path": "editors/code/src/welcome.ts",
"chars": 7373,
"preview": "import vscode from \"vscode\";\nimport path from \"path\";\nimport semver from \"semver\";\nimport { Command, ContextInit } from "
},
{
"path": "editors/code/syntaxes/stree.tmGrammar.json",
"chars": 907,
"preview": "{\n \"$schema\": \"https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json\",\n \"scopeName\": \"sou"
},
{
"path": "editors/code/syntaxes/tests/accessor.vv",
"chars": 86,
"preview": "// SYNTAX TEST \"source.v\" \"method accessor\"\nhello.world\n// ^ punctuation.accessor.v\n"
},
{
"path": "editors/code/syntaxes/tests/comma.vv",
"chars": 162,
"preview": "// SYNTAX TEST \"source.v\" \"comma\"\n [1, 2, 3, 4]\n// ^ punctuation.separator.comma.v\n// ^ punctuation.separator.comma.v"
},
{
"path": "editors/code/syntaxes/tests/comment.vv",
"chars": 194,
"preview": "// SYNTAX TEST \"source.v\" \"comment\"\n #!/usr/bin/env -S v\n// ^^ punctuation.definition.comment.shebang.v\n// ^^^^^^^^^"
},
{
"path": "editors/code/syntaxes/tests/comparison.vv",
"chars": 113,
"preview": "// SYNTAX TEST \"source.v\" \"comparison\"\n0 == 0\n//^^ keyword.operator.relation.v\n// ^ constant.numeric.integer.v\n"
},
{
"path": "editors/code/syntaxes/tests/dot.int.vv",
"chars": 71,
"preview": "// SYNTAX TEST \"source.v\"\nhello.int()\n// ^^^ entity.name.function.v\n"
},
{
"path": "editors/code/syntaxes/tests/escape.vv",
"chars": 67,
"preview": "// SYNTAX TEST \"source.v\"\n@type\n// ^^^^^ source.v - keyword.type.v\n"
},
{
"path": "editors/code/syntaxes/tests/hashtag.vv",
"chars": 189,
"preview": "// SYNTAX TEST \"source.v\" \"hashtag\"\n#include <test>\n// ^^^^^^^^^^^^^^^ markup.bold.v\n#define foo bar\n// ^^^^^^^^^^^^ mar"
},
{
"path": "editors/code/syntaxes/tests/method.vv",
"chars": 95,
"preview": "// SYNTAX TEST \"source.v\" \"method accessor\"\nhello.method()\n// ^^^^^^ entity.name.function.v\n"
},
{
"path": "editors/code/syntaxes/tests/numbers.vv",
"chars": 347,
"preview": "// SYNTAX TEST \"source.v\" \"numbers\"\n_ := 1_000_000\n// ^^^^^^^^^ constant.numeric.integer.v\n_ := 3_122.55\n// ^^^^^^^^"
},
{
"path": "editors/code/syntaxes/tests/optional.vv",
"chars": 111,
"preview": "// SYNTAX TEST \"source.v\" \"optional\"\nfn f(url string) ?string {\n// ^ keyword.operator.optional.v\n"
},
{
"path": "editors/code/syntaxes/tests/pubfn.vv",
"chars": 200,
"preview": "// SYNTAX TEST \"source.v\"\n pubfn\n// ^^^ - storage.modifier.v\n// ^^ - keyword.fn.v\n\n pub fn\n// ^^^ storage.modifie"
},
{
"path": "editors/code/syntaxes/tests/string.vv",
"chars": 398,
"preview": "// SYNTAX TEST \"source.v\" \"string\"\n_ := 'test'\n// ^^^^ string.quoted.v\na := 1\n_ := '${a}'\n// ^^ variable.other.int"
},
{
"path": "editors/code/syntaxes/tests/type.vv",
"chars": 309,
"preview": "// SYNTAX TEST \"source.v\"\n struct Foo {}\n// ^^^ entity.name.type.v\n union Foo {}\n// ^^^ entity.name.typ"
},
{
"path": "editors/code/syntaxes/tests/variable.vv",
"chars": 412,
"preview": "// SYNTAX TEST \"source.v\"\n abc := ''\n// ^^^ variable.other.assignment.v\nmut abc := ''\n// ^^^ variable.other.assignm"
},
{
"path": "editors/code/syntaxes/v.mod.tmLanguage.json",
"chars": 980,
"preview": "{\n\t\"scopeName\": \"source.v.mod\",\n\t\"patterns\": [\n\t\t{\n\t\t\t\"include\": \"#module-decl\"\n\t\t},\n\t\t{\n\t\t\t\"include\": \"#brackets\"\n\t\t}\n\t"
},
{
"path": "editors/code/syntaxes/v.tmLanguage.json",
"chars": 16354,
"preview": "{\n\t\"name\": \"V\",\n\t\"scopeName\": \"source.v\",\n\t\"fileTypes\": [\n\t\t\".v\",\n\t\t\".vsh\",\n\t\t\".vv\"\n\t],\n\t\"patterns\": [\n\t\t{\n\t\t\t\"include\":"
},
{
"path": "editors/code/tsconfig.json",
"chars": 660,
"preview": "{\n\t\"compilerOptions\": {\n\t\t\"module\": \"CommonJS\",\n\t\t\"moduleResolution\": \"node\",\n\t\t\"target\": \"ES6\",\n\t\t\"lib\": [\n\t\t\t\"ES2022\","
},
{
"path": "install.vsh",
"chars": 14808,
"preview": "#!/usr/bin/env -S v\n\n// This script is used to install and update v-analyzer.\nimport os\nimport json\nimport term\nimport t"
},
{
"path": "src/analyzer/Indexer.v",
"chars": 3539,
"preview": "module analyzer\n\nimport os\nimport time\nimport loglib\nimport analyzer.index\n\n// IndexingRootsStatus describes the indexin"
},
{
"path": "src/analyzer/IndexingManager.v",
"chars": 1370,
"preview": "module analyzer\n\nimport analyzer.psi\n\npub struct IndexingManager {\npub mut:\n\tindexer &Indexer = unsafe { nil }\n\tstub_"
},
{
"path": "src/analyzer/OpenedFile.v",
"chars": 299,
"preview": "module analyzer\n\nimport lsp\nimport utils\nimport analyzer.psi\n\npub struct OpenedFile {\npub mut:\n\turi lsp.DocumentUri"
},
{
"path": "src/analyzer/README.md",
"chars": 191,
"preview": "# Description\n\n`analyzer` module describes all the functionality related to code analysis.\n\n`server` module uses the `an"
},
{
"path": "src/analyzer/index/FileIndex.v",
"chars": 1110,
"preview": "module index\n\nimport analyzer.psi\n\n// FileIndex describes the cache of a single file.\n// By splitting the cache into fil"
},
{
"path": "src/analyzer/index/Index.v",
"chars": 1097,
"preview": "module index\n\nimport time\n\n// IndexNotFoundError is returned if the index is not found.\npub struct IndexNotFoundError {\n"
},
{
"path": "src/analyzer/index/IndexDeserializer.v",
"chars": 4807,
"preview": "module index\n\nimport analyzer.psi\nimport bytes\nimport time\n\npub struct IndexDeserializer {\nmut:\n\td bytes.Deserializer\n}\n"
},
{
"path": "src/analyzer/index/IndexSerializer.v",
"chars": 2424,
"preview": "module index\n\nimport analyzer.psi\nimport bytes\n\npub struct IndexSerializer {\nmut:\n\ts bytes.Serializer\n}\n\npub fn (mut s I"
},
{
"path": "src/analyzer/index/IndexingRoot.v",
"chars": 9925,
"preview": "module index\n\nimport time\nimport os\nimport sync\nimport runtime\nimport math\nimport loglib\nimport lsp\nimport crypto.md5\nim"
},
{
"path": "src/analyzer/index/IndexingRoot_test.v",
"chars": 302,
"preview": "module index\n\nfn test_git_files_do_not_need_indexed() {\n\tmut ir := new_indexing_root('.', .workspace, '/tmp')\n\tassert !i"
},
{
"path": "src/analyzer/index/PerFileIndex.v",
"chars": 777,
"preview": "module index\n\nimport analyzer.psi\n\n// PerFileIndex describes the cache of a group of files in the index.\npub struct PerF"
},
{
"path": "src/analyzer/index/README.md",
"chars": 72,
"preview": "## Description\n\n`index` module describes index and indexing operations.\n"
},
{
"path": "src/analyzer/index/StubTree.v",
"chars": 4397,
"preview": "module index\n\nimport strings\nimport loglib\nimport analyzer.psi\n\n// StubTree represents a tree of stubs for a file.\n// Th"
},
{
"path": "src/analyzer/lang/utils.v",
"chars": 1495,
"preview": "module lang\n\nimport analyzer.psi\nimport analyzer.psi.types\n\npub fn get_zero_value_for(typ types.Type) string {\n\treturn m"
},
{
"path": "src/analyzer/parser/README.md",
"chars": 383,
"preview": "## Description\n\n`parser` module provides way to parse V code to AST.\n\nInput may be provided in a variety of forms (see t"
},
{
"path": "src/analyzer/parser/batch.v",
"chars": 1098,
"preview": "module parser\n\nimport sync\n\npub fn parse_batch_files(files []string, count_workers int) []ParseResult {\n\teffective_worke"
},
{
"path": "src/analyzer/parser/parser.v",
"chars": 3803,
"preview": "module parser\n\nimport tree_sitter_v.bindings\nimport os\n\n// ParseResult represents the result of a parsing operation.\npub"
},
{
"path": "src/analyzer/psi/Argument.v",
"chars": 52,
"preview": "module psi\n\npub struct Argument {\n\tPsiElementImpl\n}\n"
},
{
"path": "src/analyzer/psi/ArrayCreation.v",
"chars": 210,
"preview": "module psi\n\npub struct ArrayCreation {\n\tPsiElementImpl\n\tis_fixed bool\n}\n\npub fn (n ArrayCreation) expressions() []PsiEle"
},
{
"path": "src/analyzer/psi/AstNode.v",
"chars": 225,
"preview": "module psi\n\nimport tree_sitter_v.bindings\n\npub fn (node AstNode) parent_of_type(typ bindings.NodeType) ?AstNode {\n\tmut r"
},
{
"path": "src/analyzer/psi/Attribute.v",
"chars": 573,
"preview": "module psi\n\npub struct Attribute {\n\tPsiElementImpl\n}\n\nfn (_ &Attribute) stub() {}\n\npub fn (n Attribute) expressions() []"
},
{
"path": "src/analyzer/psi/AttributeExpression.v",
"chars": 394,
"preview": "module psi\n\npub struct AttributeExpression {\n\tPsiElementImpl\n}\n\npub fn (n &AttributeExpression) value() string {\n\tif stu"
},
{
"path": "src/analyzer/psi/Attributes.v",
"chars": 189,
"preview": "module psi\n\npub struct Attributes {\n\tPsiElementImpl\n}\n\nfn (_ &Attributes) stub() {}\n\npub fn (n Attributes) attributes() "
},
{
"path": "src/analyzer/psi/AttributesOwner.v",
"chars": 73,
"preview": "module psi\n\npub interface AttributesOwner {\n\tattributes() []PsiElement\n}\n"
},
{
"path": "src/analyzer/psi/BinaryExpression.v",
"chars": 398,
"preview": "module psi\n\npub struct BinaryExpression {\n\tPsiElementImpl\n}\n\npub fn (n BinaryExpression) operator() string {\n\toperator_e"
},
{
"path": "src/analyzer/psi/Block.v",
"chars": 707,
"preview": "module psi\n\npub struct Block {\n\tPsiElementImpl\n}\n\npub fn (b Block) last_expression() ?PsiElement {\n\tstatements := b.find"
},
{
"path": "src/analyzer/psi/CallExpression.v",
"chars": 2311,
"preview": "module psi\n\nimport analyzer.psi.types\n\npub struct CallExpression {\n\tPsiElementImpl\n}\n\nfn (c &CallExpression) get_type() "
},
{
"path": "src/analyzer/psi/Comment.v",
"chars": 100,
"preview": "module psi\n\npub struct LineComment {\n\tPsiElementImpl\n}\n\npub struct BlockComment {\n\tPsiElementImpl\n}\n"
},
{
"path": "src/analyzer/psi/CompileTimeIfExpression.v",
"chars": 338,
"preview": "module psi\n\npub struct CompileTimeIfExpression {\n\tPsiElementImpl\n}\n\npub fn (n CompileTimeIfExpression) block() ?&Block {"
},
{
"path": "src/analyzer/psi/ConstantDeclaration.v",
"chars": 175,
"preview": "module psi\n\npub struct ConstantDeclaration {\n\tPsiElementImpl\n}\n\npub fn (n ConstantDeclaration) constants() []PsiElement "
},
{
"path": "src/analyzer/psi/ConstantDefinition.v",
"chars": 2164,
"preview": "module psi\n\nimport analyzer.parser\nimport analyzer.psi.types\n\npub struct ConstantDefinition {\n\tPsiElementImpl\n}\n\npub fn "
},
{
"path": "src/analyzer/psi/EmbeddedDefinition.v",
"chars": 1290,
"preview": "module psi\n\nimport analyzer.psi.types\n\npub struct EmbeddedDefinition {\n\tPsiElementImpl\n}\n\npub fn (n &EmbeddedDefinition)"
},
{
"path": "src/analyzer/psi/EnumDeclaration.v",
"chars": 1953,
"preview": "module psi\n\nimport analyzer.psi.types\n\npub struct EnumDeclaration {\n\tPsiElementImpl\n}\n\npub fn (e &EnumDeclaration) is_pu"
},
{
"path": "src/analyzer/psi/EnumFieldDeclaration.v",
"chars": 4463,
"preview": "@[translated]\nmodule psi\n\nimport analyzer.parser\nimport analyzer.psi.types\n\n__global enum_fields_cache = map[string]int{"
},
{
"path": "src/analyzer/psi/FieldDeclaration.v",
"chars": 1823,
"preview": "module psi\n\nimport analyzer.psi.types\n\npub struct FieldDeclaration {\n\tPsiElementImpl\n}\n\npub fn (f &FieldDeclaration) is_"
},
{
"path": "src/analyzer/psi/FieldName.v",
"chars": 235,
"preview": "module psi\n\npub struct FieldName {\n\tPsiElementImpl\n}\n\npub fn (n FieldName) reference_expression() ?&ReferenceExpression "
},
{
"path": "src/analyzer/psi/ForStatement.v",
"chars": 711,
"preview": "module psi\n\npub struct ForStatement {\n\tPsiElementImpl\n}\n\npub fn (n ForStatement) var_definitions() []PsiElement {\n\tif ra"
},
{
"path": "src/analyzer/psi/FunctionLiteral.v",
"chars": 235,
"preview": "module psi\n\npub struct FunctionLiteral {\n\tPsiElementImpl\n}\n\npub fn (f FunctionLiteral) signature() ?&Signature {\n\tsignat"
},
{
"path": "src/analyzer/psi/FunctionOrMethodDeclaration.v",
"chars": 2911,
"preview": "module psi\n\nimport analyzer.psi.types\n\npub struct FunctionOrMethodDeclaration {\n\tPsiElementImpl\n}\n\npub fn (f &FunctionOr"
},
{
"path": "src/analyzer/psi/GenericArgumentsOwner.v",
"chars": 93,
"preview": "module psi\n\npub interface GenericArgumentsOwner {\n\ttype_arguments() ?&GenericTypeArguments\n}\n"
},
{
"path": "src/analyzer/psi/GenericParameter.v",
"chars": 667,
"preview": "module psi\n\n@[heap]\npub struct GenericParameter {\n\tPsiElementImpl\n}\n\npub fn (n &GenericParameter) identifier_text_range("
},
{
"path": "src/analyzer/psi/GenericParameters.v",
"chars": 942,
"preview": "module psi\n\nimport strings\n\npub struct GenericParameters {\n\tPsiElementImpl\n}\n\nfn (_ &GenericParameters) stub() {}\n\npub f"
},
{
"path": "src/analyzer/psi/GenericParametersOwner.v",
"chars": 95,
"preview": "module psi\n\npub interface GenericParametersOwner {\n\tgeneric_parameters() ?&GenericParameters\n}\n"
},
{
"path": "src/analyzer/psi/GenericTypeArguments.v",
"chars": 439,
"preview": "module psi\n\nimport analyzer.psi.types\n\npub struct GenericTypeArguments {\n\tPsiElementImpl\n}\n\npub fn (n GenericTypeArgumen"
},
{
"path": "src/analyzer/psi/GenericTypeInferer.v",
"chars": 4937,
"preview": "module psi\n\nimport analyzer.psi.types\nimport math\n\nstruct GenericTypeInferer {}\n\nfn (g &GenericTypeInferer) infer_generi"
},
{
"path": "src/analyzer/psi/GenericTypeReifier.v",
"chars": 2501,
"preview": "module psi\n\nimport analyzer.psi.types\nimport math\n\npub struct GenericTypeReifier {\nmut:\n\timplicit_specialization_types_m"
},
{
"path": "src/analyzer/psi/GlobalVarDefinition.v",
"chars": 845,
"preview": "module psi\n\nimport analyzer.psi.types\n\npub struct GlobalVarDefinition {\n\tPsiElementImpl\n}\n\nfn (_ &GlobalVarDefinition) s"
},
{
"path": "src/analyzer/psi/Identifier.v",
"chars": 761,
"preview": "module psi\n\npub struct Identifier {\n\tPsiElementImpl\n}\n\npub fn (i Identifier) value() string {\n\treturn i.get_text()\n}\n\npu"
},
{
"path": "src/analyzer/psi/IfExpression.v",
"chars": 635,
"preview": "module psi\n\npub struct IfExpression {\n\tPsiElementImpl\n}\n\npub fn (n IfExpression) var_definition() ?&VarDefinition {\n\tdec"
},
{
"path": "src/analyzer/psi/ImportAlias.v",
"chars": 86,
"preview": "module psi\n\npub struct ImportAlias {\n\tPsiElementImpl\n}\n\nfn (n &ImportAlias) stub() {}\n"
},
{
"path": "src/analyzer/psi/ImportDeclaration.v",
"chars": 253,
"preview": "module psi\n\npub struct ImportDeclaration {\n\tPsiElementImpl\n}\n\npub fn (n &ImportDeclaration) spec() ?&ImportSpec {\n\tspec "
},
{
"path": "src/analyzer/psi/ImportList.v",
"chars": 84,
"preview": "module psi\n\npub struct ImportList {\n\tPsiElementImpl\n}\n\nfn (n &ImportList) stub() {}\n"
},
{
"path": "src/analyzer/psi/ImportName.v",
"chars": 84,
"preview": "module psi\n\npub struct ImportName {\n\tPsiElementImpl\n}\n\nfn (n &ImportName) stub() {}\n"
},
{
"path": "src/analyzer/psi/ImportPath.v",
"chars": 84,
"preview": "module psi\n\npub struct ImportPath {\n\tPsiElementImpl\n}\n\nfn (n &ImportPath) stub() {}\n"
},
{
"path": "src/analyzer/psi/ImportSpec.v",
"chars": 1654,
"preview": "module psi\n\npub struct ImportSpec {\n\tPsiElementImpl\n}\n\nfn (_ &ImportSpec) stub() {}\n\npub fn (_ &ImportSpec) is_public() "
},
{
"path": "src/analyzer/psi/IndexExpression.v",
"chars": 240,
"preview": "module psi\n\nimport analyzer.psi.types\n\npub struct IndexExpression {\n\tPsiElementImpl\n}\n\nfn (c &IndexExpression) get_type("
},
{
"path": "src/analyzer/psi/InterfaceDeclaration.v",
"chars": 3568,
"preview": "module psi\n\nimport analyzer.psi.types\n\npub struct InterfaceDeclaration {\n\tPsiElementImpl\n}\n\npub fn (s &InterfaceDeclarat"
},
{
"path": "src/analyzer/psi/InterfaceMethodDeclaration.v",
"chars": 1729,
"preview": "module psi\n\npub struct InterfaceMethodDeclaration {\n\tPsiElementImpl\n}\n\npub fn (_ InterfaceMethodDeclaration) is_public()"
},
{
"path": "src/analyzer/psi/KeyedElement.v",
"chars": 278,
"preview": "module psi\n\npub struct KeyedElement {\n\tPsiElementImpl\n}\n\npub fn (n KeyedElement) field() ?&FieldName {\n\tfirst_child := n"
},
{
"path": "src/analyzer/psi/Literal.v",
"chars": 500,
"preview": "module psi\n\nimport strconv\nimport analyzer.psi.types\n\npub struct Literal {\n\tPsiElementImpl\n}\n\nfn (n &Literal) get_type()"
},
{
"path": "src/analyzer/psi/MapInitExpression.v",
"chars": 173,
"preview": "module psi\n\npub struct MapInitExpression {\n\tPsiElementImpl\n}\n\npub fn (n MapInitExpression) key_values() []PsiElement {\n\t"
},
{
"path": "src/analyzer/psi/MapKeyedElement.v",
"chars": 208,
"preview": "module psi\n\npub struct MapKeyedElement {\n\tPsiElementImpl\n}\n\npub fn (n MapKeyedElement) key() ?PsiElement {\n\treturn n.fir"
},
{
"path": "src/analyzer/psi/MatchExpression.v",
"chars": 509,
"preview": "module psi\n\npub struct MatchExpression {\n\tPsiElementImpl\n}\n\npub fn (n MatchExpression) expression() ?PsiElement {\n\tretur"
},
{
"path": "src/analyzer/psi/ModuleClause.v",
"chars": 2264,
"preview": "module psi\n\nimport os\n\npub struct ModuleClause {\n\tPsiElementImpl\n}\n\nfn (_ &ModuleClause) stub() {}\n\npub fn (_ &ModuleCla"
},
{
"path": "src/analyzer/psi/MutExpression.v",
"chars": 57,
"preview": "module psi\n\npub struct MutExpression {\n\tPsiElementImpl\n}\n"
},
{
"path": "src/analyzer/psi/MutabilityModifiers.v",
"chars": 188,
"preview": "module psi\n\npub struct MutabilityModifiers {\n\tPsiElementImpl\n}\n\npub fn (n MutabilityModifiers) is_mutable() bool {\n\tchil"
},
{
"path": "src/analyzer/psi/MutabilityOwner.v",
"chars": 111,
"preview": "module psi\n\npub interface MutabilityOwner {\n\tis_mutable() bool\n\tmutability_modifiers() ?&MutabilityModifiers\n}\n"
},
{
"path": "src/analyzer/psi/OptionPropagationExpression.v",
"chars": 276,
"preview": "module psi\n\nimport analyzer.psi.types\n\npub struct OptionPropagationExpression {\n\tPsiElementImpl\n}\n\nfn (c &OptionPropagat"
},
{
"path": "src/analyzer/psi/OrBlockExpression.v",
"chars": 246,
"preview": "module psi\n\nimport analyzer.psi.types\n\npub struct OrBlockExpression {\n\tPsiElementImpl\n}\n\nfn (c &OrBlockExpression) get_t"
},
{
"path": "src/analyzer/psi/ParameterDeclaration.v",
"chars": 1152,
"preview": "module psi\n\nimport analyzer.psi.types\n\npub struct ParameterDeclaration {\n\tPsiElementImpl\n}\n\nfn (p &ParameterDeclaration)"
},
{
"path": "src/analyzer/psi/ParameterList.v",
"chars": 90,
"preview": "module psi\n\npub struct ParameterList {\n\tPsiElementImpl\n}\n\nfn (_ &ParameterList) stub() {}\n"
},
{
"path": "src/analyzer/psi/PlainType.v",
"chars": 90,
"preview": "module psi\n\n@[heap]\npub struct PlainType {\n\tPsiElementImpl\n}\n\nfn (_ &PlainType) stub() {}\n"
},
{
"path": "src/analyzer/psi/Position.v",
"chars": 71,
"preview": "module psi\n\npub struct Position {\npub:\n\tline int\n\tcharacter int\n}\n"
},
{
"path": "src/analyzer/psi/PrinterVisitor.v",
"chars": 1157,
"preview": "module psi\n\nimport arrays\n\npub struct PrinterVisitor {\nmut:\n\tindent int\n\tlines []string\n\ttext_lines []string\n}\n"
},
{
"path": "src/analyzer/psi/PsiDocCommentOwner.v",
"chars": 71,
"preview": "module psi\n\npub interface PsiDocCommentOwner {\n\tdoc_comment() string\n}\n"
},
{
"path": "src/analyzer/psi/PsiElement.v",
"chars": 6130,
"preview": "module psi\n\nimport tree_sitter_v.bindings\n\npub type ID = int\n\npub type AstNode = bindings.Node[bindings.NodeType]\n\npub i"
},
{
"path": "src/analyzer/psi/PsiElementImpl.v",
"chars": 11144,
"preview": "module psi\n\nimport tree_sitter_v.bindings\n\npub struct PsiElementImpl {\npub:\n\tnode AstNode // base node from T"
},
{
"path": "src/analyzer/psi/PsiElementVisitor.v",
"chars": 257,
"preview": "module psi\n\npub interface PsiElementVisitor {\n\tvisit_element(element PsiElement)\n\tvisit_element_impl(element PsiElement)"
},
{
"path": "src/analyzer/psi/PsiFile.v",
"chars": 6240,
"preview": "module psi\n\nimport lsp\nimport time\nimport utils\nimport loglib\nimport analyzer.parser\nimport tree_sitter_v.bindings\n\n@[he"
},
{
"path": "src/analyzer/psi/PsiNamedElement.v",
"chars": 222,
"preview": "module psi\n\nimport tree_sitter_v.bindings\n\npub interface PsiNamedElement {\n\tparent_of_type(typ bindings.NodeType) ?PsiEl"
},
{
"path": "src/analyzer/psi/PsiReference.v",
"chars": 118,
"preview": "module psi\n\npub interface PsiReference {\n\telement() PsiElement\n\tresolve() ?PsiElement\n\tmulti_resolve() []PsiElement\n}\n"
},
{
"path": "src/analyzer/psi/PsiScopeProcessor.v",
"chars": 87,
"preview": "module psi\n\npub interface PsiScopeProcessor {\nmut:\n\texecute(element PsiElement) bool\n}\n"
},
{
"path": "src/analyzer/psi/PsiTreeWalker.v",
"chars": 503,
"preview": "module psi\n\nstruct PsiTreeWalker {\nmut:\n\tcontaining_file ?&PsiFile\n\ttree_walker TreeWalker\n}\n\npub fn (mut tw PsiTree"
},
{
"path": "src/analyzer/psi/PsiTypedElement.v",
"chars": 96,
"preview": "module psi\n\nimport analyzer.psi.types\n\npub interface PsiTypedElement {\n\tget_type() types.Type\n}\n"
},
{
"path": "src/analyzer/psi/QualifiedType.v",
"chars": 954,
"preview": "module psi\n\nimport analyzer.psi.types\n\npub struct QualifiedType {\n\tPsiElementImpl\n}\n\nfn (n &QualifiedType) name() string"
},
{
"path": "src/analyzer/psi/README.md",
"chars": 89,
"preview": "## Description\n\n`psi` module describes Program Structure Interface (PSI) for V language.\n"
},
{
"path": "src/analyzer/psi/Range.v",
"chars": 381,
"preview": "module psi\n\npub struct Range {\n\tPsiElementImpl\n}\n\npub fn (n Range) left() ?PsiElement {\n\treturn n.first_child()\n}\n\npub f"
},
{
"path": "src/analyzer/psi/Receiver.v",
"chars": 1320,
"preview": "module psi\n\nimport analyzer.psi.types\n\npub struct Receiver {\n\tPsiElementImpl\n}\n\npub fn (r &Receiver) is_public() bool {\n"
},
{
"path": "src/analyzer/psi/RecursiveVisitor.v",
"chars": 378,
"preview": "module psi\n\npub struct RecursiveVisitorBase {\n}\n\nfn (r &RecursiveVisitorBase) visit_element(element PsiElement) {\n\tif !r"
},
{
"path": "src/analyzer/psi/ReferenceExpression.v",
"chars": 1274,
"preview": "module psi\n\nimport analyzer.psi.types\n\npub struct ReferenceExpression {\n\tPsiElementImpl\n}\n\npub fn (r &ReferenceExpressio"
},
{
"path": "src/analyzer/psi/ReferenceExpressionBase.v",
"chars": 186,
"preview": "module psi\n\npub interface ReferenceExpressionBase {\n\tget_text() string\n\ttext_range() TextRange\n\tname() string\n\tqualifier"
},
{
"path": "src/analyzer/psi/ReferenceImpl.v",
"chars": 21470,
"preview": "module psi\n\nimport analyzer.psi.types\nimport utils\n\npub struct ReferenceImpl {\n\telement ReferenceExpressionBase\n\t"
},
{
"path": "src/analyzer/psi/ResolveCache.v",
"chars": 1125,
"preview": "@[translated]\nmodule psi\n\nimport sync\nimport loglib\n\n__global resolve_cache = ResolveCache{}\n\npub struct ResolveCache {\n"
},
{
"path": "src/analyzer/psi/ResultPropagationExpression.v",
"chars": 276,
"preview": "module psi\n\nimport analyzer.psi.types\n\npub struct ResultPropagationExpression {\n\tPsiElementImpl\n}\n\nfn (c &ResultPropagat"
},
{
"path": "src/analyzer/psi/SelectiveImportList.v",
"chars": 388,
"preview": "module psi\n\npub struct SelectiveImportList {\n\tPsiElementImpl\n}\n\npub fn (n &SelectiveImportList) symbols() []ReferenceExp"
},
{
"path": "src/analyzer/psi/SelectorExpression.v",
"chars": 1478,
"preview": "module psi\n\nimport analyzer.psi.types\n\npub struct SelectorExpression {\n\tPsiElementImpl\n}\n\nfn (n &SelectorExpression) nam"
},
{
"path": "src/analyzer/psi/Signature.v",
"chars": 715,
"preview": "module psi\n\nimport analyzer.psi.types\n\npub struct Signature {\n\tPsiElementImpl\n}\n\npub fn (s &Signature) get_type() types."
},
{
"path": "src/analyzer/psi/SignatureOwner.v",
"chars": 70,
"preview": "module psi\n\npub interface SignatureOwner {\n\tsignature() ?&Signature\n}\n"
},
{
"path": "src/analyzer/psi/SliceExpression.v",
"chars": 549,
"preview": "module psi\n\npub struct SliceExpression {\n\tPsiElementImpl\n}\n\npub fn (c SliceExpression) expression() ?PsiElement {\n\tretur"
},
{
"path": "src/analyzer/psi/SourceFile.v",
"chars": 524,
"preview": "module psi\n\npub struct SourceFile {\n\tPsiElementImpl\n}\n\npub fn (b SourceFile) process_declarations(mut processor PsiScope"
},
{
"path": "src/analyzer/psi/StaticMethodDeclaration.v",
"chars": 2866,
"preview": "module psi\n\nimport analyzer.psi.types\n\npub struct StaticMethodDeclaration {\n\tPsiElementImpl\n}\n\npub fn (f &StaticMethodDe"
},
{
"path": "src/analyzer/psi/StaticReceiver.v",
"chars": 756,
"preview": "module psi\n\nimport analyzer.psi.types\n\npub struct StaticReceiver {\n\tPsiElementImpl\n}\n\npub fn (_ &StaticReceiver) is_publ"
},
{
"path": "src/analyzer/psi/StringLiteral.v",
"chars": 156,
"preview": "module psi\n\npub struct StringLiteral {\n\tPsiElementImpl\n}\n\npub fn (n StringLiteral) content() string {\n\ttext := n.get_tex"
},
{
"path": "src/analyzer/psi/StructDeclaration.v",
"chars": 3566,
"preview": "module psi\n\nimport analyzer.psi.types\n\npub struct StructDeclaration {\n\tPsiElementImpl\n}\n\npub fn (s &StructDeclaration) g"
},
{
"path": "src/analyzer/psi/StructFieldScope.v",
"chars": 235,
"preview": "module psi\n\npub struct StructFieldScope {\n\tPsiElementImpl\n}\n\npub fn (n StructFieldScope) is_mutable_public() (bool, bool"
},
{
"path": "src/analyzer/psi/StubBase.v",
"chars": 5851,
"preview": "module psi\n\nimport tree_sitter_v.bindings\n\npub type StubId = int\n\nconst non_stubbed_element = StubId(-1)\n\n@[params]\npub "
},
{
"path": "src/analyzer/psi/StubBasedPsiElement.v",
"chars": 1630,
"preview": "module psi\n\n// StubIndexKey describes the various types of indexes that are built on `index.StubTree`.\n// These indexes "
},
{
"path": "src/analyzer/psi/StubElement.v",
"chars": 849,
"preview": "module psi\n\n// StubElement describes the interface of any stub.\npub interface StubElement {\n\tid() StubId\n\tname() string\n"
},
{
"path": "src/analyzer/psi/StubIndex.v",
"chars": 11418,
"preview": "@[translated]\nmodule psi\n\nimport time\nimport os\nimport loglib\n\n__global stubs_index = StubIndex{}\n\nconst count_index_key"
},
{
"path": "src/analyzer/psi/StubIndexSink.v",
"chars": 946,
"preview": "module psi\n\n@[heap]\npub struct StubIndexSink {\npub mut:\n\tstub_id StubId\n\tstub_list &StubList = unsafe { "
},
{
"path": "src/analyzer/psi/StubList.v",
"chars": 3072,
"preview": "module psi\n\n// StubList describes a way to store all stubs in a specific file.\n// Storing stubs as a table is more effic"
},
{
"path": "src/analyzer/psi/StubbedElementTypeImpl.v",
"chars": 16172,
"preview": "module psi\n\nimport utils\nimport tree_sitter_v.bindings\n\npub enum StubType as u8 {\n\troot\n\tfunction_declaration\n\tmethod_de"
},
{
"path": "src/analyzer/psi/TextRange.v",
"chars": 336,
"preview": "module psi\n\n// TextRange represents a range of text in a file.\npub struct TextRange {\npub:\n\tline int\n\tcolumn i"
},
{
"path": "src/analyzer/psi/TreeWalker.v",
"chars": 1321,
"preview": "module psi\n\nimport tree_sitter_v.bindings\n\npub struct TreeWalker {\nmut:\n\talready_visited_children bool\n\tcursor "
},
{
"path": "src/analyzer/psi/TypeAliasDeclaration.v",
"chars": 2084,
"preview": "module psi\n\nimport analyzer.psi.types\n\npub struct TypeAliasDeclaration {\n\tPsiElementImpl\n}\n\npub fn (a &TypeAliasDeclarat"
},
{
"path": "src/analyzer/psi/TypeCache.v",
"chars": 1118,
"preview": "@[translated]\nmodule psi\n\nimport analyzer.psi.types\nimport sync\nimport loglib\n\n__global type_cache = TypeCache{}\n\npub st"
},
{
"path": "src/analyzer/psi/TypeInferer.v",
"chars": 28466,
"preview": "module psi\n\nimport analyzer.psi.types\n\npub fn infer_type(elem ?PsiElement) types.Type {\n\treturn TypeInferer{}.infer_type"
},
{
"path": "src/analyzer/psi/TypeInitializer.v",
"chars": 390,
"preview": "module psi\n\nimport analyzer.psi.types\n\npub struct TypeInitializer {\n\tPsiElementImpl\n}\n\npub fn (n &TypeInitializer) get_t"
},
{
"path": "src/analyzer/psi/TypeReferenceExpression.v",
"chars": 1346,
"preview": "module psi\n\nimport analyzer.psi.types\n\npub struct TypeReferenceExpression {\n\tPsiElementImpl\n}\n\nfn (_ &TypeReferenceExpre"
},
{
"path": "src/analyzer/psi/UnaryExpression.v",
"chars": 313,
"preview": "module psi\n\npub struct UnaryExpression {\n\tPsiElementImpl\n}\n\npub fn (n UnaryExpression) operator() string {\n\toperator_ele"
},
{
"path": "src/analyzer/psi/UnsafeExpression.v",
"chars": 201,
"preview": "module psi\n\npub struct UnsafeExpression {\n\tPsiElementImpl\n}\n\npub fn (n UnsafeExpression) block() ?&Block {\n\tblock := n.f"
},
{
"path": "src/analyzer/psi/ValueAttribute.v",
"chars": 159,
"preview": "module psi\n\npub struct ValueAttribute {\n\tPsiElementImpl\n}\n\npub fn (n ValueAttribute) value() string {\n\treturn n.get_text"
},
{
"path": "src/analyzer/psi/VarDeclaration.v",
"chars": 1317,
"preview": "module psi\n\npub struct VarDeclaration {\n\tPsiElementImpl\n}\n\nfn (v VarDeclaration) index_of(def VarDefinition) int {\n\tfirs"
},
{
"path": "src/analyzer/psi/VarDefinition.v",
"chars": 1564,
"preview": "module psi\n\nimport analyzer.psi.types\n\npub struct VarDefinition {\n\tPsiElementImpl\n}\n\npub fn (_ &VarDefinition) is_public"
},
{
"path": "src/analyzer/psi/VisibilityModifiers.v",
"chars": 185,
"preview": "module psi\n\npub struct VisibilityModifiers {\n\tPsiElementImpl\n}\n\npub fn (n VisibilityModifiers) is_public() bool {\n\tretur"
},
{
"path": "src/analyzer/psi/doc_comment_extractor.v",
"chars": 2682,
"preview": "module psi\n\nimport strings\n\npub fn extract_doc_comment(el PsiElement) string {\n\tel_start_line := el.node().start_point()"
},
{
"path": "src/analyzer/psi/element_factory.v",
"chars": 9211,
"preview": "module psi\n\npub fn create_element(node AstNode, containing_file ?&PsiFile) PsiElement {\n\tbase_node := new_psi_node(conta"
},
{
"path": "src/analyzer/psi/search/ReferencesSearch.v",
"chars": 9583,
"preview": "module search\n\nimport analyzer.psi\nimport analyzer.parser\nimport runtime\nimport math\nimport time\nimport loglib\n\n@[params"
},
{
"path": "src/analyzer/psi/search/common.v",
"chars": 3082,
"preview": "module search\n\nimport analyzer.psi\nimport analyzer.psi.types\n\n// is_implemented checks if the given symbol (methods and "
},
{
"path": "src/analyzer/psi/search/implementations.v",
"chars": 3707,
"preview": "module search\n\nimport analyzer.psi\n\n// implementations returns all implementations of the given interface\n//\n// Search a"
},
{
"path": "src/analyzer/psi/search/implmenttion_methods.v",
"chars": 530,
"preview": "module search\n\nimport analyzer.psi\n\n// implementation_methods returns all methods that implement the given interface met"
},
{
"path": "src/analyzer/psi/search/super_methods.v",
"chars": 618,
"preview": "module search\n\nimport analyzer.psi\n\n// super_methods returns interface methods that are implemented by the struct of giv"
},
{
"path": "src/analyzer/psi/search/supers.v",
"chars": 3133,
"preview": "module search\n\nimport analyzer.psi\n\n// supers returns all interfaces that are implemented by the given struct\n//\n// Sear"
},
{
"path": "src/analyzer/psi/types/AliasType.v",
"chars": 533,
"preview": "module types\n\npub struct AliasType {\n\tBaseNamedType\npub:\n\tinner Type\n}\n\npub fn new_alias_type(name string, module_name s"
}
]
// ... and 325 more files (download for full content)
About this extraction
This page contains the full source code of the vlang/v-analyzer GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 525 files (13.5 MB), approximately 426.5k tokens, and a symbol index with 125 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.