Skip to content

execute command line initial implementation #241

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Feb 3, 2025
Merged

execute command line initial implementation #241

merged 26 commits into from
Feb 3, 2025

Conversation

iisaduan
Copy link
Member

@iisaduan iisaduan commented Jan 21, 2025

tsc can be invoked by writing tsc as the second position, for example

tsgo tsc [rest of args to be passed into tsc]

Without tsc in the second position, there is no change to tsgo flag parsing/execution

ToDos for after this PR:

@iisaduan iisaduan requested a review from jakebailey January 21, 2025 08:59
}

func (s *osSys) EndWrite() {
// do nothing, this is needed in the interface for testing
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious, when is this called? Is it a flushing operation? Something else?

Copy link
Member Author

@iisaduan iisaduan Jan 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every occurrence of "write" is tracked in the original code for several reasons, and the tracking is most used probably with screen clears (which seem mostly relevant in watch?) The reason why this is needed is because the old code implemented write to be a separate entry each time we called write. However, because we are using stringwriters in diagnostic outputting, and we could be passing the writers through fmt.fprint.., several times in each diagnostic writing function, without an endwrite(), writing in the test system generates different output based on if it's implemented as

fmt.fprint(sys, "Output::")
fmt.fprint(sys, errors)
fmt.fprint(sys, "\n")

vs

fmt.fprint(sys, "Output::" + errors + "\n")

and i don't see an easier way to make them the same atm. I hope to be able to simplify it eventually, but I don't want to make too many changes when watch isn't implemented yet, since that's the part where "each write" is tracked.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense, though it seems like the assertion doesn't have to be per write, but it could be the entire output string buffered, right?

But, I can understand not wanting to mess with it for a port.

Copy link
Member Author

@iisaduan iisaduan Jan 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll continue to take a look at your buffer suggestion as I port/see what testing looks like after i finish adding tsconfig parsing to tests right now

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like this to be a TODO for a later PR since I would like testing to not be the priority for this PR

Comment on lines 83 to 84
// todo: revisit if improving tsc/build/watch unittest baselines
s.output = append(s.output, s.currentWrite.String())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't we just write all output to a bytes.Buffer or something, and then check it there? Checking line-based output seems like a lot of work.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -11,6 +11,7 @@ import (

type CompilerHost interface {
FS() vfs.FS
SetOptions(options *core.CompilerOptions)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect this isn't required; seems odd for the host to have a setter when that's the opposite direction of the flow of data...

(I think you have a comment of the sort in another file now that I read)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, but the host does acutally have it for other things. Makes me think the existing code isn't quite right.

Copy link
Member Author

@iisaduan iisaduan Jan 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes me think that the existing code isn't quite right

Which existing code? I often had a hard time deciding which things should belong to which interface, so it's pretty possible that I'm just confused, and also possible that the implementer of existing code also wasn't sure how things would shake out in the future/also was confused.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean the existing code implements hosts with methods that depends on options, which implies the host can answer questions about a config that hasn't even been set yet.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh yeah, it hits a lot of nil

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eventually for things like watch, the options would need to propagate back up to the host. It seems to make enough sense to me?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That feels a bit like a recipe for weird races / behavior, or confusing behavior if we want to share the host between multiple Programs... If we're saying this thing is created by Program for it to use, then I'm less afraid of it, but the way I'm reading it right now is that we are constructing one with NewCompilerHost to give to a Program.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For watch mode, I would expect us to take one program, then use it to construct another program, which would involve giving it the last program's info, not reusing the same host object with mutable state...

Copy link
Member Author

@iisaduan iisaduan Jan 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see what you mean. I think the confusion comes from strada having compilerOptions be a one-and-all object to hold everything, and at each level, not every option is used. For example, incremental is only used in executeCommandLine and emitter, and I'm pretty sure that the separate watchOptions object gets combined into the compilerOptions object at some points, but because the watchoptions aren't used in places like checker, we don't really see that.

So the CompilerHost would only need a subset of compilerOptions, and Program would need some overlapping subset. Since my implementation here has the parsed options being passed into the Program creation, that part for Program should be fine? 🤔

Copy link
Member Author

@iisaduan iisaduan Jan 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the other hand, it might be wrong to reset CompilerHost.options during program creation 🤔, but I'm not sure that at this state we could tell, since we're only doing the config parsing once

(As far as I can tell, if I comment that line out, there's no difference in behavior atm)

Comment on lines 175 to 176
// todo: remove/edit setOptions call if compilerHost no longer has CompilerOptions. Since compilerHost was created before options were parsed, compiler host would
// not have options yet
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this sort of split feels weird.

Copy link
Member Author

@iisaduan iisaduan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@navya9singh generally, I'm not sure if I'm missing places if I'm setting/saving the parsed compiler options or doing path handling for tsconfigparsing correctlu

tsConfigSourceFile := tsoptions.NewTsconfigSourceFileFromFilePath(configFileName, configFileText)
cwd := sys.Host().GetCurrentDirectory()
tsConfigSourceFile.SourceFile.SetPath(tspath.ToPath(configFileName, cwd, sys.FS().UseCaseSensitiveFileNames()))
// tsConfigSourceFile.resolvedPath = tsConfigSourceFile.FileName()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@navya9singh are these fields are needed?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe they are when files are included through project references. @jakebailey ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really know off the top of my head, but that sounds plausible.

Copy link
Member

@gabritto gabritto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mostly only had questions, nothing looked too weird to me that wasn't weird in TS already.

}).verify(t)
}

// func TestExtends(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be good to add a todo comment here saying we can run those tests when we have support for extends.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we already supported extends? vscode and the compiler are working.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests run into nilpointer errors. I think it may be an issue with path handling that wasn't evident in the tsconfigparsing tests for extends

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but extends doesn't have the exception when run via tsgo, so it feels odd for it to fail...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the same test cases?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, just on vscode and src/compiler; obviously not representative but I was just hoping this could run with some skips on specific tests or something. It's not a big deal, when I left my comment first I thought this was just accidentally commented out from before we supported it.

func (p *Program) GetOptionsDiagnostics() []*ast.Diagnostic {
return p.optionsDiagnostics
}
func (p *Program) SourceFiles() []*ast.SourceFile { return p.files }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not about this PR, but: why does compiler host have compiler options in the first place?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see there's a discussion on this elsewhere. I also agree it's weird.

}).verify(t)
}

// func TestExtends(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we already supported extends? vscode and the compiler are working.

tsConfigSourceFile := tsoptions.NewTsconfigSourceFileFromFilePath(configFileName, configFileText)
cwd := sys.Host().GetCurrentDirectory()
tsConfigSourceFile.SourceFile.SetPath(tspath.ToPath(configFileName, cwd, sys.FS().UseCaseSensitiveFileNames()))
// tsConfigSourceFile.resolvedPath = tsConfigSourceFile.FileName()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe they are when files are included through project references. @jakebailey ?

Copy link
Member

@jakebailey jakebailey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just nits.

Co-authored-by: Jake Bailey <[email protected]>
@iisaduan iisaduan merged commit 2cf4cb4 into microsoft:main Feb 3, 2025
14 checks passed
@iisaduan iisaduan deleted the executeCommandLine branch February 3, 2025 23:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants