In my 12+ years of coding, you’d think I’d have learned the lesson that software systems are hard. Well, I learned that lesson once again in an attempt to move a bunch of Java projects from Eclipse to VS Code. Here’s what I learned.
Table of Contents
- Context
- Eclipse Projects Cannot Be Imported Into VS Code Directly
- Plugins Are Now Extensions
- Odds and Ends
- Summary
Context
For a bit of context, I teach a variety of Java courses at the college level, and there are always challenges with updating curriculum. For instance, our department used to teach all of our programming classes in C/C++, but several of the software engineering courses moved over to Java. That said, all of the lower level systems courses are still in C/C++.
Of course, the move to Java was probably over a decade ago. However, there hasn’t really been a move to update any of the utilities. For example, we use Eclipse and subversion in our courses now. At the time of writing this post, subversion accounts for allegedly 0.24% of the market share for version control tools.
Now, I don’t think there is anything wrong with teaching tools that are less popular. In fact, I’d argue there are plenty of benefits. For example, you might get a leg up against the competition if you have familiarity with lesser used tools. I think about this with legacy code like Cobol, which I assume is still being maintained to this day.
However, I do think we do our students a disservice by not exposing them to the mainstream tools that they are most likely to see on the job. Nothing is more depressing than when a student tells me they failed an interview because they didn’t know about a tool that companies expect students to be able to use.
As a result, I’ve become interested in migrating students over to tools that are much more mainstream, like VS Code and git. However, I ran into many hurdles on my way there, which I wanted to share now for anyone looking to do something similar.
Eclipse Projects Cannot Be Imported Into VS Code Directly
In most cases, folks use some form of dependency management tool like Ant, Gradle, or Maven in their Java projects. In the classes I teach, we don’t expose students to these tools. In fact, any third-party libraries we ask students to use are always provided in a jar file (not ideal, I understand).
As a result, the Eclipse projects that students complete are “unmanaged”, meaning that there are no dependency management tools in place. Instead, we make use of Eclipse variables. Then, at the start of each semester, we ask that students set that variable to the location of the jar file on their system.
As you can probably imagine, this leads to so, so many issues throughout the semester as students inadvertently delete the jar file or lose their configuration files. Certainly, there are ways to fix this issue in Eclipse, but I never much liked using Eclipse anyway. That’s why I’ve decided to pivot to a more mainstream tool like VS Code.
Now, unfortunately, VS Code isn’t really all that great for Java either. However, it’s a tool I use every day, and it’s a tool that I know students will get value out of far beyond Java. In fact, you can use it for just about anything, so it seems like a solid payoff in the long term.
Of course, moving the Eclipse projects over to VS Code has proved to be a bit of a nightmare, and I was never really able to find any tools that could do it. If you read around online on how to do this, most folks provide some fairly hacky solutions like deleting key configuration files.
Worse still, VS Code doesn’t make it completely obvious how you would store multiple projects in the same workspace. There is the multi-root workspace feature, but it doesn’t seem to work exactly as I would expect. As a result, I’ll quickly share my solution.
First, because the projects are unmanaged, we need to store our libraries somewhere. There are ways to add libraries to a project using the VS Code interface, but I much prefer just dumping all of the needed jars in a lib
folder in the root (including the jars needed for JUnit testing). Then, all you really need to do is add a settings.json
file to every project folder. You can do this by copying the .vscode
folder which contains a settings.json
file with the following contents:
{ "java.project.sourcePaths": [ "src" ], "java.project.outputPath": "bin", "java.project.referencedLibraries": [ "lib/**/*.jar" ] }
Then, the project folders from Eclipse just need the source code (e.g., src
and test
) to work. You can delete or exclude all of the config files (e.g., .setting
, .classpath
, and .project
). For completion, here is quite literally my directory structure for my working VS Code template for Java using the tree command (fair warning: this is a wall of text—the part you probably care about is the project
folder which is composed of migrated Eclipse projects):
│ .gitignore │ README.md │ ├───.vscode │ extensions.json │ osu-cse-checkstyle-config.xml │ osu-cse-formatter.xml │ settings.json │ ├───assets │ markdown-preview-icon.png │ ├───homeworks │ ├───00-getting-started-with-markdown │ │ 00-getting-started-with-markdown.md │ │ playoff-chances.png │ │ │ ├───01-reading-formal-contract-specifications │ │ 01-reading-formal-contract-specifications.md │ │ │ ├───02-testing-sequence-smooth │ │ 02-testing-sequence-smooth.md │ │ │ ├───03-sequence-smooth-function │ │ 03-sequence-smooth-function.md │ │ │ ├───04-integer-average │ │ 04-integer-average.md │ │ │ ├───05-standard-java-lists │ │ 05-standard-java-lists.md │ │ │ ├───06-queue-on-sequence │ │ 06-queue-on-sequence.md │ │ │ ├───07-sequence-on-stack │ │ 07-sequence-on-stack.md │ │ │ ├───08-set-on-queue │ │ 08-set-on-queue.md │ │ │ ├───09-map-on-queue │ │ 09-map-on-queue.md │ │ │ ├───10-hashing-and-implementing-mod │ │ 10-hashing-and-implementing-mod.md │ │ │ ├───11-hashing-2 │ │ 11-hashing-2.md │ │ │ ├───12-binary-tree-and-recursion-1 │ │ 12-binary-tree-and-recursion-1.md │ │ │ ├───13-binary-tree-and-recursion-2 │ │ 13-binary-tree-and-recursion-2.md │ │ │ ├───14-binary-search-trees │ │ 14-binary-search-trees.md │ │ │ ├───15-insertion-sort │ │ 15-insertion-sort.md │ │ │ ├───16-quicksort │ │ 16-quicksort.md │ │ │ ├───17-heapsort │ │ 17-heapsort.md │ │ │ ├───18-stack-on-singly-linked-list │ │ 18-stack-on-singly-linked-list.md │ │ │ ├───19-list-implementation-with-singly-linked-list-and-two-smart-nodes │ │ 19-list-implementation-with-singly-linked-list-and-two-smart-nodes.md │ │ │ ├───20-bugs-world │ │ 20-bugs-world.md │ │ TryToGuess.bl │ │ │ ├───21-tree-and-recursion │ │ 21-tree-and-recursion.md │ │ │ ├───22-statement-and-recursion-1 │ │ 22-statement-and-recursion-1.md │ │ │ ├───23-statement-and-recursion-2 │ │ 23-statement-and-recursion-2.md │ │ │ ├───24-statement-and-recursion-3 │ │ 24-statement-and-recursion-3.md │ │ │ ├───25-refactoring-program-and-statement │ │ 25-refactoring-program-and-statement.md │ │ │ ├───26-tokenizer │ │ 26-tokenizer.md │ │ │ ├───27-context-free-grammars │ │ 27-context-free-grammars.md │ │ │ ├───28-evaluation-of-arithmetic-expressions │ │ 28-evaluation-of-arithmetic-expressions.md │ │ │ ├───29-evaluation-of-boolean-expressions │ │ 29-evaluation-of-boolean-expressions.md │ │ │ ├───30-bugsworld-virtual-machine-byecode-generator │ │ 30-bugsworld-virtual-machine-bytecode-generator.md │ │ │ ├───31-statement-and-recursion-4 │ │ 31-statement-and-recursion-4.md │ │ │ ├───32-waitingline-family-interface-design │ │ 32-waitingline-family-interface-design.md │ │ │ ├───33-waitingline-family-class-implementation │ │ 33-waitingline-family-class-implementation.md │ │ │ ├───34-the-java-collections-framework-1 │ │ 34-the-java-collections-framework-1.md │ │ │ ├───35-the-java-collections-framework-2 │ │ 35-the-java-collections-framework-2.md │ │ │ ├───36-java-file-io │ │ 36-java-file-io.md │ │ │ └───37-java-class-data-members │ 37-java-class-data-members.md │ ├───labs │ ├───01-sequence-palindromes │ │ ├───.vscode │ │ │ settings.json │ │ │ │ │ └───src │ │ SequencePalindrome.java │ │ │ ├───02-sequence-smooth │ │ ├───.vscode │ │ │ settings.json │ │ │ │ │ ├───src │ │ │ SequenceSmooth.java │ │ │ │ │ └───test │ │ SequenceSmoothTest.java │ │ │ ├───03-junit-testing │ │ ├───.vscode │ │ │ settings.json │ │ │ │ │ └───test │ │ Stack1LTest.java │ │ StackTest.java │ │ │ ├───04-version-control │ │ ├───.vscode │ │ │ settings.json │ │ │ │ │ ├───src │ │ │ Queue3.java │ │ │ │ │ └───test │ │ Queue3Test.java │ │ QueueTest.java │ │ │ ├───05-sequence-on-stack │ │ ├───.vscode │ │ │ settings.json │ │ │ │ │ ├───src │ │ │ Sequence3.java │ │ │ │ │ └───test │ │ Sequence3Test.java │ │ SequenceTest.java │ │ │ ├───06-set-on-queue │ │ ├───.vscode │ │ │ settings.json │ │ │ │ │ ├───src │ │ │ Set2.java │ │ │ │ │ └───test │ │ Set2Test.java │ │ SetTest.java │ │ │ ├───07-map-on-queue │ │ ├───.vscode │ │ │ settings.json │ │ │ │ │ ├───src │ │ │ Map2.java │ │ │ │ │ └───test │ │ Map2Test.java │ │ MapTest.java │ │ │ ├───08-hashing-experiments │ │ ├───.vscode │ │ │ settings.json │ │ │ │ │ ├───data │ │ │ length8.txt │ │ │ mod30.txt │ │ │ random.txt │ │ │ startend.txt │ │ │ │ │ ├───src │ │ │ HashingExploration.java │ │ │ │ │ └───test │ │ ModTest.java │ │ │ ├───09-binary-tree-and-recursion │ │ ├───.vscode │ │ │ settings.json │ │ │ │ │ ├───src │ │ │ BinaryTreeMethods.java │ │ │ │ │ └───test │ │ BinaryTreeMethodsTest.java │ │ │ ├───10-binary-search-trees │ │ ├───.vscode │ │ │ settings.json │ │ │ │ │ ├───src │ │ │ BinarySearchTreeMethods.java │ │ │ │ │ └───test │ │ BinarySearchTreeMethodsTest.java │ │ │ ├───11-queue-insertion-sort │ │ ├───.vscode │ │ │ settings.json │ │ │ │ │ ├───data │ │ │ lines.txt │ │ │ │ │ ├───src │ │ │ Queue1LSort3.java │ │ │ QueueSortMain.java │ │ │ SortingMachine3.java │ │ │ │ │ └───test │ │ SortingMachine3Test.java │ │ SortingMachineTest.java │ │ │ ├───12-queue-quicksort │ │ ├───.vscode │ │ │ settings.json │ │ │ │ │ ├───data │ │ │ lines.txt │ │ │ │ │ ├───src │ │ │ Queue1LSort4.java │ │ │ QueueSortMain.java │ │ │ SortingMachine4.java │ │ │ │ │ └───test │ │ SortingMachine4Test.java │ │ SortingMachineTest.java │ │ │ ├───13-heapsort │ │ ├───.vscode │ │ │ settings.json │ │ │ │ │ └───src │ │ ArraySiftDownMain.java │ │ │ ├───14-stack-with-linked-list │ │ ├───.vscode │ │ │ settings.json │ │ │ │ │ ├───src │ │ │ Stack2.java │ │ │ │ │ └───test │ │ Stack2Test.java │ │ StackTest.java │ │ │ ├───15-list-with-two-smart-nodes │ │ ├───.vscode │ │ │ settings.json │ │ │ │ │ ├───src │ │ │ List2.java │ │ │ │ │ └───test │ │ List2Test.java │ │ ListTest.java │ │ │ ├───16-bugs-world-contest │ │ ├───.vscode │ │ │ settings.json │ │ │ │ │ └───src │ │ TryToGuess.bl │ │ │ ├───17-statement-and-recursion │ │ ├───.vscode │ │ │ settings.json │ │ │ │ │ ├───data │ │ │ test1.bl │ │ │ │ │ ├───src │ │ │ CountPrimitiveCalls.java │ │ │ │ │ └───test │ │ CountPrimitiveCallsTest.java │ │ │ ├───18-program-pretty-print │ │ ├───.vscode │ │ │ settings.json │ │ │ │ │ ├───data │ │ │ SampleProgram.bl │ │ │ │ │ └───src │ │ Program1PrettyPrint1.java │ │ │ ├───19-statement-pretty-print │ │ ├───.vscode │ │ │ settings.json │ │ │ │ │ ├───data │ │ │ SampleStatement.bl │ │ │ │ │ └───src │ │ Statement1PrettyPrint1.java │ │ │ ├───20-tokenizer │ │ ├───.vscode │ │ │ settings.json │ │ │ │ │ ├───data │ │ │ SampleInput.txt │ │ │ SampleProgram.bl │ │ │ │ │ └───src │ │ Tokenizer.java │ │ │ ├───21-resursive-descent-parser │ │ ├───.vscode │ │ │ settings.json │ │ │ │ │ ├───src │ │ │ ExpressionEvaluator.java │ │ │ │ │ └───test │ │ ExpressionEvaluatorTest.java │ │ │ ├───22-bugs-world-vm-interpreter │ │ ├───.vscode │ │ │ settings.json │ │ │ │ │ ├───data │ │ │ TestProgram.bl │ │ │ TestProgram.bo │ │ │ │ │ ├───src │ │ │ BugsWorldVMInterpreter.java │ │ │ │ │ └───test │ │ BugsWorldVMInterpreterTest.java │ │ │ ├───23-24-waiting-line-family-interface-design │ │ ├───.vscode │ │ │ settings.json │ │ │ │ │ ├───doc │ │ │ stylesheet.css │ │ │ │ │ └───src │ │ └───components │ │ └───queue │ │ package.html │ │ Queue.java │ │ QueueKernel.java │ │ │ ├───25-practice-with-java-collections-framework │ │ ├───.vscode │ │ │ settings.json │ │ │ │ │ ├───src │ │ │ JCFExplorations.java │ │ │ │ │ └───test │ │ JCFExplorationsTest.java │ │ │ ├───26-practice-with-java-io │ │ ├───.vscode │ │ │ settings.json │ │ │ │ │ ├───data │ │ │ importance.txt │ │ │ strings.txt │ │ │ │ │ └───src │ │ CopyFileStdJava.java │ │ │ └───27-practice-with-static-data-members │ ├───.vscode │ │ settings.json │ │ │ └───src │ EmailAccount.java │ EmailAccount1.java │ EmailAccountMain.java │ ├───lib │ binary-tree-utility.jar │ components.jar │ hamcrest-core-1.3.jar │ junit-4.13.2.jar │ └───projects ├───01-word-counter │ ├───.vscode │ │ settings.json │ │ │ ├───data │ │ gettysburg.txt │ │ │ └───doc │ 01-word-counter-checklist.md │ 01-word-counter-rubric.md │ ├───02-natural-number-on-string │ ├───.vscode │ │ settings.json │ │ │ ├───doc │ │ 02-natural-number-on-string-checklist.md │ │ 02-natural-number-on-string-rubric.md │ │ │ ├───src │ │ NaturalNumber3.java │ │ │ └───test │ NaturalNumber3Test.java │ NaturalNumberTest.java │ ├───03-map-with-hashing │ ├───.vscode │ │ settings.json │ │ │ ├───doc │ │ 03-map-with-hashing-checklist.md │ │ 03-map-with-hashing-rubric.md │ │ │ ├───src │ │ Map4.java │ │ │ └───test │ Map4Test.java │ Map4Test1009.java │ MapTest.java │ ├───04-set-on-binary-search-trees │ ├───.vscode │ │ settings.json │ │ │ ├───doc │ │ 04-set-on-binary-search-trees-checklist.md │ │ 04-set-on-binary-search-trees-rubric.md │ │ │ ├───src │ │ Set3a.java │ │ │ └───test │ Set3aTest.java │ SetTest.java │ ├───05-sorting-maching-with-heapsort │ ├───.vscode │ │ settings.json │ │ │ ├───doc │ │ 05-sorting-machine-with-heapsort-checklist.md │ │ 05-sorting-machine-with-heapsort-rubric.md │ │ │ ├───src │ │ SortingMachine5a.java │ │ │ └───test │ SortingMachine5aTest.java │ SortingMachineTest.java │ ├───06-list-with-retreat │ ├───.vscode │ │ settings.json │ │ │ ├───doc │ │ 06-list-with-retreat-checklist.md │ │ 06-list-with-retreat-rubric.md │ │ │ ├───src │ │ List3.java │ │ │ └───test │ List3Test.java │ ListTest.java │ ├───07-program-and-statement │ ├───.vscode │ │ settings.json │ │ │ ├───data │ │ program-sample.bl │ │ statement-sample.bl │ │ │ ├───doc │ │ 07-program-and-statement-kernels-checklist.md │ │ 07-program-and-statement-kernels-rubric.md │ │ │ ├───src │ │ Program2.java │ │ ProgramTester.java │ │ Statement2.java │ │ StatementTester.java │ │ │ └───test │ Program2Test.java │ ProgramTest.java │ Statement2Test.java │ StatementTest.java │ ├───08-bl-parser │ ├───.vscode │ │ settings.json │ │ │ ├───doc │ │ 08-program-and-statement-parse-checklist.md │ │ 08-program-and-statement-parse-rubric.md │ │ │ ├───src │ │ Program1Parse1.java │ │ Statement1Parse1.java │ │ │ └───test │ program1.bl │ Program1Parse1Test.java │ program2.bl │ ProgramTest.java │ statement1.bl │ Statement1Parse1Test.java │ statement2.bl │ StatementTest.java │ ├───09-tag-cloud-generator │ ├───.vscode │ │ settings.json │ │ │ ├───data │ │ alice.txt │ │ doriangray.txt │ │ importance.txt │ │ lesmiz.txt │ │ tomsawyer.txt │ │ │ └───doc │ 09-tag-cloud-generator-checklist.md │ 09-tag-cloud-generator-rubric.md │ └───10-tag-cloud-generator-jcf ├───.vscode │ settings.json │ ├───data │ alice.txt │ doriangray.txt │ importance.txt │ lesmiz.txt │ tomsawyer.txt │ └───doc 10-tag-cloud-generator-jcf-checklist.md 10-tag-cloud-generator-jcf-rubric.md
Plugins Are Now Extensions
In Eclipse, there is a somewhat messy plugin marketplace, where you can add features like we do for our classes. For instance, we use a plugin for checking the style of the Java code—literally called CheckStyle. We use a similar linting tool for spotting bugs—literally called SpotBugs. In addition, there are other odds and ends like a plugin for subversion, a version control tool.
When migrating to VS Code, the plugins are not going to carry over, so you need to find suitable replacements through the extensions marketplace. However, once you find the extension you want, I find they’re much easier to integrate into a student’s VS Code than in Eclipse. For context, we have a literal document that tells students what to type into the marketplace, so they can download the appropriate plugins in Eclipse. With VS Code, it is much, much easier.
To start, create an extensions.json
file in the root .vscode
folder. If you’re not sure what I mean, take a peek above at the directory layout. At any rate, the file is pretty straightforward. Inside, you’ll find something that looks like this:
{ "recommendations": [ "yzane.markdown-pdf", // Markdown to PDF extension "vscjava.vscode-java-pack", // General Java startup extension pack "pdconsec.vscode-print", // Code printing to PDF extension "therenegadecoder.bugslang", // BugsLang syntax highlighting extension "shengchen.vscode-checkstyle" // Java CheckStyle extension ], }
Basically, there is a key in the JSON for extension recommendations. Here, you’ll list the ID of each extension you want in an array. I also included comments, which are typically not allowed in JSON, but VS Code lets you do it.
When the students open the directory for the first time, they will be prompted to install all of the recommended extensions. And even if they miss this message, they can still install everything by opening the command palette to search for “Recommended Extensions”.
Odds and Ends
With the projects able to be run and tested and all the appropriate extensions installed, VS Code is basically ready to go as an Eclipse replacement. That said, there are a variety of small things you can still do to improve the experience of the students.
For example, we ask students to “print” their code as a PDF, so when it’s uploaded to our learning management system, we can comment on the PDFs directly. To do that, you can recommend the “pdconsec.vscode-print” extension, which adds a little print button to the top right corner of the VS Code window. Further, you can customize the exact theme of the output to your liking (regardless of the theme students use) through the settings.json
file in the root .vscode
folder as follows:
{ "print.colourScheme": "A11 Y", }
Also, I make heavy use of markdown, so I always recommend the “yzane.markdown-pdf” extension, so students can render their markdown files for grading. Similarly, this extension can be configured as follows:
{ "markdown-pdf.convertOnSave": true, "markdown-pdf.format": "Letter", "markdown-pdf.outputDirectory": "", "markdown-pdf.highlightStyle": "a11y-light.css", }
In general, most of what you’ll need to do to get things working has already been listed. However, I will warn that this current solution isn’t ideal, at least to me. It’s somewhat brittle from a build standpoint, and folks should probably make use of managed projects. That said, I’m not ready to tackle that problem just yet!
Summary
In general, if you want to make the jump to VS Code from Eclipse, you must only move over your source code and delete any sort of build configurations—at least for unmanaged projects. Then, you’ll have to configure VS Code to your liking through the workspace settings.
If you’re not trying to migrate over a bunch of projects (like I was), then here’s what a single project repo might look like:
│ .gitattributes │ .gitignore │ LICENSE │ README.md │ ├───.vscode │ extensions.json │ checkstyle-config.xml │ formatter.xml │ settings.json │ ├───doc │ │ README.md │ │ │ ├───01-component-brainstorming │ │ 01-component-brainstorming.md │ │ │ ├───02-component-proof-of-concept │ │ 02-component-proof-of-concept.md │ │ │ ├───03-component-interfaces │ │ 03-component-interfaces.md │ │ │ ├───04-component-abstract-class │ │ 04-component-abstract-classes.md │ │ │ ├───05-component-kernel-implementation │ │ 05-component-kernel-implementation.md │ │ │ └───06-component-finishing-touches │ 06-component-finishing-touches.md │ ├───lib │ README.md │ ├───src │ README.md │ └───test README.md
And here is a similar template with some actual code in it:
│ .gitignore │ LICENSE │ README.md │ ├───.vscode │ extensions.json │ settings.json │ ├───bin │ └───components │ └───geometry │ └───point │ Point3D.class │ Point3D1.class │ Point3DKernel.class │ Point3DSecondary.class │ Point3DTest.class │ ├───lib │ components.jar │ hamcrest-core-1.3.jar │ junit-4.13.2.jar │ ├───src │ └───components │ └───geometry │ └───point │ Point3D.java │ Point3D1.java │ Point3DKernel.java │ Point3DSecondary.java │ └───test └───components └───geometry └───point Point3DTest.java
The structure is roughly the same, but there are no nested projects. This is probably the project setup you’re used to seeing in Eclipse, so I might recommend just copying over the source files into this format.
In any case, there’s not a ton of documentation on how to actually accomplish a seamless migration between Eclipse and VS Code, so take whatever I’ve shared with a grain of salt. This solution worked for me, but it may not work for you. In addition, I’m also testing these changes with my classes this semester, so I’ll probably follow up with some additional hurdles later.
At any rate, I need to call it a day! Hopefully this was helpful to you in some way. If so, consider sticking around a bit longer:
- Java Has A Remainder Operator—Not a Mod Operator
- Flexible Interfaces With Optional Methods Are Good: A Java List Case Study
- Why Does == Sometimes Work on Strings in Java?
Also, you might consider taking some time to learn about ways to grow the site. Otherwise, take care!
Recent Posts
Recently, I was thinking about the old Pavlov's dog story and how we hardly treat our students any different. While reflecting on this idea, I decided to write the whole thing up for others to read....
In the world of programming languages, expressions are an interesting concept that folks tend to implicitly understand but might not be able to define. As a result, I figured I'd take a crack at...