Skip to content

Commit

Permalink
lsp: impr. code and fix finalization of singleDocumentDiagnostics
Browse files Browse the repository at this point in the history
  • Loading branch information
GraphR00t committed May 16, 2024
1 parent f3af947 commit a54d7be
Show file tree
Hide file tree
Showing 14 changed files with 198 additions and 152 deletions.
4 changes: 2 additions & 2 deletions internal/projectserver/code_action.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type codeActionsParam struct {
diagnostics []defines.Diagnostic
codeRange defines.Range
doc defines.TextDocumentIdentifier
fpath string
fpath absoluteFilePath

rpcSession *jsonrpc.Session
fls *Filesystem
Expand All @@ -27,7 +27,7 @@ type codeActionsParam struct {

func getCodeActions(params codeActionsParam) (*[]defines.CodeAction, error) {

chunk, err := core.ParseFileChunk(params.fpath, params.fls, parse.ParserOptions{
chunk, err := core.ParseFileChunk(string(params.fpath), params.fls, parse.ParserOptions{
Timeout: SINGLE_FILE_PARSING_TIMEOUT,
})

Expand Down
2 changes: 1 addition & 1 deletion internal/projectserver/completions.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func handleCompletion(ctx context.Context, req *defines.CompletionParams) (resul
}

// getCompletions gets the completions for a specific position in an Inox code file.
func getCompletions(fpath string, line, column int32, rpcSession *jsonrpc.Session, memberAuthToken string) ([]codecompletion.Completion, *parse.ParsedChunkSource, bool) {
func getCompletions(fpath absoluteFilePath, line, column int32, rpcSession *jsonrpc.Session, memberAuthToken string) ([]codecompletion.Completion, *parse.ParsedChunkSource, bool) {
//----------------------------------------------------
session := getCreateLockedProjectSession(rpcSession)

Expand Down
100 changes: 58 additions & 42 deletions internal/projectserver/document_diagnostics.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ func computeDocumentDiagnostics(params diagnosticNotificationParams) (result *si
return nil, err
}

fileExtension := filepath.Ext(fpath)
fileExtension := filepath.Ext(string(fpath))

switch fileExtension {
case inoxconsts.INOXLANG_FILE_EXTENSION:
Expand All @@ -183,7 +183,10 @@ func computeDocumentDiagnostics(params diagnosticNotificationParams) (result *si
}
}

func computeInoxFileDiagnostics(params diagnosticNotificationParams, fpath string) (result *singleDocumentDiagnostics, _ error) {
func computeInoxFileDiagnostics(params diagnosticNotificationParams, fpath absoluteFilePath) (result *singleDocumentDiagnostics, _ error) {
startTime := time.Now()
fpathS := string(fpath)

session, _, usingInoxFS, fls, project, memberAuthToken :=
params.rpcSession, params.docURI, params.usingInoxFS, params.fls, params.project, params.memberAuthToken

Expand All @@ -194,28 +197,16 @@ func computeInoxFileDiagnostics(params diagnosticNotificationParams, fpath strin
})

defer func() {
if result != nil {
result.finalize(fpath, startTime, params.rpcSession)
}

go func() {
defer utils.Recover()
handlingCtx.CancelGracefully()
}()
}()

defer func() {
if result != nil {
//Finalize the result.
result.id = MakeDocDiagnosticId(fpath)
if result.items == nil {
//Make sure the items are serialized as an empty array, not 'null'.
result.items = []defines.Diagnostic{}
}

// Save the result in the session.
projSession := getCreateLockedProjectSession(params.rpcSession)
defer projSession.lock.Unlock()
projSession.documentDiagnostics[fpath] = result
}
}()

preparationResult, ok := prepareSourceFileInExtractionMode(handlingCtx, filePreparationParams{
fpath: fpath,
requiresState: false,
Expand Down Expand Up @@ -279,7 +270,7 @@ func computeInoxFileDiagnostics(params diagnosticNotificationParams, fpath strin
Range: rangeToLspRange(pos),
}

if pos.SourceName == fpath {
if pos.SourceName == fpathS {
diagnostics = append(diagnostics, diagnostic)
} else if uriErr == nil {
otherDocumentDiagnostics[docURI] = append(otherDocumentDiagnostics[docURI], diagnostic)
Expand All @@ -295,7 +286,7 @@ func computeInoxFileDiagnostics(params diagnosticNotificationParams, fpath strin
if state.PrenitStaticCheckErrors != nil {
for _, err := range state.PrenitStaticCheckErrors {

pos := getPositionInPositionStackOrFirst(err.Location, fpath)
pos := getPositionInPositionStackOrFirst(err.Location, fpathS)
docURI, uriErr := getFileURI(pos.SourceName, usingInoxFS)

diagnostic := defines.Diagnostic{
Expand All @@ -304,7 +295,7 @@ func computeInoxFileDiagnostics(params diagnosticNotificationParams, fpath strin
Range: rangeToLspRange(pos),
}

if pos.SourceName == fpath {
if pos.SourceName == fpathS {
diagnostics = append(diagnostics, diagnostic)
} else if uriErr == nil {
otherDocumentDiagnostics[docURI] = append(otherDocumentDiagnostics[docURI], diagnostic)
Expand All @@ -322,13 +313,13 @@ func computeInoxFileDiagnostics(params diagnosticNotificationParams, fpath strin

if errors.As(state.MainPreinitError, &locatedEvalError) {
msg = locatedEvalError.Message
pos = getPositionInPositionStackOrFirst(locatedEvalError.Location, fpath)
pos = getPositionInPositionStackOrFirst(locatedEvalError.Location, fpathS)
_range = rangeToLspRange(pos)
} else {
_range = firstCharsLspRange(5)
msg = state.MainPreinitError.Error()
pos = parse.SourcePositionRange{
SourceName: fpath,
SourceName: fpathS,
StartLine: 1,
StartColumn: 1,
EndLine: 1,
Expand All @@ -344,7 +335,7 @@ func computeInoxFileDiagnostics(params diagnosticNotificationParams, fpath strin
Range: _range,
}

if pos.SourceName == fpath {
if pos.SourceName == fpathS {
diagnostics = append(diagnostics, diagnostic)
} else if uriErr == nil {
otherDocumentDiagnostics[docURI] = append(otherDocumentDiagnostics[docURI], diagnostic)
Expand All @@ -360,7 +351,7 @@ func computeInoxFileDiagnostics(params diagnosticNotificationParams, fpath strin
//Add static check errors.

for _, err := range state.StaticCheckData.Errors() {
pos := getPositionInPositionStackOrFirst(err.Location, fpath)
pos := getPositionInPositionStackOrFirst(err.Location, fpathS)
docURI, uriErr := getFileURI(pos.SourceName, usingInoxFS)

diagnostic := defines.Diagnostic{
Expand All @@ -369,7 +360,7 @@ func computeInoxFileDiagnostics(params diagnosticNotificationParams, fpath strin
Range: rangeToLspRange(pos),
}

if pos.SourceName == fpath {
if pos.SourceName == fpathS {
diagnostics = append(diagnostics, diagnostic)
} else if uriErr == nil {
otherDocumentDiagnostics[docURI] = append(otherDocumentDiagnostics[docURI], diagnostic)
Expand All @@ -378,7 +369,7 @@ func computeInoxFileDiagnostics(params diagnosticNotificationParams, fpath strin

//Add static check warnings.
for _, warning := range state.StaticCheckData.Warnings() {
pos := getPositionInPositionStackOrFirst(warning.Location, fpath)
pos := getPositionInPositionStackOrFirst(warning.Location, fpathS)
docURI, uriErr := getFileURI(pos.SourceName, usingInoxFS)

diagnostic := defines.Diagnostic{
Expand All @@ -387,7 +378,7 @@ func computeInoxFileDiagnostics(params diagnosticNotificationParams, fpath strin
Range: rangeToLspRange(pos),
}

if pos.SourceName == fpath {
if pos.SourceName == fpathS {
diagnostics = append(diagnostics, diagnostic)
} else if uriErr == nil {
otherDocumentDiagnostics[docURI] = append(otherDocumentDiagnostics[docURI], diagnostic)
Expand All @@ -397,7 +388,7 @@ func computeInoxFileDiagnostics(params diagnosticNotificationParams, fpath strin
//Add symbolic check errors.

for _, err := range state.SymbolicData.Errors() {
pos := getPositionInPositionStackOrFirst(err.Location, fpath)
pos := getPositionInPositionStackOrFirst(err.Location, fpathS)
docURI, uriErr := getFileURI(pos.SourceName, usingInoxFS)

if uriErr == nil {
Expand All @@ -410,7 +401,7 @@ func computeInoxFileDiagnostics(params diagnosticNotificationParams, fpath strin
Range: rangeToLspRange(pos),
}

if pos.SourceName == fpath {
if pos.SourceName == fpathS {
diagnostics = append(diagnostics, diagnostic)
} else if uriErr == nil {
otherDocumentDiagnostics[docURI] = append(otherDocumentDiagnostics[docURI], diagnostic)
Expand All @@ -419,7 +410,7 @@ func computeInoxFileDiagnostics(params diagnosticNotificationParams, fpath strin

//Add symbolic check warnings.
for _, warning := range state.SymbolicData.Warnings() {
pos := getPositionInPositionStackOrFirst(warning.Location, fpath)
pos := getPositionInPositionStackOrFirst(warning.Location, fpathS)
docURI, uriErr := getFileURI(pos.SourceName, usingInoxFS)

diagnostic := defines.Diagnostic{
Expand All @@ -428,7 +419,7 @@ func computeInoxFileDiagnostics(params diagnosticNotificationParams, fpath strin
Range: rangeToLspRange(pos),
}

if pos.SourceName == fpath {
if pos.SourceName == fpathS {
diagnostics = append(diagnostics, diagnostic)
} else if uriErr == nil {
otherDocumentDiagnostics[docURI] = append(otherDocumentDiagnostics[docURI], diagnostic)
Expand All @@ -443,7 +434,16 @@ func computeInoxFileDiagnostics(params diagnosticNotificationParams, fpath strin
}, nil
}

func computeHyperscriptFileDiagnostics(params diagnosticNotificationParams, fpath string) (result *singleDocumentDiagnostics, _ error) {
func computeHyperscriptFileDiagnostics(params diagnosticNotificationParams, fpath absoluteFilePath) (result *singleDocumentDiagnostics, _ error) {
startTime := time.Now()
fpathS := string(fpath)

defer func() {
if result != nil {
result.finalize(fpath, startTime, params.rpcSession)
}
}()

session, _, fls, hyperscriptFileCache :=
params.rpcSession, params.docURI, params.fls, params.hyperscriptParseCache

Expand All @@ -458,18 +458,18 @@ func computeHyperscriptFileDiagnostics(params diagnosticNotificationParams, fpat

var sourceCode string
{
content, err := util.ReadFile(fls, fpath)
content, err := util.ReadFile(fls, fpathS)
if err != nil {
return
}
sourceCode = utils.BytesAsString(content) //after this line $content should not be used
}

sourceFile := sourcecode.File{
NameString: fpath,
UserFriendlyNameString: fpath,
Resource: fpath,
ResourceDir: filepath.Dir(fpath),
NameString: fpathS,
UserFriendlyNameString: fpathS,
Resource: fpathS,
ResourceDir: filepath.Dir(fpathS),
CodeString: sourceCode,
}
parsedFile, criticalErr := hsparse.ParseFile(sessionCtx, sourceFile, hyperscriptFileCache, nil)
Expand All @@ -479,7 +479,7 @@ func computeHyperscriptFileDiagnostics(params diagnosticNotificationParams, fpat
}

if parsedFile.Error != nil {
location := hscode.MakePositionFromParsingError(parsedFile.Error, fpath)
location := hscode.MakePositionFromParsingError(parsedFile.Error, fpathS)
result.items = append(result.items, defines.Diagnostic{
Range: rangeToLspRange(location),
Severity: &errSeverity,
Expand Down Expand Up @@ -539,19 +539,35 @@ type DocDiagnosticId string

// MakeDocDiagnosticId returns a DocDiagnosticId for the document at $absPath.
// The time of the ULID part is the current time.
func MakeDocDiagnosticId(absPath string) DocDiagnosticId {
return DocDiagnosticId(ulid.Make().String() + "-" + absPath)
func MakeDocDiagnosticId(absPath absoluteFilePath) DocDiagnosticId {
return DocDiagnosticId(ulid.Make().String() + "-" + string(absPath))
}

// A singleDocumentDiagnostics contains the diagnostics of a single Inox or Hyperscript document.
type singleDocumentDiagnostics struct {
id DocDiagnosticId
startTime time.Time
items []defines.Diagnostic
containsWorkspaceDiagnostics bool
lock sync.Mutex

//Inox-specific fields
//Fields specific to Inox files.

otherDocumentDiagnostics map[defines.DocumentUri][]defines.Diagnostic
symbolicErrors map[defines.DocumentUri][]symbolic.EvaluationError
}

func (d *singleDocumentDiagnostics) finalize(fpath absoluteFilePath, computeStart time.Time, rpcSession *jsonrpc.Session) {
d.id = MakeDocDiagnosticId(fpath)
d.startTime = computeStart

if d.items == nil {
//Make sure the items are serialized as an empty array, not 'null'.
d.items = []defines.Diagnostic{}
}

// Save $d in the session.
projSession := getCreateLockedProjectSession(rpcSession)
defer projSession.lock.Unlock()
projSession.documentDiagnostics[fpath] = d
}
23 changes: 16 additions & 7 deletions internal/projectserver/document_uri.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func normalizeURI[S ~string](uri S) S {
return S(scheme + ":///" + afterColonSlash)
}

func getFileURI(path string, usingInoxFs bool) (defines.DocumentUri, error) {
func getFileURI[P ~string](path P, usingInoxFs bool) (defines.DocumentUri, error) {
if path == "" {
return "", errors.New("failed to get document URI: empty path")
}
Expand All @@ -39,8 +39,8 @@ func getFileURI(path string, usingInoxFs bool) (defines.DocumentUri, error) {
return defines.DocumentUri("file://" + path), nil
}

// getPath returns a clean path from a URI.
func getPath(uri defines.URI, usingInoxFS bool) (string, error) {
// getPath returns a clean absolute path from a URI.
func getPath(uri defines.URI, usingInoxFS bool) (absoluteFilePath, error) {
u, err := url.Parse(string(uri))
if err != nil {
return "", fmt.Errorf("invalid URI: %s: %w", uri, err)
Expand All @@ -51,11 +51,11 @@ func getPath(uri defines.URI, usingInoxFS bool) (string, error) {
if !usingInoxFS && u.Scheme != "file" {
return "", fmt.Errorf("%w, actual is: %s", ErrFileURIExpected, string(uri))
}
return filepath.Clean(u.Path), nil
return absoluteFilePath(filepath.Clean(u.Path)), nil
}

// getPath returns a clean path from a URI, it also checks that the file extension is `.ix` or `._hs`.
func getSupportedFilePath(uri defines.DocumentUri, usingInoxFs bool) (string, error) {
// getPath returns a clean path from a document URI, it also checks that the file extension is `.ix` or `._hs`.
func getSupportedFilePath(uri defines.DocumentUri, usingInoxFs bool) (absoluteFilePath, error) {
u, err := url.Parse(string(uri))

if err != nil {
Expand All @@ -72,5 +72,14 @@ func getSupportedFilePath(uri defines.DocumentUri, usingInoxFs bool) (string, er
if !strings.HasSuffix(clean, inoxconsts.INOXLANG_FILE_EXTENSION) && !strings.HasSuffix(clean, hscode.FILE_EXTENSION) {
return "", fmt.Errorf("unexpected file extension: '%s'", filepath.Ext(clean))
}
return clean, nil
return absoluteFilePath(clean), nil
}

type absoluteFilePath string

func absoluteFilePathFrom(s string) (absoluteFilePath, bool) {
if s == "" || s[0] != '/' {
return "", false
}
return absoluteFilePath(s), true
}
12 changes: 6 additions & 6 deletions internal/projectserver/edition_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ var (
type projectEditionState struct {
lock sync.Mutex

files map[string]*projectFileState
files map[absoluteFilePath]*projectFileState
}

func getCreateProjectEditionState(id core.ProjectID) *projectEditionState {
Expand All @@ -42,13 +42,13 @@ func getCreateProjectEditionState(id core.ProjectID) *projectEditionState {
}

state = &projectEditionState{
files: map[string]*projectFileState{},
files: map[absoluteFilePath]*projectFileState{},
}
projectEditionStates[id] = state
return state
}

func (s *projectEditionState) startFileUpload(fpath string, firstPart []byte, info uploadInfo, rpcSession *jsonrpc.Session) (uploadId, error) {
func (s *projectEditionState) startFileUpload(fpath absoluteFilePath, firstPart []byte, info uploadInfo, rpcSession *jsonrpc.Session) (uploadId, error) {
s.lock.Lock()
s.cleanupInactiveFilesNoLock("")
file, ok := s.files[fpath]
Expand All @@ -61,7 +61,7 @@ func (s *projectEditionState) startFileUpload(fpath string, firstPart []byte, in
return file.startFileUpload(rpcSession, firstPart, info)
}

func (s *projectEditionState) continueFileUpload(fpath string, part []byte, id uploadId, rpcSession *jsonrpc.Session) (uploadInfo, error) {
func (s *projectEditionState) continueFileUpload(fpath absoluteFilePath, part []byte, id uploadId, rpcSession *jsonrpc.Session) (uploadInfo, error) {
s.lock.Lock()
s.cleanupInactiveFilesNoLock(fpath)
file, ok := s.files[fpath]
Expand All @@ -74,7 +74,7 @@ func (s *projectEditionState) continueFileUpload(fpath string, part []byte, id u
return file.continueFileUpload(rpcSession, part, id)
}

func (s *projectEditionState) finishFileUpload(fpath string, lastPart []byte, id uploadId, rpcSession *jsonrpc.Session) ([][]byte, uploadInfo, error) {
func (s *projectEditionState) finishFileUpload(fpath absoluteFilePath, lastPart []byte, id uploadId, rpcSession *jsonrpc.Session) ([][]byte, uploadInfo, error) {
s.lock.Lock()
defer s.lock.Unlock()

Expand All @@ -95,7 +95,7 @@ func (s *projectEditionState) finishFileUpload(fpath string, lastPart []byte, id
return nil, uploadInfo{}, err
}

func (s *projectEditionState) cleanupInactiveFilesNoLock(ignoredPath string) {
func (s *projectEditionState) cleanupInactiveFilesNoLock(ignoredPath absoluteFilePath) {
for path, file := range s.files {
if (path == "" || path != ignoredPath) && time.Since(file.LastActivity()) >= PROJECT_FILE_STATE_CLEANUP_TIMEOUT {
delete(s.files, path)
Expand Down
Loading

0 comments on commit a54d7be

Please sign in to comment.