Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
47 changes: 47 additions & 0 deletions demo-output.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -980,6 +980,52 @@

matchingXML: ""
effort: 1
node-sample-rule-001:
description: Testing that the node provider works - type
category: potential
incidents:
- uri: file:///examples/nodejs/test_a.ts
message: nodejs sample rule 001
codeSnip: " 1 export interface Greeter {\n 2 name: string;\n 3 hello(): string;\n 4 }\n 5 \n 6 export const greeter: Greeter = {\n 7 name: \"Person1\",\n 8 hello() {\n 9 return `Hello, I'm ${this.name}`;\n10 },\n11 };\n"
lineNumber: 5
variables:
file: file:///examples/nodejs/test_a.ts
- uri: file:///examples/nodejs/test_b.ts
message: nodejs sample rule 001
codeSnip: " 1 import { greeter } from './test_a';\n 2 \n 3 console.log(greeter.hello());\n"
lineNumber: 0
variables:
file: file:///examples/nodejs/test_b.ts
- uri: file:///examples/nodejs/test_b.ts
message: nodejs sample rule 001
codeSnip: " 1 import { greeter } from './test_a';\n 2 \n 3 console.log(greeter.hello());\n"
lineNumber: 2
variables:
file: file:///examples/nodejs/test_b.ts
effort: 1
node-sample-rule-002:
description: Testing that the node provider works - function
category: potential
incidents:
- uri: file:///examples/nodejs/test_a.ts
message: nodejs sample rule 002
codeSnip: " 1 export interface Greeter {\n 2 name: string;\n 3 hello(): string;\n 4 }\n 5 \n 6 export const greeter: Greeter = {\n 7 name: \"Person1\",\n 8 hello() {\n 9 return `Hello, I'm ${this.name}`;\n10 },\n11 };\n"
lineNumber: 2
variables:
file: file:///examples/nodejs/test_a.ts
- uri: file:///examples/nodejs/test_a.ts
message: nodejs sample rule 002
codeSnip: " 1 export interface Greeter {\n 2 name: string;\n 3 hello(): string;\n 4 }\n 5 \n 6 export const greeter: Greeter = {\n 7 name: \"Person1\",\n 8 hello() {\n 9 return `Hello, I'm ${this.name}`;\n10 },\n11 };\n"
lineNumber: 7
variables:
file: file:///examples/nodejs/test_a.ts
- uri: file:///examples/nodejs/test_b.ts
message: nodejs sample rule 002
codeSnip: " 1 import { greeter } from './test_a';\n 2 \n 3 console.log(greeter.hello());\n"
lineNumber: 2
variables:
file: file:///examples/nodejs/test_b.ts
effort: 1
insights:
field-rule-00001:
description: Sample field declaration rule
Expand Down Expand Up @@ -1265,4 +1311,5 @@
unmatched:
- file-002
- lang-ref-002
- node-sample-rule-003
- python-sample-rule-003
5 changes: 0 additions & 5 deletions examples/nodejs/test.ts

This file was deleted.

11 changes: 11 additions & 0 deletions examples/nodejs/test_a.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export interface Greeter {
name: string;
hello(): string;
}

export const greeter: Greeter = {
name: "Person1",
hello() {
return `Hello, I'm ${this.name}`;
},
};
3 changes: 3 additions & 0 deletions examples/nodejs/test_b.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { greeter } from './test_a';

console.log(greeter.hello());
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import (

"github.com/go-logr/logr"
serverconf "github.com/konveyor/analyzer-lsp/external-providers/generic-external-provider/pkg/server_configurations"
"github.com/konveyor/analyzer-lsp/external-providers/generic-external-provider/pkg/server_configurations/generic"
"github.com/konveyor/analyzer-lsp/external-providers/generic-external-provider/pkg/server_configurations/nodejs"
"github.com/konveyor/analyzer-lsp/external-providers/generic-external-provider/pkg/server_configurations/pylsp"
"github.com/konveyor/analyzer-lsp/external-providers/generic-external-provider/pkg/server_configurations/yaml_language_server"
"github.com/konveyor/analyzer-lsp/provider"
)

