Skip to content

Commit efdca48

Browse files
authored
add watch options (microsoft#276)
1 parent b314870 commit efdca48

27 files changed

+962
-557
lines changed

internal/core/parsedoptions.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package core
22

33
type ParsedOptions struct {
4-
CompilerOptions *CompilerOptions
4+
CompilerOptions *CompilerOptions
5+
WatchOptions *WatchOptions
6+
57
FileNames []string
68
ProjectReferences []ProjectReference
79
}

internal/core/watchoptions.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package core
2+
3+
type WatchOptions struct {
4+
FileKind WatchFileKind `json:"watchFile"`
5+
DirectoryKind WatchDirectoryKind `json:"watchDirectory"`
6+
FallbackPolling PollingKind `json:"fallbackPolling"`
7+
SyncWatchDir Tristate `json:"synchronousWatchDirectory"`
8+
ExcludeDir []string `json:"excludeDirectories"`
9+
ExcludeFiles []string `json:"excludeFiles"`
10+
}
11+
12+
type WatchFileKind int32
13+
14+
const (
15+
WatchFileKindNone WatchFileKind = 0
16+
WatchFileKindFixedPollingInterval WatchFileKind = 1
17+
WatchFileKindPriorityPollingInterval WatchFileKind = 2
18+
WatchFileKindDynamicPriorityPolling WatchFileKind = 3
19+
WatchFileKindFixedChunkSizePolling WatchFileKind = 4
20+
WatchFileKindUseFsEvents WatchFileKind = 5
21+
WatchFileKindUseFsEventsOnParentDirectory WatchFileKind = 6
22+
)
23+
24+
type WatchDirectoryKind int32
25+
26+
const (
27+
WatchDirectoryKindNone WatchDirectoryKind = 0
28+
WatchDirectoryKindUseFsEvents WatchDirectoryKind = 1
29+
WatchDirectoryKindFixedPollingInterval WatchDirectoryKind = 2
30+
WatchDirectoryKindDynamicPriorityPolling WatchDirectoryKind = 3
31+
WatchDirectoryKindFixedChunkSizePolling WatchDirectoryKind = 4
32+
)
33+
34+
type PollingKind int32
35+
36+
const (
37+
PollingKindNone PollingKind = 0
38+
PollingKindFixedInterval PollingKind = 1
39+
PollingKindPriorityInterval PollingKind = 2
40+
PollingKindDynamicPriority PollingKind = 3
41+
PollingKindFixedChunkSize PollingKind = 4
42+
)

internal/tsoptions/commandlineoption.go

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,19 @@ type CommandLineOption struct {
2424

2525
// used in parsing
2626
isFilePath bool
27-
isTSConfigOnly bool
27+
IsTSConfigOnly bool
2828
IsCommandLineOnly bool
2929

3030
// used in output
31-
description *diagnostics.Message
32-
defaultValueDescription any
31+
Description *diagnostics.Message
32+
DefaultValueDescription any
3333
showInSimplifiedHelpView bool
3434

3535
// used in output in serializing and generate tsconfig
36-
category *diagnostics.Message
36+
Category *diagnostics.Message
3737

38-
// defined once
39-
extraValidation func(value CompilerOptionsValue) (d *diagnostics.Message, args []string)
38+
// a flag indicating whether `validateJsonOptionValue` should perform extra checks
39+
extraValidation bool
4040

4141
// true or undefined
4242
// used for configDirTemplateSubstitutionOptions
@@ -95,7 +95,7 @@ var commandLineOptionElements = map[string]*CommandLineOption{
9595
"lib": {
9696
Name: "lib",
9797
Kind: CommandLineOptionTypeEnum, // libMap,
98-
defaultValueDescription: core.TSUnknown,
98+
DefaultValueDescription: core.TSUnknown,
9999
},
100100
"rootDirs": {
101101
Name: "rootDirs",
@@ -144,6 +144,19 @@ var commandLineOptionElements = map[string]*CommandLineOption{
144144
Name: "extends",
145145
Kind: CommandLineOptionTypeString,
146146
},
147+
// For Watch options
148+
"excludeDirectories": {
149+
Name: "excludeDirectory",
150+
Kind: CommandLineOptionTypeString,
151+
isFilePath: true,
152+
extraValidation: true,
153+
},
154+
"excludeFiles": {
155+
Name: "excludeFile",
156+
Kind: CommandLineOptionTypeString,
157+
isFilePath: true,
158+
extraValidation: true,
159+
},
147160
}
148161

149162
// CommandLineOption.EnumMap()
@@ -155,6 +168,9 @@ var commandLineOptionEnumMap = map[string]*collections.OrderedMap[string, any]{
155168
"moduleDetection": moduleDetectionOptionMap,
156169
"jsx": jsxOptionMap,
157170
"newLine": newLineOptionMap,
171+
"watchFile": watchFileEnumMap,
172+
"watchDirectory": watchDirectoryEnumMap,
173+
"fallbackPolling": fallbackEnumMap,
158174
}
159175

160176
// CommandLineOption.DeprecatedKeys()

internal/tsoptions/commandlineparser.go

Lines changed: 53 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -11,47 +11,29 @@ import (
1111
"github.com/microsoft/typescript-go/internal/vfs"
1212
)
1313

14-
func (p *CommandLineParser) AlternateMode() *AlternateModeDiagnostics {
14+
func (p *commandLineParser) AlternateMode() *AlternateModeDiagnostics {
1515
return p.workerDiagnostics.didYouMean.alternateMode
1616
}
1717

18-
func (p *CommandLineParser) OptionsDeclarations() []*CommandLineOption {
18+
func (p *commandLineParser) OptionsDeclarations() []*CommandLineOption {
1919
return p.workerDiagnostics.didYouMean.OptionDeclarations
2020
}
2121

22-
func (p *CommandLineParser) UnknownOptionDiagnostic() *diagnostics.Message {
22+
func (p *commandLineParser) UnknownOptionDiagnostic() *diagnostics.Message {
2323
return p.workerDiagnostics.didYouMean.UnknownOptionDiagnostic
2424
}
2525

26-
func (p *CommandLineParser) UnknownDidYouMeanDiagnostic() *diagnostics.Message {
26+
func (p *commandLineParser) UnknownDidYouMeanDiagnostic() *diagnostics.Message {
2727
return p.workerDiagnostics.didYouMean.UnknownDidYouMeanDiagnostic
2828
}
2929

30-
func (p *CommandLineParser) GetOptionsNameMap() *NameMap {
31-
p.workerDiagnostics.optionsNameMapOnce.Do(func() {
32-
optionsNames := map[string]*CommandLineOption{}
33-
shortOptionNames := map[string]string{}
34-
for _, option := range p.workerDiagnostics.didYouMean.OptionDeclarations {
35-
optionsNames[strings.ToLower(option.Name)] = option
36-
if option.shortName != "" {
37-
shortOptionNames[option.shortName] = option.Name
38-
}
39-
}
40-
p.workerDiagnostics.optionsNameMap = &NameMap{
41-
optionsNames: optionsNames,
42-
shortOptionNames: shortOptionNames,
43-
}
44-
})
45-
return p.workerDiagnostics.optionsNameMap
46-
}
47-
48-
type CommandLineParser struct {
30+
type commandLineParser struct {
4931
workerDiagnostics *ParseCommandLineWorkerDiagnostics
32+
optionsMap *NameMap
5033
fs vfs.FS
5134
options map[string]any
52-
// todo: watchOptions map[string]any
53-
fileNames []string
54-
errors []*ast.Diagnostic
35+
fileNames []string
36+
errors []*ast.Diagnostic
5537
}
5638

5739
func ParseCommandLine(
@@ -63,14 +45,16 @@ func ParseCommandLine(
6345
}
6446
parser := parseCommandLineWorker(CompilerOptionsDidYouMeanDiagnostics, commandLine, host.FS())
6547
optionsWithAbsolutePaths := convertToOptionsWithAbsolutePaths(parser.options, commandLineCompilerOptionsMap, host.GetCurrentDirectory())
66-
o, d := convertOptionsFromJson(commandLineCompilerOptionsMap, optionsWithAbsolutePaths, host.GetCurrentDirectory(), &core.CompilerOptions{})
48+
compilerOptions, d1 := convertOptionsFromJson(commandLineCompilerOptionsMap, optionsWithAbsolutePaths, host.GetCurrentDirectory(), &compilerOptionsParser{&core.CompilerOptions{}, true})
49+
watchOptions, d2 := convertOptionsFromJson(commandLineCompilerOptionsMap, optionsWithAbsolutePaths, host.GetCurrentDirectory(), &watchOptionsParser{&core.WatchOptions{}, true})
6750
return &ParsedCommandLine{
6851
ParsedConfig: &core.ParsedOptions{
69-
CompilerOptions: o,
52+
CompilerOptions: compilerOptions.CompilerOptions,
53+
WatchOptions: watchOptions.WatchOptions,
7054
FileNames: parser.fileNames,
7155
},
7256
ConfigFile: nil,
73-
Errors: append(parser.errors, d...),
57+
Errors: append(append(parser.errors, d1...), d2...),
7458
Raw: parser.options, // todo: keep optionsBase incase needed later
7559
CompileOnSave: nil,
7660
}
@@ -80,19 +64,20 @@ func parseCommandLineWorker(
8064
parseCommandLineWithDiagnostics *ParseCommandLineWorkerDiagnostics,
8165
commandLine []string,
8266
fs vfs.FS,
83-
) *CommandLineParser {
84-
parser := &CommandLineParser{
67+
) *commandLineParser {
68+
parser := &commandLineParser{
8569
fs: fs,
8670
workerDiagnostics: parseCommandLineWithDiagnostics,
8771
fileNames: []string{},
8872
options: map[string]any{},
8973
errors: []*ast.Diagnostic{},
9074
}
75+
parser.optionsMap = GetNameMapFromList(parser.OptionsDeclarations())
9176
parser.parseStrings(commandLine)
9277
return parser
9378
}
9479

95-
func (p *CommandLineParser) parseStrings(args []string) {
80+
func (p *commandLineParser) parseStrings(args []string) {
9681
i := 0
9782
for i < len(args) {
9883
s := args[i]
@@ -105,17 +90,16 @@ func (p *CommandLineParser) parseStrings(args []string) {
10590
p.parseResponseFile(s[1:])
10691
case '-':
10792
inputOptionName := getInputOptionName(s)
108-
opt := p.GetOptionsNameMap().GetOptionDeclarationFromName(inputOptionName, true /*allowShort*/)
93+
opt := p.optionsMap.GetOptionDeclarationFromName(inputOptionName, true /*allowShort*/)
10994
if opt != nil {
110-
i = p.parseOptionValue(args, i, opt)
95+
i = p.parseOptionValue(args, i, opt, nil)
11196
} else {
112-
// todo: watch options not yet implemented
113-
// watchOpt := getOptionDeclarationFromName(watchOptionsDidYouMeanDiagnostics.getOptionsNameMap, inputOptionName, /*allowShort*/ true);
114-
// if (watchOpt != nil) {
115-
// i := parser.parseOptionValue(args, i, watchOptionsDidYouMeanDiagnostics, watchOpt, watchOptions, errors);
116-
// } else {
117-
p.errors = append(p.errors, p.createUnknownOptionError(inputOptionName, s, nil, nil))
118-
// }
97+
watchOpt := WatchNameMap.GetOptionDeclarationFromName(inputOptionName, true /*allowShort*/)
98+
if watchOpt != nil {
99+
i = p.parseOptionValue(args, i, watchOpt, watchOptionsDidYouMeanDiagnostics.OptionTypeMismatchDiagnostic)
100+
} else {
101+
p.errors = append(p.errors, p.createUnknownOptionError(inputOptionName, s, nil, nil))
102+
}
119103
}
120104
default:
121105
p.fileNames = append(p.fileNames, s)
@@ -128,7 +112,7 @@ func getInputOptionName(input string) string {
128112
return strings.ToLower(strings.TrimLeft(strings.TrimLeft(input, "-"), "-"))
129113
}
130114

131-
func (p *CommandLineParser) parseResponseFile(fileName string) {
115+
func (p *commandLineParser) parseResponseFile(fileName string) {
132116
fileContents, errors := TryReadFile(fileName, func(fileName string) (string, bool) {
133117
if p.fs == nil {
134118
return "", false
@@ -188,12 +172,13 @@ func TryReadFile(fileName string, readFile func(string) (string, bool), errors [
188172
return text, errors
189173
}
190174

191-
func (p *CommandLineParser) parseOptionValue(
175+
func (p *commandLineParser) parseOptionValue(
192176
args []string,
193177
i int,
194178
opt *CommandLineOption,
179+
diag *diagnostics.Message,
195180
) int {
196-
if opt.isTSConfigOnly && i <= len(args) {
181+
if opt.IsTSConfigOnly && i <= len(args) {
197182
optValue := ""
198183
if i < len(args) {
199184
optValue = args[i]
@@ -221,7 +206,10 @@ func (p *CommandLineParser) parseOptionValue(
221206
// Check to see if no argument was provided (e.g. "--locale" is the last command-line argument).
222207
if i >= len(args) {
223208
if opt.Kind != "boolean" {
224-
p.errors = append(p.errors, ast.NewCompilerDiagnostic(p.workerDiagnostics.OptionTypeMismatchDiagnostic, opt.Name, getCompilerOptionValueTypeString(opt)))
209+
if diag == nil {
210+
diag = p.workerDiagnostics.OptionTypeMismatchDiagnostic
211+
}
212+
p.errors = append(p.errors, ast.NewCompilerDiagnostic(diag, opt.Name, getCompilerOptionValueTypeString(opt)))
225213
if opt.Kind == "list" {
226214
p.options[opt.Name] = []string{}
227215
} else if opt.Kind == "enum" {
@@ -257,13 +245,17 @@ func (p *CommandLineParser) parseOptionValue(
257245
}
258246
case "string":
259247
val, err := validateJsonOptionValue(opt, args[i], nil, nil)
260-
p.options[opt.Name] = val
261-
p.errors = append(p.errors, err...)
248+
if err == nil {
249+
p.options[opt.Name] = val
250+
} else {
251+
p.errors = append(p.errors, err...)
252+
}
262253
i++
263254
case "list":
264-
result := p.parseListTypeOption(opt, args[i])
255+
result, err := p.parseListTypeOption(opt, args[i])
265256
p.options[opt.Name] = result
266-
if len(result) != 0 {
257+
p.errors = append(p.errors, err...)
258+
if len(result) > 0 || len(err) > 0 {
267259
i++
268260
}
269261
case "listOrElement":
@@ -283,10 +275,8 @@ func (p *CommandLineParser) parseOptionValue(
283275
return i
284276
}
285277

286-
func (p *CommandLineParser) parseListTypeOption(opt *CommandLineOption, value string) []string {
287-
elements, errors := ParseListTypeOption(opt, value)
288-
p.errors = append(p.errors, errors...)
289-
return elements
278+
func (p *commandLineParser) parseListTypeOption(opt *CommandLineOption, value string) ([]string, []*ast.Diagnostic) {
279+
return ParseListTypeOption(opt, value)
290280
}
291281

292282
func ParseListTypeOption(opt *CommandLineOption, value string) ([]string, []*ast.Diagnostic) {
@@ -297,7 +287,9 @@ func ParseListTypeOption(opt *CommandLineOption, value string) ([]string, []*ast
297287
}
298288
if opt.Kind == "listOrElement" && !strings.ContainsRune(value, ',') {
299289
val, err := validateJsonOptionValue(opt, value, nil, nil)
300-
errors = append(errors, err...)
290+
if err != nil {
291+
return []string{}, err
292+
}
301293
return []string{val.(string)}, errors
302294
}
303295
if value == "" {
@@ -308,30 +300,27 @@ func ParseListTypeOption(opt *CommandLineOption, value string) ([]string, []*ast
308300
case "string":
309301
elements := core.Filter(core.Map(values, func(v string) string {
310302
val, err := validateJsonOptionValue(opt.Elements(), v, nil, nil)
303+
if _, ok := val.(string); ok {
304+
return val.(string)
305+
}
311306
errors = append(errors, err...)
312-
return val.(string)
307+
return ""
313308
}), isDefined)
314309
return elements, errors
315310
case "boolean", "object", "number":
316311
// do nothing: only string and enum/object types currently allowed as list entries
317312
// !!! we don't actually have number list options, so I didn't implement number list parsing
318313
panic("List of " + opt.Elements().Kind + " is not yet supported.")
319314
default:
320-
result := core.Map(values, func(v string) string {
315+
result := core.Filter(core.Map(values, func(v string) string {
321316
val, err := convertJsonOptionOfEnumType(opt.Elements(), strings.TrimFunc(v, stringutil.IsWhiteSpaceLike), nil, nil)
322317
if _, ok := val.(string); ok {
323318
return val.(string)
324319
}
325320
errors = append(errors, err...)
326321
return ""
327-
})
328-
var mappedValues []string
329-
for _, v := range result {
330-
if isDefined(v) {
331-
mappedValues = append(mappedValues, v)
332-
}
333-
}
334-
return mappedValues, errors
322+
}), isDefined)
323+
return result, errors
335324
}
336325
}
337326

0 commit comments

Comments
 (0)