Skip to content

Commit e391e41

Browse files
Copilotjakebailey
andauthored
Support disabling ATA via VS Code settings for inferred projects (#3584)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
1 parent eb04840 commit e391e41

4 files changed

Lines changed: 186 additions & 1 deletion

File tree

internal/ls/lsutil/userpreferences.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,12 +168,31 @@ type UserPreferences struct {
168168
DisplayPartsForJSDoc core.Tristate `raw:"displayPartsForJSDoc"` // !!!
169169
ReportStyleChecksAsWarnings core.Tristate `raw:"reportStyleChecksAsWarnings"` // !!! If this changes, we need to ask the client to recompute diagnostics
170170

171+
// ------- ATA -------
172+
173+
// DisableAutomaticTypeAcquisition is the deprecated setting from typescript.disableAutomaticTypeAcquisition.
174+
DisableAutomaticTypeAcquisition core.Tristate `raw:"disableAutomaticTypeAcquisition" config:"disableAutomaticTypeAcquisition"`
175+
// AutomaticTypeAcquisitionEnabled is the unified setting from tsserver.automaticTypeAcquisition.enabled under the js/ts section.
176+
// When set, it takes precedence over DisableAutomaticTypeAcquisition.
177+
AutomaticTypeAcquisitionEnabled core.Tristate `raw:"automaticTypeAcquisitionEnabled" config:"tsserver.automaticTypeAcquisition.enabled"`
178+
// TODO: add tsserver.web.typeAcquisition.enabled under the js/ts section for the web variant when web support is implemented.
179+
171180
// ------- Project Configuration -------
172181

173182
// CustomConfigFileName specifies a custom config file name to use before defaulting to tsconfig.json/jsconfig.json.
174183
CustomConfigFileName string `raw:"customConfigFileName" config:"native-preview.customConfigFileName"`
175184
}
176185

186+
// IsATADisabled returns whether Automatic Type Acquisition is disabled based on user preferences.
187+
// It checks the unified setting (tsserver.automaticTypeAcquisition.enabled) first,
188+
// then falls back to the deprecated setting (disableAutomaticTypeAcquisition).
189+
func (p UserPreferences) IsATADisabled() bool {
190+
if !p.AutomaticTypeAcquisitionEnabled.IsUnknown() {
191+
return !p.AutomaticTypeAcquisitionEnabled.IsTrue()
192+
}
193+
return p.DisableAutomaticTypeAcquisition.IsTrue()
194+
}
195+
177196
type InlayHintsPreferences struct {
178197
IncludeInlayParameterNameHints IncludeInlayParameterNameHints `raw:"includeInlayParameterNameHints" config:"inlayHints.parameterNames.enabled"`
179198
IncludeInlayParameterNameHintsWhenArgumentMatchesName core.Tristate `raw:"includeInlayParameterNameHintsWhenArgumentMatchesName" config:"inlayHints.parameterNames.suppressWhenArgumentMatchesName,invert"`

internal/ls/lsutil/userpreferences_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,3 +326,58 @@ func TestUserPreferencesParseUnstable(t *testing.T) {
326326
})
327327
}
328328
}
329+
330+
func TestUserPreferencesParseATA(t *testing.T) {
331+
t.Parallel()
332+
333+
t.Run("ParseUserPreferences with unified ATA setting in js/ts section", func(t *testing.T) {
334+
t.Parallel()
335+
prefs := ParseUserPreferences(map[string]any{
336+
"js/ts": map[string]any{
337+
"tsserver": map[string]any{
338+
"automaticTypeAcquisition": map[string]any{
339+
"enabled": false,
340+
},
341+
},
342+
},
343+
})
344+
assert.Assert(t, prefs.IsATADisabled())
345+
assert.Equal(t, prefs.AutomaticTypeAcquisitionEnabled, core.TSFalse)
346+
})
347+
348+
t.Run("ParseUserPreferences with deprecated disableAutomaticTypeAcquisition in typescript section", func(t *testing.T) {
349+
t.Parallel()
350+
prefs := ParseUserPreferences(map[string]any{
351+
"typescript": map[string]any{
352+
"disableAutomaticTypeAcquisition": true,
353+
},
354+
})
355+
assert.Assert(t, prefs.IsATADisabled())
356+
assert.Equal(t, prefs.DisableAutomaticTypeAcquisition, core.TSTrue)
357+
})
358+
359+
t.Run("unified setting takes precedence over deprecated setting", func(t *testing.T) {
360+
t.Parallel()
361+
// Both settings set: unified (js/ts) should take precedence
362+
prefs := ParseUserPreferences(map[string]any{
363+
"typescript": map[string]any{
364+
"disableAutomaticTypeAcquisition": true,
365+
},
366+
"js/ts": map[string]any{
367+
"tsserver": map[string]any{
368+
"automaticTypeAcquisition": map[string]any{
369+
"enabled": true,
370+
},
371+
},
372+
},
373+
})
374+
assert.Assert(t, !prefs.IsATADisabled())
375+
assert.Equal(t, prefs.AutomaticTypeAcquisitionEnabled, core.TSTrue)
376+
})
377+
378+
t.Run("IsATADisabled returns false when neither setting is configured", func(t *testing.T) {
379+
t.Parallel()
380+
prefs := NewDefaultUserPreferences()
381+
assert.Assert(t, !prefs.IsATADisabled())
382+
})
383+
}