Expand Down Expand Up @@ -65,25 +69,37 @@ func (p *genericProvider) Capabilities() []provider.Capability {
// a provider.ServiceClient to the original analyzer process. genericProvider
// here just sort of... doesn't matter at all
func (p *genericProvider) Init(ctx context.Context, log logr.Logger, c provider.InitConfig) (provider.ServiceClient, provider.InitConfig, error) {
// return nil, fmt.Errorf("nothing")

log.Error(fmt.Errorf("Nothing"), "Started generic provider init")
fmt.Fprintf(os.Stderr, "started generic provider init")
lspServerName, ok := c.ProviderSpecificConfig["lspServerName"].(string)
if !ok {
lspServerName = "generic"
}

// because we spawn a generic client first, before knowing which service client we will need
if p.lspServerName != lspServerName {
log.Error(fmt.Errorf("lspServerName must be the same for each instantiation of the generic-external-provider (%s != %s)", p.lspServerName, lspServerName), "Inside genericProvider init")
fmt.Fprintf(os.Stderr, "lspservername blah")
// we want to be able to set which generic provider to use by tne provider config
// because 'generic' is selected from the start, we need to update that if needed
p.lspServerName = lspServerName
// these have already been verified in NewGenericProvider() - no need to err
switch p.lspServerName {
case serverconf.GenericClient:
p.serviceClientBuilder = &generic.GenericServiceClientBuilder{}
case serverconf.PythonClient:
p.serviceClientBuilder = &pylsp.PythonServiceClientBuilder{}
case serverconf.NodeClient:
p.serviceClientBuilder = &nodejs.NodeServiceClientBuilder{}
case serverconf.YamlClient:
p.serviceClientBuilder = &yaml_language_server.YamlServiceClientBuilder{}
default:
return nil, provider.InitConfig{}, fmt.Errorf("generic client name not found")
}

return nil, provider.InitConfig{}, fmt.Errorf("lspServerName must be the same for each instantiation of the generic-external-provider (%s != %s)", p.lspServerName, lspServerName)
}

// Simple matter of calling the constructor that we set earlier to get the
// service client
sc, err := p.serviceClientBuilder.Init(ctx, log, c)

if err != nil {
log.Error(err, "ctor error")
fmt.Fprintf(os.Stderr, "ctor blah")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,22 @@ import (
"github.com/konveyor/analyzer-lsp/provider"
)

const (
GenericClient = "generic"
PythonClient = "pylsp"
YamlClient = "yaml_language_server"
NodeClient = "nodejs"
)

type ServiceClientBuilder interface {
Init(context.Context, logr.Logger, provider.InitConfig) (provider.ServiceClient, error)
GetGenericServiceClientCapabilities(log logr.Logger) []base.LSPServiceClientCapability
}

var SupportedLanguages = map[string]ServiceClientBuilder{
// "": generic.NewGenericServiceClient,
"generic": &generic.GenericServiceClientBuilder{},
"pylsp": &pylsp.PythonServiceClientBuilder{},
"yaml_language_server": &yaml.YamlServiceClientBuilder{},
"nodejs": &nodejs.NodeServiceClientBuilder{},
GenericClient: &generic.GenericServiceClientBuilder{},
PythonClient: &pylsp.PythonServiceClientBuilder{},
YamlClient: &yaml.YamlServiceClientBuilder{},
NodeClient: &nodejs.NodeServiceClientBuilder{},
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/go-logr/logr"
Expand Down Expand Up @@ -43,10 +45,6 @@ func (n *NodeServiceClientBuilder) Init(ctx context.Context, log logr.Logger, c
return nil, err
}

// Create the parameters for the `initialize` request
//
// TODO(jsussman): Support more than one folder. This hack with only taking
// the first item in WorkspaceFolders is littered throughout.
params := protocol.InitializeParams{}

if c.Location != "" {
Expand Down Expand Up @@ -136,38 +134,99 @@ func (sc *NodeServiceClient) EvaluateReferenced(ctx context.Context, cap string,
return resp{}, fmt.Errorf("unable to get query info")
}

// f, err := os.ReadFile("/path/to/test.ts")
// if err != nil {
// panic(err)
// }
// get all ts files
folder := strings.TrimPrefix(sc.Config.WorkspaceFolders[0], "file://")
var nodeFiles []string
err = filepath.Walk(folder, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}

// p := protocol.DidOpenTextDocumentParams{
// TextDocument: protocol.TextDocumentItem{
// URI: "file:///path/to/test.ts",
// LanguageID: "typescript",
// Version: 0,
// Text: string(f),
// },
// }
// err = sc.Conn.Notify(ctx, "textDocument/didOpen", p)
// if err != nil {
// panic(err)
// }
if !info.IsDir() && filepath.Ext(path) == ".ts" {
path = "file://" + path
nodeFiles = append(nodeFiles, path)
}

return nil
})

// time.Sleep(2 * time.Second)
if err != nil {
return provider.ProviderEvaluateResponse{}, err
}

symbols := sc.GetAllDeclarations(ctx, sc.BaseConfig.WorkspaceFolders, query)
didOpen := func(uri string, text []byte) error {
params := protocol.DidOpenTextDocumentParams{
TextDocument: protocol.TextDocumentItem{
URI: uri,
LanguageID: "typescript",
Version: 0,
Text: string(text),
},
}
// typescript server seems to throw "No project" error without notification
// perhaps there's a better way to do this
return sc.Conn.Notify(ctx, "textDocument/didOpen", params)
}

// fmt.Printf("symbols: %v\n", symbols)
didClose := func(uri string) error {
params := protocol.DidCloseTextDocumentParams{
TextDocument: protocol.TextDocumentIdentifier{
URI: uri,
},
}
return sc.Conn.Notify(ctx, "textDocument/didClose", params)
}

BATCH_SIZE := 32
batchRight, batchLeft := 0, 0
var symbols []protocol.WorkspaceSymbol
for batchRight < len(nodeFiles) {
for batchRight-batchLeft < BATCH_SIZE && batchRight < len(nodeFiles) {
trimmedURI := strings.TrimPrefix(nodeFiles[batchRight], "file://")
text, err := os.ReadFile(trimmedURI)
if err != nil {
return provider.ProviderEvaluateResponse{}, err
}
err = didOpen(nodeFiles[batchRight], text)
if err != nil {
return provider.ProviderEvaluateResponse{}, err
}

batchRight++
}
symbols = sc.GetAllDeclarations(ctx, sc.BaseConfig.WorkspaceFolders, query)
}

incidentsMap, err := sc.EvaluateSymbols(ctx, symbols)
if err != nil {
return resp{}, err
}

for batchLeft < batchRight {
if err = didClose(nodeFiles[batchLeft]); err != nil {
return provider.ProviderEvaluateResponse{}, err
}
batchLeft++
}

incidents := []provider.IncidentContext{}
incidentsMap := make(map[string]provider.IncidentContext) // Remove duplicates
for _, incident := range incidentsMap {
incidents = append(incidents, incident)
}
if len(incidents) == 0 {
return resp{Matched: false}, nil
}
return resp{
Matched: true,
Incidents: incidents,
}, nil
}

func (sc *NodeServiceClient) EvaluateSymbols(ctx context.Context, symbols []protocol.WorkspaceSymbol) (map[string]provider.IncidentContext, error) {
incidentsMap := make(map[string]provider.IncidentContext)

for _, s := range symbols {
references := sc.GetAllReferences(ctx, s.Location.Value.(protocol.Location))

//fmt.Printf("references: %v\n", references)

breakEarly := false
for _, ref := range references {
// Look for things that are in the location loaded,
Expand All @@ -186,14 +245,13 @@ func (sc *NodeServiceClient) EvaluateReferenced(ctx context.Context, cap string,
break
}
}

if breakEarly {
break
}

u, err := uri.Parse(ref.URI)
if err != nil {
return resp{}, err
return nil, err
}
lineNumber := int(ref.Range.Start.Line)
incident := provider.IncidentContext{
Expand All @@ -202,23 +260,16 @@ func (sc *NodeServiceClient) EvaluateReferenced(ctx context.Context, cap string,
Variables: map[string]interface{}{
"file": ref.URI,
},
CodeLocation: &provider.Location{
StartPosition: provider.Position{Line: float64(lineNumber)},
EndPosition: provider.Position{Line: float64(lineNumber)},
},
}
b, _ := json.Marshal(incident)

incidentsMap[string(b)] = incident
}
}

for _, incident := range incidentsMap {
incidents = append(incidents, incident)
}

// No results were found.
if len(incidents) == 0 {
return resp{Matched: false}, nil
}
return resp{
Matched: true,
Incidents: incidents,
}, nil
return incidentsMap, nil
}
Loading
Loading