Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 2 additions & 0 deletions _extension/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
readNativePreviewConfig,
} from "./util";
import { getLanguageForUri } from "./util";
import { workspaceSymbolSendRequestMiddleware } from "./workspaceSymbolMiddleware";

export class Client implements vscode.Disposable {
private outputChannel: vscode.LogOutputChannel;
Expand Down Expand Up @@ -97,6 +98,7 @@ export class Client implements vscode.Disposable {
},
},
sendNotification: sendNotificationMiddleware,
sendRequest: workspaceSymbolSendRequestMiddleware,
provideHover: () => undefined,
},
diagnosticCollectionName: "typescript-push",
Expand Down
40 changes: 40 additions & 0 deletions _extension/src/workspaceSymbolMiddleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import * as vscode from "vscode";
import type { CancellationToken } from "vscode";
import type { MessageSignature } from "vscode-languageserver-protocol";
import {
disabledSchemes,
isSupportedLanguageMode,
} from "./util";

function getDocument(): vscode.TextDocument | undefined {
const activeDocument = vscode.window.activeTextEditor?.document;
if (activeDocument && isSupportedLanguageMode(activeDocument) && !disabledSchemes.has(activeDocument.uri.scheme)) {
Comment thread
gabritto marked this conversation as resolved.
Outdated
return activeDocument;
}

return vscode.workspace.textDocuments.find(
document => isSupportedLanguageMode(document) && !disabledSchemes.has(document.uri.scheme),
);
}

export function workspaceSymbolSendRequestMiddleware<P, R>(
type: string | MessageSignature,
params: P | undefined,
token: CancellationToken | undefined,
next: (type: string | MessageSignature, params?: P, token?: CancellationToken) => Promise<R>,
): Promise<R> {
const method = typeof type === "string" ? type : type.method;
if (method !== "workspace/symbol") {
return next(type, params, token);
}

const document = getDocument();
if (!document) {
return next(type, params, token);
}

return next(type, {
...params,
textDocument: { uri: document.uri.toString() },
} as P, token);
}
11 changes: 10 additions & 1 deletion internal/ls/lsutil/userpreferences.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func NewDefaultUserPreferences() UserPreferences {
ReportStyleChecksAsWarnings: core.TSTrue,

ExcludeLibrarySymbolsInNavTo: core.TSTrue,
WorkspaceSymbolsScope: WorkspaceSymbolsScopeAllOpenProjects,
}
}

