Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Autocompletion for viewModel/baseType directives #6

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5918cb8
Initial prototype of .NET Native library for analyzing dll metadata
exyi Sep 12, 2022
308a311
Remove unneeded linker flags
exyi Sep 14, 2022
d6352b2
attempt to add node.js binding, but it doesn't work
exyi Sep 14, 2022
3bcf1c1
analyzerlib actually builds
exyi Sep 25, 2022
53b419c
rename native library to dotvvmspy
exyi Sep 25, 2022
2a5418e
cleanup CMakeLists
exyi Sep 25, 2022
8531050
Fix yarn again
exyi Sep 25, 2022
f831528
switch FileSystemProvider to fs/promises
exyi Sep 25, 2022
136d083
@viewModel and @baseType type autocompletion
exyi Sep 26, 2022
1e86c74
Avoid file locking to prevent conflicting with MSBuild
exyi Sep 26, 2022
493c343
dotvvm-spy: add to global build.sh, ditch static lib
exyi Nov 22, 2022
c6d92ae
Reduce pointless build verbosity
exyi Nov 22, 2022
cb204b5
Add some vscode settings for c++
exyi Nov 22, 2022
d2a0392
Fix directive autocomplete with generic types
exyi Nov 22, 2022
40b3650
Added support for Windows and refactored CMakeLists
acizmarik Nov 24, 2022
2445d6e
Added build scripts for Windows
acizmarik Nov 24, 2022
573923c
Merge branch 'dotnet-native-analyzer' into feature/windows-support
acizmarik Nov 24, 2022
1c3177c
Fix dotvvmspy node binding build on Linux
exyi Dec 7, 2022
21e77d4
Merge remote-tracking branch 'origin/feature/windows-support' into do…
exyi Dec 7, 2022
57a6614
dotvvmspy: handle unsupported platforms more gracefuly
exyi Dec 7, 2022
7498222
Publish LS with the .NET Native module
exyi Dec 7, 2022
f579821
Fix setExecutableFlag when runWithNode is set
exyi Dec 8, 2022
1b29ff9
dotvvmspy: link to library name, not fullpath
exyi Dec 9, 2022
310b684
Workaround for pkg only loading additional libs if in node_modules
exyi Dec 9, 2022
0114591
Better hack for pkg + dynamic native libraries
exyi Dec 9, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ jobs:
cd src/tree-sitter-dotvvm
yarn install --immutable
yarn build
- name: Build .NET native module
if: ${{ matrix.os == 'ubuntu-latest' }}
run: |
cd src/native-lib-dotvvm-spy
./build.sh
- name: Build .NET native module
if: ${{ matrix.os == 'windows-latest' }}
run: |
cd src/native-lib-dotvvm-spy
./build.ps1
- name: Build language server
run: |
cd src/dothtml-basic-ls
Expand Down
17 changes: 17 additions & 0 deletions .vscode/c_cpp_properties.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
"/usr/include/node"
],
"defines": [],
"compilerPath": "/usr/bin/clang",
"cStandard": "c17",
"cppStandard": "c++14",
"intelliSenseMode": "linux-clang-x64"
}
],
"version": 4
}
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"cSpell.words": [
"dotvvmspy"
]
}
1 change: 1 addition & 0 deletions src/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ root = true
[*]
end_of_line = lf
insert_final_newline = true
indent_style = space

[*.{js,json,yml}]
charset = utf-8
Expand Down
532 changes: 266 additions & 266 deletions src/.yarn/releases/yarn-3.2.2.cjs → src/.yarn/releases/yarn-3.2.3.cjs

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/.yarnrc.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
yarnPath: .yarn/releases/yarn-3.2.2.cjs
nodeLinker: node-modules

yarnPath: .yarn/releases/yarn-3.2.3.cjs
21 changes: 21 additions & 0 deletions src/build.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
cd tree-sitter-dotvvm
echo "Building tree sitter"
yarn install --immutable
yarn build

cd ..
cd native-lib-dotvvm-spy
echo "Attempting to build .NET Native dotvvm-spy"
./build.ps1 -ErrorAction SilentlyContinue

cd ..
echo "Building dohtml language server"
cd dothtml-basic-ls
yarn install --immutable
yarn build

cd ..
echo "Building dotvvm vscode"
cd dotvvm-vscode
yarn install --immutable
yarn build
7 changes: 7 additions & 0 deletions src/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ echo "Building tree sitter"
yarn install --immutable
yarn build

cd ..
cd native-lib-dotvvm-spy
echo "Attempting to build .NET Native dotvvm-spy"
set +e
./build.sh
set -e