internal/project/ata/ata_test.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"testing"
77

88
"github.com/microsoft/typescript-go/internal/bundled"
9+
"github.com/microsoft/typescript-go/internal/ls/lsutil"
910
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
1011
"github.com/microsoft/typescript-go/internal/project"
1112
"github.com/microsoft/typescript-go/internal/testutil/projecttestutil"
@@ -662,4 +663,105 @@ func TestATA(t *testing.T) {
662663
assert.NilError(t, err)
663664
assert.Assert(t, ls != nil)
664665
})
666+
667+
ataDisabledCases := []struct {
668+
name string
669+
config map[string]any
670+
}{
671+
{
672+
name: "unified setting",
673+
config: map[string]any{
674+
"js/ts": map[string]any{
675+
"tsserver": map[string]any{
676+
"automaticTypeAcquisition": map[string]any{
677+
"enabled": false,
678+
},
679+
},
680+
},
681+
},
682+
},
683+
{
684+
name: "deprecated setting",
685+
config: map[string]any{
686+
"typescript": map[string]any{
687+
"disableAutomaticTypeAcquisition": true,
688+
},
689+
},
690+
},
691+
}
692+
693+
for _, tc := range ataDisabledCases {
694+
t.Run("ATA disabled via "+tc.name, func(t *testing.T) {
695+
t.Parallel()
696+
697+
files := map[string]any{
698+
"/user/username/projects/project/app.js": ``,
699+
"/user/username/projects/project/package.json": `{
700+
"name": "test",
701+
"dependencies": {
702+
"jquery": "^3.1.0"
703+
}
704+
}`,
705+
}
706+
707+
session, utils := projecttestutil.SetupWithTypingsInstaller(files, &projecttestutil.TypingsInstallerOptions{
708+
PackageToFile: map[string]string{
709+
"jquery": `declare const $: { x: number }`,
710+
},
711+
})
712+
713+
session.Configure(lsutil.ParseUserPreferences(tc.config))
714+
session.DidOpenFile(context.Background(), lsproto.DocumentUri("file:///user/username/projects/project/app.js"), 1, files["/user/username/projects/project/app.js"].(string), lsproto.LanguageKindJavaScript)
715+
session.WaitForBackgroundTasks()
716+
717+
calls := utils.NpmExecutor().NpmInstallCalls()
718+
assert.Equal(t, 0, len(calls), "Expected no npm install calls when ATA is disabled via "+tc.name)
719+
})
720+
}
721+
722+
t.Run("ATA re-enabled after being disabled triggers diagnostics refresh", func(t *testing.T) {
723+
t.Parallel()
724+
725+
files := map[string]any{
726+
"/user/username/projects/project/app.js": ``,
727+
"/user/username/projects/project/package.json": `{
728+
"name": "test",
729+
"dependencies": {
730+
"jquery": "^3.1.0"
731+
}
732+
}`,
733+
}
734+
735+
session, utils := projecttestutil.SetupWithTypingsInstaller(files, &projecttestutil.TypingsInstallerOptions{
736+
PackageToFile: map[string]string{
737+
"jquery": `declare const $: { x: number }`,
738+
},
739+
})
740+
741+
// Disable ATA
742+
session.Configure(lsutil.ParseUserPreferences(map[string]any{
743+
"js/ts": map[string]any{
744+
"tsserver": map[string]any{
745+
"automaticTypeAcquisition": map[string]any{
746+
"enabled": false,
747+
},
748+
},
749+
},
750+
}))
751+
752+
session.DidOpenFile(context.Background(), lsproto.DocumentUri("file:///user/username/projects/project/app.js"), 1, files["/user/username/projects/project/app.js"].(string), lsproto.LanguageKindJavaScript)
753+
session.WaitForBackgroundTasks()
754+
755+
calls := utils.NpmExecutor().NpmInstallCalls()
756+
assert.Equal(t, 0, len(calls), "Expected no npm install calls when ATA is disabled")
757+
758+
baselineRefreshCount := len(utils.Client().RefreshDiagnosticsCalls())
759+
760+
// Re-enable ATA
761+
session.Configure(lsutil.ParseUserPreferences(map[string]any{}))
762+
session.WaitForBackgroundTasks()
763+
764+
refreshCount := len(utils.Client().RefreshDiagnosticsCalls())
765+
assert.Assert(t, refreshCount > baselineRefreshCount, "Expected RefreshDiagnostics call after ATA re-enabled")
766+
})
665767
}