Expand Down Expand Up @@ -167,7 +168,8 @@ type UserPreferences struct {

// ------- Symbols -------

ExcludeLibrarySymbolsInNavTo core.Tristate `raw:"excludeLibrarySymbolsInNavTo" config:"workspaceSymbols.excludeLibrarySymbols"`
ExcludeLibrarySymbolsInNavTo core.Tristate `raw:"excludeLibrarySymbolsInNavTo" config:"workspaceSymbols.excludeLibrarySymbols"`
WorkspaceSymbolsScope WorkspaceSymbolsScope `config:"workspaceSymbols.scope"`

// ------- Misc -------

Expand Down Expand Up @@ -227,6 +229,13 @@ type CodeLensUserPreferences struct {

type QuotePreference string

type WorkspaceSymbolsScope string

const (
WorkspaceSymbolsScopeAllOpenProjects WorkspaceSymbolsScope = "allOpenProjects"
WorkspaceSymbolsScopeCurrentProject WorkspaceSymbolsScope = "currentProject"
)

const (
QuotePreferenceUnknown QuotePreference = ""
QuotePreferenceAuto QuotePreference = "auto"
Expand Down
4 changes: 3 additions & 1 deletion internal/ls/lsutil/userpreferences_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,13 +239,15 @@ func TestUserPreferencesParseUnstable(t *testing.T) {
"importModuleSpecifier": "relative"
},
"workspaceSymbols": {
"excludeLibrarySymbols": true
"excludeLibrarySymbols": true,
"scope": "currentProject"
}
}`,
expected: UserPreferences{
DisplayPartsForJSDoc: core.TSTrue,
ImportModuleSpecifierPreference: modulespecifiers.ImportModuleSpecifierPreferenceRelative,
ExcludeLibrarySymbolsInNavTo: core.TSTrue,
WorkspaceSymbolsScope: WorkspaceSymbolsScopeCurrentProject,
},
},
{
Expand Down
11 changes: 11 additions & 0 deletions internal/lsp/lsproto/_generate/generate.mts
Original file line number Diff line number Diff line change
Expand Up @@ -1051,6 +1051,17 @@ function patchAndPreprocessModel() {
});
}

// Patch WorkspaceSymbolParams to optionally scope the search to projects
// containing a document, matching Strada's currentProject mode.
if (structure.name === "WorkspaceSymbolParams") {
structure.properties.push({
name: "textDocument",
type: { kind: "reference", name: "TextDocumentIdentifier" },
optional: true,
documentation: "Scopes the workspace symbol search to projects containing this document.",
});
}

// Patch Hover to add canIncreaseVerbosity
if (structure.name === "Hover") {
structure.properties.push(
Expand Down
3 changes: 3 additions & 0 deletions internal/lsp/lsproto/lsp_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 14 additions & 3 deletions internal/lsp/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1704,17 +1704,28 @@ func (s *Server) handleDocumentOnTypeFormat(ctx context.Context, ls *ls.Language
func (s *Server) handleWorkspaceSymbol(ctx context.Context, params *lsproto.WorkspaceSymbolParams, reqMsg *lsproto.RequestMessage) (lsproto.WorkspaceSymbolResponse, error) {
var resp lsproto.WorkspaceSymbolResponse
var lsErr error
s.session.WithSnapshotLoadingProjectTree(ctx, nil, func(snapshot *project.Snapshot) {
provideSymbols := func(snapshot *project.Snapshot, programs []*compiler.Program) {
defer s.recover(reqMsg)
programs := core.Map(snapshot.ProjectCollection.Projects(), (*project.Project).GetProgram)
resp, lsErr = ls.ProvideWorkspaceSymbols(
ctx,
programs,
snapshot.Converters(),
snapshot.UserPreferences(),
params.Query,
)
})
}
if params.TextDocument != nil && s.session.Config().WorkspaceSymbolsScope == lsutil.WorkspaceSymbolsScopeCurrentProject {
uri := params.TextDocument.Uri
s.session.WithSnapshotForDocument(ctx, uri, func(snapshot *project.Snapshot) {
programs := core.Map(snapshot.GetProjectsContainingFile(uri), ls.Project.GetProgram)
provideSymbols(snapshot, programs)
})
} else {
s.session.WithSnapshotLoadingProjectTree(ctx, nil, func(snapshot *project.Snapshot) {
programs := core.Map(snapshot.ProjectCollection.Projects(), (*project.Project).GetProgram)
provideSymbols(snapshot, programs)
})
}
return resp, lsErr
}

Expand Down
84 changes: 84 additions & 0 deletions internal/lsp/server_workspace_symbol_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package lsp_test

import (
"context"
"io"
"testing"

"github.com/microsoft/typescript-go/internal/bundled"
"github.com/microsoft/typescript-go/internal/ls/lsconv"
"github.com/microsoft/typescript-go/internal/lsp"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
"github.com/microsoft/typescript-go/internal/testutil/lsptestutil"
"github.com/microsoft/typescript-go/internal/vfs/vfstest"
"gotest.tools/v3/assert"
)

func TestWorkspaceSymbolsCurrentProject(t *testing.T) {
Comment thread
gabritto marked this conversation as resolved.
Outdated
t.Parallel()
if !bundled.Embedded {
t.Skip("bundled files are not embedded")
}

files := map[string]string{
"/home/projects/a/tsconfig.json": `{}`,
"/home/projects/a/index.ts": `export function fromA() {}`,
"/home/projects/b/tsconfig.json": `{}`,
"/home/projects/b/index.ts": `export function fromB() {}`,
}
fs := bundled.WrapFS(vfstest.FromMap(files, false))
onServerRequest := func(_ context.Context, req *lsproto.RequestMessage) *lsproto.ResponseMessage {
if req.Method == lsproto.MethodClientRegisterCapability || req.Method == lsproto.MethodClientUnregisterCapability {
return &lsproto.ResponseMessage{ID: req.ID, JSONRPC: req.JSONRPC, Result: lsproto.Null{}}
}
return nil
}
client, closeClient := lsptestutil.NewLSPClient(t, lsp.ServerOptions{
Err: io.Discard,
Cwd: "/home/projects",
FS: fs,
DefaultLibraryPath: bundled.LibPath(),
}, onServerRequest)
t.Cleanup(func() { _ = closeClient() })

initMsg, _, ok := lsptestutil.SendRequest(t, client, lsproto.InitializeInfo, &lsproto.InitializeParams{
Capabilities: &lsproto.ClientCapabilities{},
})
assert.Assert(t, ok && initMsg.AsResponse().Error == nil, "Initialize failed")
lsptestutil.SendNotification(t, client, lsproto.InitializedInfo, &lsproto.InitializedParams{})
<-client.Server.InitComplete()

for _, file := range []string{"/home/projects/a/index.ts", "/home/projects/b/index.ts"} {
lsptestutil.SendNotification(t, client, lsproto.TextDocumentDidOpenInfo, &lsproto.DidOpenTextDocumentParams{
TextDocument: &lsproto.TextDocumentItem{
Uri: lsconv.FileNameToDocumentURI(file),
LanguageId: lsproto.LanguageKindTypeScript,
Version: 1,
Text: files[file],
},
})
}

workspaceSymbolParams := &lsproto.WorkspaceSymbolParams{
Query: "from",
TextDocument: &lsproto.TextDocumentIdentifier{Uri: lsconv.FileNameToDocumentURI("/home/projects/a/index.ts")},
}
_, allProjects, ok := lsptestutil.SendRequest(t, client, lsproto.WorkspaceSymbolInfo, workspaceSymbolParams)
assert.Assert(t, ok)
assert.Equal(t, len(*allProjects.SymbolInformations), 2)

lsptestutil.SendNotification(t, client, lsproto.WorkspaceDidChangeConfigurationInfo, &lsproto.DidChangeConfigurationParams{
Settings: map[string]any{
"js/ts": map[string]any{
"workspaceSymbols": map[string]any{
"scope": "currentProject",
},
},
},
})

_, currentProject, ok := lsptestutil.SendRequest(t, client, lsproto.WorkspaceSymbolInfo, workspaceSymbolParams)
assert.Assert(t, ok)
assert.Equal(t, len(*currentProject.SymbolInformations), 1)
assert.Equal(t, (*currentProject.SymbolInformations)[0].Name, "fromA")
}
14 changes: 14 additions & 0 deletions internal/project/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -1132,6 +1132,20 @@ func (s *Session) WithSnapshotLoadingProjectTree(
fn(snapshot)
}

func (s *Session) WithSnapshotForDocument(
ctx context.Context,
uri lsproto.DocumentUri,
fn func(*Snapshot),
) {
snapshot := s.getSnapshot(
ctx,
ResourceRequest{Documents: []lsproto.DocumentUri{uri}},
true, /*callerRef*/
)
defer snapshot.Deref(s)
fn(snapshot)
}

// GetCurrentLanguageServiceWithAutoImports flushes pending file changes, clones the
// current snapshot with auto-import preparation for the given URI, then returns a
// LanguageService for the default project. Use this only outside of request handling
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,8 @@ Config::
"enabled": true
},
"workspaceSymbols": {
"excludeLibrarySymbols": true
"excludeLibrarySymbols": true,
"scope": "allOpenProjects"
}
}
}
Expand Down