cd ..
echo "Building dohtml language server"
cd dothtml-basic-ls
Expand Down
5 changes: 4 additions & 1 deletion src/dothtml-basic-ls/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
},
"pkg": {
"assets": [
"node_modules/lib-dotvvm-spy/build/hack/node_modules/dotnet-analyzer-lib-node/libDotvvmSpy.so",
"node_modules/lib-dotvvm-spy/build/hack/node_modules/dotnet-analyzer-lib-node/dotnet-analyzer-lib-node.node",
"node_modules/web-tree-sitter/tree-sitter.wasm",
"node_modules/tree-sitter-dotvvm/tree-sitter-dotvvm.wasm",
"./data/*.json",
Expand Down Expand Up @@ -55,7 +57,7 @@
"@types/estree": "^0.0.42",
"@types/lodash": "^4.14.116",
"@types/mocha": "^9.1.0",
"@types/node": "^13.9.0",
"@types/node": "^18.7.21",
"@types/prettier": "^2.2.3",
"@types/sinon": "^7.5.2",
"cross-env": "^7.0.2",
Expand All @@ -70,6 +72,7 @@
"chokidar": "^3.4.1",
"estree-walker": "^2.0.1",
"fast-glob": "^3.2.7",
"lib-dotvvm-spy": "portal:../native-lib-dotvvm-spy/node_binding",
"lodash": "^4.17.21",
"tree-sitter-dotvvm": "portal:../tree-sitter-dotvvm",
"tree-sitter-javascript": "^0.19.0",
Expand Down
134 changes: 134 additions & 0 deletions src/dothtml-basic-ls/src/lib/dllSeeker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { FSWatcher, watch } from "chokidar";
import fs from 'fs'
import path from 'path'
import fsp from 'fs/promises'
import glob from 'fast-glob'
import { timeStamp } from "console";
import type { AnalyzerContext } from "lib-dotvvm-spy";
import { Logger } from "../logger";
import { unique } from "../utils";
import _ from "lodash";
import { parseTypeName } from "./dotnetUtils";


var dotvvmspy: undefined | typeof import("lib-dotvvm-spy");
if (process.arch != "x64" || !["win32", "linux"].includes(process.platform)) {
Logger.error(`Cannot start lib-dotvvm-spy on ${process.platform}-${process.arch}, it currently only works on x64 Linux/Windows. Some features will be disabled.`);
}
else if (process.env["DOTVVM_LS_DISABLE_DOTNET"]) {
// mainly for testing that LS works even without this.
Logger.error(`DOTVVM_LS_DISABLE_DOTNET is set, some features will be disabled.`)
}
else {
try {
dotvvmspy = require('lib-dotvvm-spy');

if (dotvvmspy?.sanityCheck() !== true) {
throw new Error("Sanity check didn't return true");
}
} catch(e) {
Logger.error("lib-dotvvm-spy doesn't work, some features will be disabled", e);
}
}

export class DllSeeker {
watchers: { [key: string]: FSWatcher } = {}

analyzers: { [project: string]: AnalyzerContext } = {}

static get analyzerOk() { return !!dotvvmspy }

constructor() {
}

public async searchProject(dir: string) {
if (!dotvvmspy) { return }

dir = dir.replace('\\', '/').replace(/\/\/+|\/*$/, '/')

if (Object.keys(this.watchers).some(w => dir.startsWith(w)) ||
Object.keys(this.analyzers).some(w => w.startsWith(dir))) {
return
}

const projectFiles = await glob('*.*proj', { cwd: dir, onlyFiles: true, absolute: true })
// if we find a dll with the project name, it will be out main assembly
const projectNames = projectFiles.map(f => path.basename(f).replace(/\.\w*proj$/, ''))

// const files = fsp.readdir(path)
// go into bin directory if that makes sense
let binDirectory = dir
if (await fsp.stat(binDirectory + "bin") && (await glob('*.dll', { cwd: binDirectory, onlyFiles: true })).length == 0) {
binDirectory = binDirectory + "bin/"
}

const dlls = await glob('**/*.dll', { cwd: dir, onlyFiles: true, absolute: true })

Logger.log("Searching for dlls in " + dir, ", found", dlls.length)

if (dlls.length == 0) {
return
}

const dllPaths = unique(dlls.map(f => path.dirname(f)))
const dllsWithProjectName = dlls.filter(d => projectNames.some(p => d.endsWith(p + ".dll")))

if (dllsWithProjectName.length > 0) {
// newest of the found dlls (to prefer which configuration got compiled last)
const mainAssembly = _.maxBy(dllsWithProjectName, d => fs.statSync(d).mtime)!
Logger.log("Found dll with project name", mainAssembly, "creating AnalyzerContext")
const analyzer = new dotvvmspy.AnalyzerContext(mainAssembly, dllPaths)

this.analyzers[dir] = analyzer

// TODO: reload when changes
return
}

Logger.error("Could not find main assembly for project " + dir)
}

findImplementations(type: string, options: SymbolSearchFlags & { limit?: number }) {
if (!dotvvmspy) { return [] }

const analyzers = Object.values(this.analyzers)
return unique(analyzers.flatMap(a => {
const r = a.findImplementations(type, flagsToInt(options), options.limit ?? 1_000_000)

if (r.length == 0) {
Logger.log("No implementations found for", type, "in", a.mainAssembly)
}
return r.filter(r => !!r)
}))
}

dispose() {
for (const watcher of Object.values(this.watchers)) {
watcher.close()
}
for (const analyzer of Object.values(this.analyzers)) {
analyzer.dispose()
}
this.watchers = {}
this.analyzers = {}
}
}

function flagsToInt(flags: SymbolSearchFlags): number {
return (flags.public ? 1 : 0) |
(flags.protected ? 2 : 0) |
(flags.internal ? 4 : 0) |
(flags.static ? 8 : 0) |
(flags.instance ? 0x10 : 0) |
(flags.nonAbstract ? 0x20 : 0) |
0;
}

type SymbolSearchFlags = {
public?: boolean
protected?: boolean
internal?: boolean
static?: boolean
instance?: boolean
nonAbstract?: boolean
}
26 changes: 24 additions & 2 deletions src/dothtml-basic-ls/src/lib/documents/Document.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import { urlToPath } from '../../utils';
import { WritableDocument } from './DocumentBase';
import { extractScriptTags, extractStyleTag, extractTemplateTag, TagInformation } from './utils';
import { parseHtml } from './parseHtml';
import { HTMLDocument } from 'vscode-html-languageservice';
import { Position, Range } from 'vscode-languageserver';
import { AttributeNode, HtmlElementNode, ScriptElementNode, StyleElementNode, SyntaxNode } from 'tree-sitter-dotvvm';
import type { AttributeNode, HtmlElementNode, ScriptElementNode, StyleElementNode } from 'tree-sitter-dotvvm';
import { typeAncestor } from '../parserutils';
import { Logger } from '../../logger';

export type DothtmlSublanguage =
| { lang: 'html' }
| { lang: 'dotvvm-specific' }
| { lang: 'css' | 'js', range: [ number, number ], element: HtmlElementNode | ScriptElementNode | StyleElementNode | null, attribute?: AttributeNode | null }


export type DotvvmDocumentType = "control" | "page" | "masterpage"
/**
* Represents a text document contains a svelte component.
*/
Expand Down Expand Up @@ -116,4 +118,24 @@ export class DotvvmDocument extends WritableDocument {
getURL() {
return this.url;
}

get dotvvmType(): DotvvmDocumentType {
if (this.path?.endsWith(".dotmaster") || this.path?.endsWith(".dotlayout")) {
return "masterpage"
}

const directives =
this.tree?.rootNode.descendantsOfType("directives")?.[0]
if (!directives) {
Logger.log("No directives found in document?", this.path)
}
Logger.log(directives?.toString())
const hasControlDirectives = directives && (
directives.baseTypeNodes.length > 0 ||
directives.propertyNodes.length > 0)
if (this.path?.endsWith(".dotcontrol") || hasControlDirectives) {
return "control"
}
return "page"
}
}
23 changes: 23 additions & 0 deletions src/dothtml-basic-ls/src/lib/documents/DocumentBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,29 @@ export abstract class ReadableDocument implements TextDocument {
get lineCount(): number {
return this.getText().split(/\r?\n/).length;
}

getLastNonWhitespaceIndex(startIndex: number) {
const text = this.getText();
for (let i = startIndex; i >= 0; i--) {
if (!/\s/.test(text[i])) return i
}
return 0
}
isAtLineEnd(offset: number) {
const text = this.getText();
while (text[offset] == ' ' || text[offset] == '\t') {
offset++
}
return text[offset] == '\n' || text[offset] == '\r' || offset == text.length
}
isAtLineStart(offset: number) {
offset = offset - 1
const text = this.getText();
while (text[offset] == ' ' || text[offset] == '\t') {
offset--
}
return text[offset] == '\n' || text[offset] == '\r' || offset == 0
}
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/dothtml-basic-ls/src/lib/dotvvmControlResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ export function resolveControlOrProperty(

property: do {// break target
const parentElement = typeAncestor("html_element", element.parent)
Logger.log("considering html_element ", node.text, "to be a inner element property")
// Logger.log("considering html_element ", node, "to be a inner element property")
if (parentElement == null) break property

const parentName = elementName(parentElement)
Expand Down
Loading