internal/project/session.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ func (s *Session) Configure(config lsutil.UserPreferences) {
268268
s.refreshInlayHintsIfNeeded(oldConfig, config)
269269
s.refreshCodeLensIfNeeded(oldConfig, config)
270270
s.refreshDiagnosticsIfNeeded(oldConfig, config)
271+
s.refreshATAIfNeeded(oldConfig, config)
271272
}
272273

273274
func (s *Session) InitializeWithUserConfig(config lsutil.UserPreferences) {
@@ -1100,7 +1101,7 @@ func (s *Session) updateSnapshot(ctx context.Context, overlays map[tspath.Path]*
11001101
s.snapshotMu.Unlock()
11011102

11021103
// Enqueue ATA updates if needed
1103-
if s.typingsInstaller != nil {
1104+
if s.typingsInstaller != nil && !s.Config().IsATADisabled() {
11041105
s.triggerATAForUpdatedProjects(newSnapshot)
11051106
}
11061107

@@ -1453,6 +1454,14 @@ func (s *Session) refreshDiagnosticsIfNeeded(oldPrefs lsutil.UserPreferences, ne
14531454
}
14541455
}
14551456

1457+
func (s *Session) refreshATAIfNeeded(oldPrefs lsutil.UserPreferences, newPrefs lsutil.UserPreferences) {
1458+
if oldPrefs.IsATADisabled() && !newPrefs.IsATADisabled() {
1459+
// ATA was re-enabled; schedule a diagnostics refresh so the next snapshot update
1460+
// re-triggers ATA for existing projects with the new setting.
1461+
s.ScheduleDiagnosticsRefresh()
1462+
}
1463+
}
1464+
14561465
func (s *Session) publishProgramDiagnostics(oldSnapshot *Snapshot, newSnapshot *Snapshot) {
14571466
if !s.options.PushDiagnosticsEnabled {
14581467
return

0 commit comments

Comments
 (0)