refactor(options): Define default options as an explicit instance. (#34)

This simplifies option parsing logic a bit, and sets us up to add a flag
to override the default options
(https://github.com/google/keep-sorted/pull/35).

I also tweaked most of the tests in keep_sorted_test to not rely on the
default options at all and instead be more explicit about the behavior
they need to pass.
diff --git a/keepsorted/block.go b/keepsorted/block.go
index 65e8716..c292c85 100644
--- a/keepsorted/block.go
+++ b/keepsorted/block.go
@@ -109,7 +109,7 @@
 				continue
 			}
 
-			opts, err := f.parseBlockOptions(start.line)
+			opts, err := f.parseBlockOptions(start.line, defaultOptions)
 			if err != nil {
 				// TODO(b/250608236): Is there a better way to surface this error?
 				log.Err(fmt.Errorf("keep-sorted block at index %d had bad start directive: %w", start.index+offset, err)).Msg("")
diff --git a/keepsorted/keep_sorted_test.go b/keepsorted/keep_sorted_test.go
index 94a89e2..307030f 100644
--- a/keepsorted/keep_sorted_test.go
+++ b/keepsorted/keep_sorted_test.go
@@ -23,31 +23,6 @@
 	"github.com/rs/zerolog/log"
 )
 
-var (
-	defaultOptions = blockOptions{
-		Lint:             true,
-		SkipLines:        0,
-		Group:            true,
-		GroupPrefixes:    nil,
-		IgnorePrefixes:   nil,
-		Numeric:          false,
-		StickyComments:   true,
-		StickyPrefixes:   map[string]bool{"//": true},
-		RemoveDuplicates: true,
-		PrefixOrder:      nil,
-		Block:            false,
-		NewlineSeparated: false,
-		CaseSensitive:    true,
-		commentMarker:    "//",
-	}
-
-	defaultMetadata = blockMetadata{
-		startDirective: "keep-sorted-test start",
-		endDirective:   "keep-sorted-test end",
-		opts:           defaultOptions,
-	}
-)
-
 // initZerolog initializes zerolog to log as part of the test.
 // It returns a function that restores zerolog to its state before this function was called.
 func initZerolog(t testing.TB) {
@@ -56,16 +31,26 @@
 	t.Cleanup(func() { log.Logger = oldLogger })
 }
 
-func defaultOptionsWith(f func(*blockOptions)) blockOptions {
+func defaultOptionsWithCommentMarker(marker string) blockOptions {
 	opts := defaultOptions
-	f(&opts)
+	opts.StickyComments = true
+	opts.setCommentMarker(marker)
+	return opts
+}
+
+func optionsWithCommentMarker(marker string) blockOptions {
+	var opts blockOptions
+	opts.StickyComments = true
+	opts.setCommentMarker(marker)
 	return opts
 }
 
 func defaultMetadataWith(opts blockOptions) blockMetadata {
-	meta := defaultMetadata
-	meta.opts = opts
-	return meta
+	return blockMetadata{
+		startDirective: "keep-sorted-test start",
+		endDirective:   "keep-sorted-test end",
+		opts:           opts,
+	}
 }
 
 func TestFix(t *testing.T) {
@@ -375,7 +360,7 @@
 
 			wantBlocks: []block{
 				{
-					metadata: defaultMetadata,
+					metadata: defaultMetadataWith(defaultOptionsWithCommentMarker("//")),
 					start:    3,
 					end:      7,
 					lines: []string{
@@ -385,7 +370,7 @@
 					},
 				},
 				{
-					metadata: defaultMetadata,
+					metadata: defaultMetadataWith(defaultOptionsWithCommentMarker("//")),
 					start:    9,
 					end:      13,
 					lines: []string{
@@ -412,7 +397,7 @@
 
 			wantBlocks: []block{
 				{
-					metadata: defaultMetadata,
+					metadata: defaultMetadataWith(defaultOptionsWithCommentMarker("//")),
 					start:    5,
 					end:      7,
 					lines: []string{
@@ -450,7 +435,7 @@
 
 			wantBlocks: []block{
 				{
-					metadata: defaultMetadata,
+					metadata: defaultMetadataWith(defaultOptionsWithCommentMarker("//")),
 					start:    3,
 					end:      7,
 					lines: []string{
@@ -478,7 +463,7 @@
 
 			wantBlocks: []block{
 				{
-					metadata: defaultMetadata,
+					metadata: defaultMetadataWith(defaultOptionsWithCommentMarker("//")),
 					start:    1,
 					end:      6,
 					lines: []string{
@@ -511,7 +496,7 @@
 
 			wantBlocks: []block{
 				{
-					metadata: defaultMetadata,
+					metadata: defaultMetadataWith(defaultOptionsWithCommentMarker("//")),
 					start:    1,
 					end:      13,
 					lines: []string{
@@ -529,7 +514,7 @@
 					},
 					nestedBlocks: []block{
 						{
-							metadata: defaultMetadata,
+							metadata: defaultMetadataWith(defaultOptionsWithCommentMarker("//")),
 							start:    5,
 							end:      9,
 							lines: []string{
@@ -589,7 +574,7 @@
 
 			wantBlocks: []block{
 				{
-					metadata: defaultMetadata,
+					metadata: defaultMetadataWith(defaultOptionsWithCommentMarker("//")),
 					start:    1,
 					end:      34,
 					lines: []string{
@@ -628,7 +613,7 @@
 					},
 					nestedBlocks: []block{
 						{
-							metadata: defaultMetadata,
+							metadata: defaultMetadataWith(defaultOptionsWithCommentMarker("//")),
 							start:    5,
 							end:      30,
 							lines: []string{
@@ -659,7 +644,7 @@
 							},
 							nestedBlocks: []block{
 								{
-									metadata: defaultMetadata,
+									metadata: defaultMetadataWith(defaultOptionsWithCommentMarker("//")),
 									start:    9,
 									end:      21,
 									lines: []string{
@@ -677,7 +662,7 @@
 									},
 									nestedBlocks: []block{
 										{
-											metadata: defaultMetadata,
+											metadata: defaultMetadataWith(defaultOptionsWithCommentMarker("//")),
 											start:    13,
 											end:      17,
 											lines: []string{
@@ -689,7 +674,7 @@
 									},
 								},
 								{
-									metadata: defaultMetadata,
+									metadata: defaultMetadataWith(defaultOptionsWithCommentMarker("//")),
 									start:    22,
 									end:      26,
 									lines: []string{
@@ -703,7 +688,7 @@
 					},
 				},
 				{
-					metadata: defaultMetadata,
+					metadata: defaultMetadataWith(defaultOptionsWithCommentMarker("//")),
 					start:    35,
 					end:      39,
 					lines: []string{
@@ -729,7 +714,7 @@
 
 			wantBlocks: []block{
 				{
-					metadata: defaultMetadata,
+					metadata: defaultMetadataWith(defaultOptionsWithCommentMarker("//")),
 					start:    5,
 					end:      7,
 					lines:    []string{"2"},
@@ -771,8 +756,7 @@
 		{
 			name: "NothingToSort",
 
-			opts: defaultOptions,
-			in:   []string{},
+			in: []string{},
 
 			want:              []string{},
 			wantAlreadySorted: true,
@@ -780,7 +764,6 @@
 		{
 			name: "AlreadySorted",
 
-			opts: defaultOptions,
 			in: []string{
 				"Bar",
 				"Baz",
@@ -799,7 +782,9 @@
 		{
 			name: "AlreadySorted_ExceptForDuplicate",
 
-			opts: defaultOptions,
+			opts: blockOptions{
+				RemoveDuplicates: true,
+			},
 			in: []string{
 				"Bar",
 				"Bar",
@@ -815,9 +800,9 @@
 		{
 			name: "AlreadySorted_NewlineSeparated",
 
-			opts: defaultOptionsWith(func(opts *blockOptions) {
-				opts.NewlineSeparated = true
-			}),
+			opts: blockOptions{
+				NewlineSeparated: true,
+			},
 			in: []string{
 				"Bar",
 				"",
@@ -838,9 +823,9 @@
 		{
 			name: "AlreadySorted_ExceptForNewlineSorted",
 
-			opts: defaultOptionsWith(func(opts *blockOptions) {
-				opts.NewlineSeparated = true
-			}),
+			opts: blockOptions{
+				NewlineSeparated: true,
+			},
 			in: []string{
 				"Bar",
 				"Baz",
@@ -859,7 +844,6 @@
 		{
 			name: "SimpleSorting",
 
-			opts: defaultOptions,
 			in: []string{
 				"Baz",
 				"Foo",
@@ -877,10 +861,7 @@
 		{
 			name: "CommentOnlyBlock",
 
-			opts: defaultOptionsWith(func(opts *blockOptions) {
-				opts.StickyComments = true
-				opts.StickyPrefixes = map[string]bool{"//": true}
-			}),
+			opts: optionsWithCommentMarker("//"),
 			in: []string{
 				"2",
 				"1",
@@ -896,9 +877,9 @@
 		{
 			name: "Prefix",
 
-			opts: defaultOptionsWith(func(opts *blockOptions) {
-				opts.PrefixOrder = []string{"INIT_", "", "FINAL_"}
-			}),
+			opts: blockOptions{
+				PrefixOrder: []string{"INIT_", "", "FINAL_"},
+			},
 			in: []string{
 				// keep-sorted start prefix_order=
 				"DO_SOMETHING_WITH_BAR",
@@ -937,11 +918,11 @@
 		{
 			name: "RemoveDuplicates_ConsidersComments",
 
-			opts: defaultOptionsWith(func(opts *blockOptions) {
+			opts: func() blockOptions {
+				opts := optionsWithCommentMarker("//")
 				opts.RemoveDuplicates = true
-				opts.StickyComments = true
-				opts.StickyPrefixes = map[string]bool{"//": true}
-			}),
+				return opts
+			}(),
 			in: []string{
 				"// comment 1",
 				"foo",
@@ -963,9 +944,9 @@
 		{
 			name: "RemoveDuplicates_IgnoresTraliningCommas",
 
-			opts: defaultOptionsWith(func(opts *blockOptions) {
-				opts.RemoveDuplicates = true
-			}),
+			opts: blockOptions{
+				RemoveDuplicates: true,
+			},
 			in: []string{
 				"foo,",
 				"bar,",
@@ -980,9 +961,9 @@
 		{
 			name: "RemoveDuplicates_IgnoresTrailingCommas_RemovesCommaIfLastElement",
 
-			opts: defaultOptionsWith(func(opts *blockOptions) {
-				opts.RemoveDuplicates = true
-			}),
+			opts: blockOptions{
+				RemoveDuplicates: true,
+			},
 			in: []string{
 				"foo,",
 				"foo,",
@@ -997,9 +978,9 @@
 		{
 			name: "RemoveDuplicates_IgnoresTrailingCommas_RemovesCommaIfOnlyElement",
 
-			opts: defaultOptionsWith(func(opts *blockOptions) {
-				opts.RemoveDuplicates = true
-			}),
+			opts: blockOptions{
+				RemoveDuplicates: true,
+			},
 			in: []string{
 				"foo,",
 				"foo",
@@ -1012,9 +993,9 @@
 		{
 			name: "RemoveDuplicates_Keep",
 
-			opts: defaultOptionsWith(func(opts *blockOptions) {
-				opts.RemoveDuplicates = false
-			}),
+			opts: blockOptions{
+				RemoveDuplicates: false,
+			},
 			in: []string{
 				"foo",
 				"foo",
@@ -1030,7 +1011,6 @@
 		{
 			name: "TrailingCommas",
 
-			opts: defaultOptions,
 			in: []string{
 				"foo,",
 				"baz,",
@@ -1046,9 +1026,9 @@
 		{
 			name: "IgnorePrefixes",
 
-			opts: defaultOptionsWith(func(opts *blockOptions) {
-				opts.IgnorePrefixes = []string{"fs.setBoolFlag", "fs.setIntFlag"}
-			}),
+			opts: blockOptions{
+				IgnorePrefixes: []string{"fs.setBoolFlag", "fs.setIntFlag"},
+			},
 			in: []string{
 				// keep-sorted start ignore_prefixes=
 				`fs.setBoolFlag("paws_with_cute_toebeans", true)`,
@@ -1066,9 +1046,9 @@
 		{
 			name: "CaseInsensitive",
 
-			opts: defaultOptionsWith(func(opts *blockOptions) {
-				opts.CaseSensitive = false
-			}),
+			opts: blockOptions{
+				CaseSensitive: false,
+			},
 			in: []string{
 				// keep-sorted start case=yes
 				"Bravo",
@@ -1086,9 +1066,9 @@
 		{
 			name: "Numeric",
 
-			opts: defaultOptionsWith(func(opts *blockOptions) {
-				opts.Numeric = true
-			}),
+			opts: blockOptions{
+				Numeric: true,
+			},
 			in: []string{
 				// keep-sorted start numeric=no
 				"PROGRESS_100_PERCENT",
@@ -1110,10 +1090,10 @@
 		{
 			name: "MultipleTransforms",
 
-			opts: defaultOptionsWith(func(opts *blockOptions) {
-				opts.IgnorePrefixes = []string{"R2D2", "C3PO", "R4"}
-				opts.Numeric = true
-			}),
+			opts: blockOptions{
+				IgnorePrefixes: []string{"R2D2", "C3PO", "R4"},
+				Numeric:        true,
+			},
 			in: []string{
 				// keep-sorted start ignore_prefixes= numeric=no
 				"C3PO_ARM_L",
@@ -1139,9 +1119,9 @@
 		{
 			name: "NewlineSeparated",
 
-			opts: defaultOptionsWith(func(opts *blockOptions) {
-				opts.NewlineSeparated = true
-			}),
+			opts: blockOptions{
+				NewlineSeparated: true,
+			},
 			in: []string{
 				"B",
 				"",
@@ -1160,9 +1140,9 @@
 		{
 			name: "NewlineSeparated_Empty",
 
-			opts: defaultOptionsWith(func(opts *blockOptions) {
-				opts.NewlineSeparated = true
-			}),
+			opts: blockOptions{
+				NewlineSeparated: true,
+			},
 			in: []string{},
 
 			want:              []string{},
@@ -1192,7 +1172,6 @@
 	}{
 		{
 			name: "Simple",
-			opts: defaultOptions,
 
 			want: []lineGroup{
 				{nil, []string{"foo"}},
@@ -1201,10 +1180,7 @@
 		},
 		{
 			name: "StickyComments",
-			opts: defaultOptionsWith(func(opts *blockOptions) {
-				opts.StickyComments = true
-				opts.StickyPrefixes = map[string]bool{"//": true}
-			}),
+			opts: optionsWithCommentMarker("//"),
 
 			want: []lineGroup{
 				{
@@ -1227,10 +1203,7 @@
 		},
 		{
 			name: "CommentOnlyGroup",
-			opts: defaultOptionsWith(func(opts *blockOptions) {
-				opts.StickyComments = true
-				opts.StickyPrefixes = map[string]bool{"//": true}
-			}),
+			opts: optionsWithCommentMarker("//"),
 
 			want: []lineGroup{
 				{
@@ -1251,10 +1224,9 @@
 		},
 		{
 			name: "Group",
-			opts: defaultOptionsWith(func(opts *blockOptions) {
-				opts.Group = true
-				opts.Block = false
-			}),
+			opts: blockOptions{
+				Group: true,
+			},
 
 			want: []lineGroup{
 				{nil, []string{
@@ -1268,10 +1240,10 @@
 		},
 		{
 			name: "Group_Prefixes",
-			opts: defaultOptionsWith(func(opts *blockOptions) {
-				opts.GroupPrefixes = map[string]bool{"and": true, "with": true}
-				opts.Block = false
-			}),
+			opts: blockOptions{
+				Group:         true,
+				GroupPrefixes: map[string]bool{"and": true, "with": true},
+			},
 
 			want: []lineGroup{
 				{nil, []string{
@@ -1295,10 +1267,9 @@
 		},
 		{
 			name: "Group_UnindentedNewlines",
-			opts: defaultOptionsWith(func(opts *blockOptions) {
-				opts.Group = true
-				opts.Block = false
-			}),
+			opts: blockOptions{
+				Group: true,
+			},
 
 			want: []lineGroup{
 				{nil, []string{
@@ -1319,10 +1290,11 @@
 		},
 		{
 			name: "Group_NestedKeepSortedBlocksWithoutAnyIndentation",
-			opts: defaultOptionsWith(func(opts *blockOptions) {
+			opts: func() blockOptions {
+				opts := optionsWithCommentMarker("//")
 				opts.Group = true
-				opts.Block = false
-			}),
+				return opts
+			}(),
 
 			want: []lineGroup{
 				{[]string{
@@ -1347,9 +1319,9 @@
 		},
 		{
 			name: "Block_Brackets",
-			opts: defaultOptionsWith(func(opts *blockOptions) {
-				opts.Block = true
-			}),
+			opts: blockOptions{
+				Block: true,
+			},
 
 			want: []lineGroup{
 				{nil, []string{
@@ -1368,9 +1340,9 @@
 		},
 		{
 			name: "Block_Quotes",
-			opts: defaultOptionsWith(func(opts *blockOptions) {
-				opts.Block = true
-			}),
+			opts: blockOptions{
+				Block: true,
+			},
 
 			want: []lineGroup{
 				{nil, []string{
@@ -1389,9 +1361,9 @@
 		},
 		{
 			name: "Block_EscapedQuote",
-			opts: defaultOptionsWith(func(opts *blockOptions) {
-				opts.Block = true
-			}),
+			opts: blockOptions{
+				Block: true,
+			},
 
 			want: []lineGroup{
 				{nil, []string{
@@ -1410,9 +1382,9 @@
 		},
 		{
 			name: "Block_IgnoresQuotesWithinQuotes",
-			opts: defaultOptionsWith(func(opts *blockOptions) {
-				opts.Block = true
-			}),
+			opts: blockOptions{
+				Block: true,
+			},
 
 			want: []lineGroup{
 				{nil, []string{
@@ -1431,9 +1403,9 @@
 		},
 		{
 			name: "Block_IgnoresBracesWithinQuotes",
-			opts: defaultOptionsWith(func(opts *blockOptions) {
-				opts.Block = true
-			}),
+			opts: blockOptions{
+				Block: true,
+			},
 
 			want: []lineGroup{
 				{nil, []string{
@@ -1452,10 +1424,11 @@
 		},
 		{
 			name: "Block_IgnoresSpecialCharactersWithinFullLineComments",
-			opts: defaultOptionsWith(func(opts *blockOptions) {
+			opts: func() blockOptions {
+				opts := optionsWithCommentMarker("//")
 				opts.Block = true
-				opts.StickyPrefixes["//"] = true
-			}),
+				return opts
+			}(),
 
 			want: []lineGroup{
 				{nil, []string{
@@ -1476,10 +1449,11 @@
 		},
 		{
 			name: "Block_IgnoresSpecialCharactersWithinTrailingComments",
-			opts: defaultOptionsWith(func(opts *blockOptions) {
+			opts: func() blockOptions {
+				opts := optionsWithCommentMarker("//")
 				opts.Block = true
-				opts.StickyPrefixes["//"] = true
-			}),
+				return opts
+			}(),
 
 			want: []lineGroup{
 				{nil, []string{
@@ -1502,9 +1476,9 @@
 		},
 		{
 			name: "Block_TripleQuotes",
-			opts: defaultOptionsWith(func(opts *blockOptions) {
-				opts.Block = true
-			}),
+			opts: blockOptions{
+				Block: true,
+			},
 
 			want: []lineGroup{
 				{nil, []string{
@@ -1534,106 +1508,127 @@
 
 func TestBlockOptions(t *testing.T) {
 	for _, tc := range []struct {
-		name string
-		in   string
+		name           string
+		in             string
+		defaultOptions blockOptions
 
 		want    blockOptions
 		wantErr string
 	}{
 		{
-			name: "DefaultOptions",
-			in:   "// keep-sorted-test",
+			name:           "DefaultOptions",
+			in:             "",
+			defaultOptions: defaultOptions,
 
 			want: defaultOptions,
 		},
 		{
-			name: "SimpleSwitch",
-			in:   "// keep-sorted-test lint=no",
+			name: "CommentMarker",
+			in:   "// keep-sorted-test start",
 
-			want: defaultOptionsWith(func(opts *blockOptions) {
-				opts.Lint = false
-			}),
+			want: blockOptions{
+				commentMarker: "//",
+			},
+		},
+		{
+			name: "StickyComments",
+			in:   "// keep-sorted-test start sticky_comments=yes",
+
+			want: blockOptions{
+				StickyComments: true,
+				StickyPrefixes: map[string]bool{"//": true},
+				commentMarker:  "//",
+			},
+		},
+		{
+			name: "SimpleSwitch",
+			in:   "lint=yes",
+
+			want: blockOptions{Lint: true},
 		},
 		{
 			name: "SkipLines",
-			in:   "// keep-sorted-test skip_lines=10",
+			in:   "skip_lines=10",
 
-			want: defaultOptionsWith(func(opts *blockOptions) {
-				opts.SkipLines = 10
-			}),
+			want: blockOptions{SkipLines: 10},
 		},
 		{
 			name: "ErrorSkipLinesIsNegative",
-			in:   "// keep-sorted-test skip_lines=-1",
+			in:   "skip_lines=-1",
 
-			want:    defaultOptions,
-			wantErr: `skip_lines has invalid value: -1`,
+			wantErr: "skip_lines has invalid value: -1",
 		},
 		{
 			name: "ItemList",
-			in:   "// keep-sorted-test prefix_order=a,b,c,d",
+			in:   "prefix_order=a,b,c,d",
 
-			want: defaultOptionsWith(func(opts *blockOptions) {
-				opts.PrefixOrder = []string{"a", "b", "c", "d"}
-			}),
+			want: blockOptions{
+				PrefixOrder: []string{"a", "b", "c", "d"},
+			},
 		},
 		{
 			name: "ItemSet",
-			in:   "keep-sorted-test sticky_prefixes=a,b,c,d",
+			in:   "sticky_prefixes=a,b,c,d",
 
-			want: defaultOptionsWith(func(opts *blockOptions) {
-				opts.StickyPrefixes = map[string]bool{"a": true, "b": true, "c": true, "d": true}
-				opts.commentMarker = ""
-			}),
+			want: blockOptions{
+				StickyPrefixes: map[string]bool{
+					"a": true,
+					"b": true,
+					"c": true,
+					"d": true,
+				},
+			},
 		},
 		{
 			name: "ignore_prefixes",
-			in:   "// keep-sorted-test ignore_prefixes=a,b,c,d",
+			in:   "ignore_prefixes=a,b,c,d",
 
-			want: defaultOptionsWith(func(opts *blockOptions) {
-				opts.IgnorePrefixes = []string{"a", "b", "c", "d"}
-			}),
+			want: blockOptions{
+				IgnorePrefixes: []string{"a", "b", "c", "d"},
+			},
+		},
+		{
+			name: "ignore_prefixes_ChecksLongestPrefixesFirst",
+			in:   "ignore_prefixes=DoSomething(,DoSomething({",
+
+			want: blockOptions{
+				IgnorePrefixes: []string{"DoSomething({", "DoSomething("},
+			},
 		},
 		{
 			name: "GroupPrefixesRequiresGrouping",
-			in:   "// keep-sorted-test group_prefixes=a,b,c group=no",
+			in:   "group_prefixes=a,b,c group=no",
 
-			want: defaultOptionsWith(func(opts *blockOptions) {
-				opts.Group = false
-			}),
 			wantErr: "group_prefixes may not be used with group=no",
 		},
 		{
-			name: "ignore_prefixes_ChecksLognestPrefixesFirst",
-			in:   "// keep-sorted-test ignore_prefixes=DoSomething(,DoSomething({",
-
-			want: defaultOptionsWith(func(opts *blockOptions) {
-				opts.IgnorePrefixes = []string{"DoSomething({", "DoSomething("}
-			}),
-		},
-		{
 			name: "OptionInTrailingComment",
-			in:   "// keep-sorted-test block=yes  # lint=no",
+			in:   "# keep-sorted-test start block=yes  # lint=yes",
 
-			want: defaultOptionsWith(func(opts *blockOptions) {
-				opts.Block = true
-				opts.Lint = false
-			}),
+			want: blockOptions{
+				Block:         true,
+				Lint:          true,
+				commentMarker: "#",
+			},
 		},
 		{
 			name: "ErrorDoesNotStopParsing",
-			in:   "// keep-sorted-test lint=yep case=no",
+			in:   "lint=nah case=no",
+			defaultOptions: blockOptions{
+				Lint:          true,
+				CaseSensitive: true,
+			},
 
-			want: defaultOptionsWith(func(opts *blockOptions) {
-				opts.Lint = true // The default value should not change.
-				opts.CaseSensitive = false
-			}),
-			wantErr: `option "lint" has unknown value "yep"`,
+			want: blockOptions{
+				Lint:          true, // The default value should not change.
+				CaseSensitive: false,
+			},
+			wantErr: `option "lint" has unknown value "nah"`,
 		},
 	} {
 		t.Run(tc.name, func(t *testing.T) {
 			initZerolog(t)
-			got, err := New("keep-sorted-test").parseBlockOptions(tc.in)
+			got, err := New("keep-sorted-test").parseBlockOptions(tc.in, tc.defaultOptions)
 			if err != nil {
 				if tc.wantErr == "" {
 					t.Errorf("parseBlockOptions(%q) = %v", tc.in, err)
diff --git a/keepsorted/options.go b/keepsorted/options.go
index fd9ea18..1e5a72c 100644
--- a/keepsorted/options.go
+++ b/keepsorted/options.go
@@ -45,7 +45,7 @@
 //  4. int:             key=123
 type blockOptions struct {
 	// Lint determines whether we emit lint warnings for this block.
-	Lint bool `default:"true"`
+	Lint bool
 
 	///////////////////////////
 	//  Pre-sorting options  //
@@ -54,13 +54,13 @@
 	// SkipLines is the number of lines to ignore before sorting.
 	SkipLines int `key:"skip_lines"`
 	// Group determines whether we group lines together based on increasing indentation.
-	Group bool `default:"true"`
+	Group bool
 	// GroupPrefixes tells us about other types of lines that should be added to a group.
 	GroupPrefixes map[string]bool `key:"group_prefixes"`
 	// Block opts us into a more complicated algorithm to try and understand blocks of code.
-	Block bool `default:"false"`
+	Block bool
 	// StickyComments tells us to attach comments to the line immediately below them while sorting.
-	StickyComments bool `key:"sticky_comments" default:"true"`
+	StickyComments bool `key:"sticky_comments"`
 	// StickyPrefixes tells us about other types of lines that should behave as sticky comments.
 	StickyPrefixes map[string]bool `key:"sticky_prefixes"`
 
@@ -69,9 +69,9 @@
 	///////////////////////
 
 	// CaseSensitive is whether we're case sensitive while sorting.
-	CaseSensitive bool `key:"case" default:"true"`
+	CaseSensitive bool `key:"case"`
 	// Numeric indicates that the contents should be sorted like numbers.
-	Numeric bool `default:"false"`
+	Numeric bool
 	// PrefixOrder allows the user to explicitly order lines based on their matching prefix.
 	PrefixOrder []string `key:"prefix_order"`
 	// IgnorePrefixes is a slice of prefixes that we do not consider when sorting lines.
@@ -82,16 +82,28 @@
 	////////////////////////////
 
 	// NewlineSeparated indicates that the groups should be separated with newlines.
-	NewlineSeparated bool `key:"newline_separated" default:"false"`
+	NewlineSeparated bool `key:"newline_separated"`
 	// RemoveDuplicates determines whether we drop lines that are an exact duplicate.
-	RemoveDuplicates bool `key:"remove_duplicates" default:"true"`
+	RemoveDuplicates bool `key:"remove_duplicates"`
 
 	// Syntax used to start a comment for keep-sorted annotation, e.g. "//".
 	commentMarker string
 }
 
-func (f *Fixer) parseBlockOptions(startLine string) (blockOptions, error) {
-	ret := blockOptions{}
+var defaultOptions = blockOptions{
+	Lint:             true,
+	Group:            true,
+	StickyComments:   true,
+	StickyPrefixes:   nil, // Will be populated with the comment marker of the start directive.
+	CaseSensitive:    true,
+	RemoveDuplicates: true,
+	// If we ever add a default map or slice or other reference type to this
+	// struct, we'll want to make sure we're doing a deep copy in
+	// parseBlockOptions.
+}
+
+func (f *Fixer) parseBlockOptions(startLine string, defaults blockOptions) (blockOptions, error) {
+	ret := defaults
 	opts := reflect.ValueOf(&ret)
 	var errs error
 	for i := 0; i < opts.Elem().NumField(); i++ {
@@ -102,10 +114,13 @@
 
 		val, err := parseBlockOption(field, startLine)
 		if err != nil {
+			if errors.Is(err, errOptionNotSet) {
+				continue
+			}
 			errs = errors.Join(errs, err)
+		} else {
+			opts.Elem().Field(i).Set(val)
 		}
-
-		opts.Elem().Field(i).Set(val)
 	}
 
 	if ret.SkipLines < 0 {
@@ -119,13 +134,7 @@
 	}
 
 	if cm := f.guessCommentMarker(startLine); cm != "" {
-		ret.commentMarker = cm
-		if ret.StickyComments {
-			if ret.StickyPrefixes == nil {
-				ret.StickyPrefixes = make(map[string]bool)
-			}
-			ret.StickyPrefixes[cm] = true
-		}
+		ret.setCommentMarker(cm)
 	}
 	if len(ret.IgnorePrefixes) > 1 {
 		// Look at longer prefixes first, in case one of these prefixes is a prefix of another.
@@ -135,6 +144,8 @@
 	return ret, errs
 }
 
+var errOptionNotSet = errors.New("not set in start directive")
+
 func parseBlockOption(f reflect.StructField, startLine string) (reflect.Value, error) {
 	key := strings.ToLower(f.Name)
 	if k, ok := f.Tag.Lookup("key"); ok {
@@ -146,39 +157,27 @@
 		val := string(regex.ExpandString(nil, "${value}", startLine, m))
 		return parseValue(f, key, val)
 	}
-	return parseDefaultValue(f, key), nil
-}
-
-func parseDefaultValue(f reflect.StructField, key string) reflect.Value {
-	val, err := parseValueWithDefault(f, key, f.Tag.Get("default"), func() reflect.Value { return reflect.Zero(f.Type) })
-	if err != nil {
-		panic(fmt.Errorf("blockOptions field %s has invalid default %q: %w", f.Name, f.Tag.Get("default"), err))
-	}
-	return val
+	return reflect.Zero(f.Type), fmt.Errorf("option %q: %w", key, errOptionNotSet)
 }
 
 func parseValue(f reflect.StructField, key, val string) (reflect.Value, error) {
-	return parseValueWithDefault(f, key, val, func() reflect.Value { return parseDefaultValue(f, key) })
-}
-
-func parseValueWithDefault(f reflect.StructField, key, val string, defaultFn func() reflect.Value) (reflect.Value, error) {
 	switch f.Type {
 	case reflect.TypeOf(true):
 		b, ok := boolValues[val]
 		if !ok {
-			return defaultFn(), fmt.Errorf("option %q has unknown value %q", key, val)
+			return reflect.Zero(f.Type), fmt.Errorf("option %q has unknown value %q", key, val)
 		}
 
 		return reflect.ValueOf(b), nil
 	case reflect.TypeOf([]string{}):
 		if val == "" {
-			return defaultFn(), nil
+			return reflect.Zero(f.Type), nil
 		}
 
 		return reflect.ValueOf(strings.Split(val, ",")), nil
 	case reflect.TypeOf(map[string]bool{}):
 		if val == "" {
-			return defaultFn(), nil
+			return reflect.Zero(f.Type), nil
 		}
 
 		sp := strings.Split(val, ",")
@@ -189,12 +188,12 @@
 		return reflect.ValueOf(m), nil
 	case reflect.TypeOf(0):
 		if val == "" {
-			return defaultFn(), nil
+			return reflect.Zero(f.Type), nil
 		}
 
 		i, err := strconv.Atoi(val)
 		if err != nil {
-			return defaultFn(), fmt.Errorf("option %q has invalid value %q: %w", key, val, err)
+			return reflect.Zero(f.Type), fmt.Errorf("option %q has invalid value %q: %w", key, val, err)
 		}
 		return reflect.ValueOf(i), nil
 	}
@@ -220,6 +219,16 @@
 	return ""
 }
 
+func (opts *blockOptions) setCommentMarker(marker string) {
+	opts.commentMarker = marker
+	if opts.StickyComments {
+		if opts.StickyPrefixes == nil {
+			opts.StickyPrefixes = make(map[string]bool)
+		}
+		opts.StickyPrefixes[marker] = true
+	}
+}
+
 // hasPrefix determines if s has one of the prefixes.
 func hasPrefix(s string, prefixes map[string]bool) bool {
 	if len(prefixes) == 0 {