@@ -20,7 +20,9 @@ local translations = df.language_translation.get_vector()
2020-- target selection
2121--
2222
23- local entity_names = {
23+ --- @alias target_type { name : df.language_name , sync_names : (df.language_name | fun (): nil )[]?, civ_id : df.historical_entity.id ? }
24+
25+ local entity_names = { --- @type table<df.language_name_type , df.entity_name_type>
2426 [df .language_name_type .Figure ]= df .entity_name_type .OTHER ,
2527 [df .language_name_type .FigureFirstOnly ]= df .entity_name_type .OTHER ,
2628 [df .language_name_type .FigureNoFirst ]= df .entity_name_type .OTHER ,
@@ -33,7 +35,7 @@ local entity_names = {
3335 [df .language_name_type .Hospital ]= df .entity_name_type .HOSPITAL ,
3436}
3537
36- local category_names = {
38+ local category_names = { --- @type table<df.language_name_type , df.language_name_category>
3739 [df .language_name_type .World ]= df .language_name_category .Region ,
3840 [df .language_name_type .Region ]= df .language_name_category .Region ,
3941 [df .language_name_type .LegendaryFigure ]= df .language_name_category .Unit ,
@@ -50,6 +52,9 @@ local category_names = {
5052
5153local wt = language .word_table
5254
55+ --- @param name_type df.language_name_type
56+ --- @param civ df.historical_entity
57+ --- @return df.language_word_table major , df.language_word_table minor
5358local function get_word_selectors (name_type , civ )
5459 -- special cases
5560 if name_type == df .language_name_type .Artifact then
@@ -78,15 +83,19 @@ local function get_word_selectors(name_type, civ)
7883 return wt [0 ][df .language_name_category .River ], wt [1 ][df .language_name_category .River ]
7984end
8085
86+ --- @param item df.item
87+ --- @return target_type ?
8188local function get_artifact_target (item )
8289 if not item or not item .flags .artifact then return end
83- local gref = dfhack .items .getGeneralRef (item , df .general_ref_type .IS_ARTIFACT )
90+ local gref = dfhack .items .getGeneralRef (item , df .general_ref_type .IS_ARTIFACT ) --- @cast gref df.general_ref_is_artifactst ?
8491 if not gref then return end
8592 local rec = df .artifact_record .find (gref .artifact_id )
8693 if not rec then return end
8794 return {name = rec .name }
8895end
8996
97+ --- @param hf df.historical_figure
98+ --- @return target_type ?
9099local function get_hf_target (hf )
91100 if not hf then return end
92101 local name = dfhack .units .getVisibleName (hf )
@@ -101,6 +110,8 @@ local function get_hf_target(hf)
101110 return {name = name , sync_names = sync_names , civ_id = hf .civ_id }
102111end
103112
113+ --- @param unit df.unit
114+ --- @return target_type ?
104115local function get_unit_target (unit )
105116 if not unit then return end
106117 local hf = df .historical_figure .find (unit .hist_figure_id )
@@ -111,6 +122,8 @@ local function get_unit_target(unit)
111122 return {name = dfhack .units .getVisibleName (unit ), civ_id = unit .civ_id }
112123end
113124
125+ --- @param entity df.historical_entity ?
126+ --- @return df.historical_entity.id ?
114127local function get_civ_id_from_entity (entity )
115128 if not entity then return end
116129 if entity .type == df .historical_entity_type .Civilization then return entity .id end
@@ -124,6 +137,8 @@ local function get_civ_id_from_entity(entity)
124137 end
125138end
126139
140+ --- @param site df.world_site ?
141+ --- @return df.historical_entity.id ?
127142local function get_civ_id_from_site (site )
128143 if not site then return end
129144 for _ ,he_link in ipairs (site .entity_links ) do
@@ -136,27 +151,38 @@ local function get_civ_id_from_site(site)
136151 end
137152end
138153
154+ --- @param entity df.historical_entity
155+ --- @return target_type ?
139156local function get_entity_target (entity )
140157 if not entity then return end
141158 return {name = entity .name , civ_id = get_civ_id_from_entity (entity )}
142159end
143160
161+ --- @param site df.world_site
162+ --- @return target_type ?
144163local function get_site_target (site )
145164 if not site then return end
146165 return {name = site .name , civ_id = get_civ_id_from_site (site )}
147166end
148167
168+ --- @param site df.world_site
169+ --- @param loc_id integer index into site.buildings
170+ --- @return target_type ?
149171local function get_location_target (site , loc_id )
150172 if not site or loc_id < 0 then return end
151173 local loc = utils .binsearch (site .buildings , loc_id , ' id' )
152174 if not loc then return end
153175 return {name = loc .name , civ_id = get_civ_id_from_site (site )}
154176end
155177
178+ --- @param fort df.historical_entity
179+ --- @param squad df.squad
180+ --- @return target_type ?
156181local function get_squad_target (fort , squad )
157182 return {name = squad .name , civ_id = get_civ_id_from_entity (fort )}
158183end
159184
185+ --- @return target_type ?
160186local function get_world_target ()
161187 local name = df .global .world .world_data .name
162188 local sync_names = {
@@ -185,12 +211,15 @@ local function select_artifact(cb)
185211 choices , function (_ , choice ) cb (choice .data .target ) end , nil , nil , true )
186212end
187213
214+ --- @param site df.world_site
188215local function select_location (site , cb )
189216 local choices = {}
190217 for _ ,loc in ipairs (site .buildings ) do
191- local desc , pen = sitemap .get_location_desc (loc )
218+ --- @diagnostic disable-next-line : need-check-nil
219+ local desc , pen = sitemap .get_location_desc (loc ) --- @type string , dfhack.colors | dfhack.pen
192220 table.insert (choices , {
193- text = {
221+ text = { -- note: the df.abstract_building base class doesn't have a name field, but all relevant subclasses do, so using the name field is ok.
222+ --- @diagnostic disable-next-line : undefined-field
194223 dfhack .translation .translateName (loc .name , true ),
195224 ' (' ,
196225 {text = desc , pen = pen },
@@ -203,16 +232,21 @@ local function select_location(site, cb)
203232 choices , function (_ , choice ) cb (choice .data .target ) end , nil , nil , true )
204233end
205234
235+ --- @param entity df.historical_entity
236+ --- @param cb fun ( target : target_type ?): nil
206237local function select_entity (entity , cb )
207238 cb (get_entity_target (entity ))
208239end
209240
241+ --- @param cb fun ( target : target_type ?): nil
210242local function select_site (site , cb )
211243 cb (get_site_target (site ))
212244end
213245
246+ --- @param fort df.historical_entity
247+ --- @param cb fun ( target : target_type ?): nil
214248local function select_squad (fort , cb )
215- local choices = {}
249+ local choices = {} --- @type { text : string , data :{ target : target_type ? } } []
216250 for _ ,squad_id in ipairs (fort .squads ) do
217251 local squad = df .squad .find (squad_id )
218252 if squad then
@@ -226,8 +260,9 @@ local function select_squad(fort, cb)
226260 choices , function (_ , choice ) cb (choice .data .target ) end , nil , nil , true )
227261end
228262
263+ --- @param cb fun ( target : target_type ?): nil
229264local function select_unit (cb )
230- local choices = {}
265+ local choices = {} --- @type { text : string , data :{ target : target_type ? } } []
231266 -- scan through units.all instead of units.active so we can choose starting dwarves on embark prep screen
232267 for _ ,unit in ipairs (df .global .world .units .all ) do
233268 if not dfhack .units .isActive (unit ) then goto continue end
@@ -244,6 +279,7 @@ local function select_unit(cb)
244279 nil , nil , true )
245280end
246281
282+ --- @param cb fun ( target : target_type ?): nil
247283local function select_world (cb )
248284 cb (get_world_target ())
249285end
@@ -293,7 +329,7 @@ Rename.ATTRS {
293329}
294330
295331local function get_language_options ()
296- local options , max_width = {}, 5
332+ local options , max_width = {}, 5 --- @type { label : string , value : integer , pen : dfhack.colors | dfhack.pen } [] , integer
297333 for idx , lang in ipairs (translations ) do
298334 max_width = math.max (max_width , # lang .name )
299335 table.insert (options , {label = dfhack .capitalizeStringWords (dfhack .lowerCp437 (lang .name )), value = idx , pen = COLOR_CYAN })
@@ -371,9 +407,10 @@ local function sort_by_part_of_speech_asc(a, b)
371407 return a_native < b_native
372408end
373409
410+ --- @param info { target : target_type , show_selector : boolean ? }
374411function Rename :init (info )
375412 self .target = info .target
376- self .cache = {}
413+ self .cache = {} --- @type table< df.language_name_component , { text : ( string | table ) [] | string , search_key : ( fun (): string ), data :{ idx : integer , english : string , native_fn :( fun (): string ), part_of_speech : df.part_of_speech } }[] >
377414
378415 local function normalize_name ()
379416 if self .target .name .type == df .language_name_type .NONE then
@@ -510,6 +547,7 @@ function Rename:init(info)
510547 enabled = function ()
511548 local _ , comp_choice = self .subviews .component_list :getSelected ()
512549 if comp_choice .data .is_first_name then return false end
550+ if comp_choice .data .val == df .language_name_component .NONE then return false end
513551 return self .target .name .words [comp_choice .data .val ] >= 0
514552 end ,
515553 },
@@ -605,10 +643,11 @@ function Rename:get_component_choices()
605643 },
606644 data = {val = df .language_name_component .TheX , is_first_name = true }})
607645 for val , comp in ipairs (df .language_name_component ) do
608- if val < 0 then goto continue end -- ignore language_name_component NONE element with value -1
646+ if val == df . language_name_component . NONE then goto continue end
609647 local text = {
610648 {text = comp :gsub (' (%l)(%u)' , ' %1 %2' )}, NEWLINE ,
611649 {gap = 2 , pen = COLOR_YELLOW , text = function ()
650+ if val == df .language_name_component .NONE then return ' ' end
612651 local word = self .target .name .words [val ]
613652 if word < 0 then return end
614653 return (' %s' ):format (language .words [word ].forms [self .target .name .parts_of_speech [val ]])
@@ -636,7 +675,7 @@ function Rename:get_component_action_choices()
636675
637676 local randomize_text = {{text = ' [' , pen = COLOR_RED }, ' Random' , {text = ' ]' , pen = COLOR_RED }}
638677 for val , comp in ipairs (df .language_name_component ) do
639- if val < 0 then goto continue end -- ignore language_name_component NONE element with value -1
678+ if val == df . language_name_component . NONE then goto continue end
640679 local randomize_fn = self :callback (' randomize_component_word' , comp )
641680 table.insert (choices , {text = randomize_text , data = {fn = randomize_fn }})
642681 local clear_text = {
@@ -653,6 +692,7 @@ function Rename:get_component_action_choices()
653692 return choices
654693end
655694
695+ --- @param comp df.language_name_component
656696function Rename :clear_component_word (comp )
657697 self .target .name .words [comp ] = - 1
658698 for _ , sync_name in ipairs (self .target .sync_names ) do
@@ -664,6 +704,7 @@ function Rename:clear_component_word(comp)
664704 end
665705end
666706
707+ --- @param word_idx integer
667708function Rename :set_first_name (word_idx )
668709 -- support giving names to previously unnamed units
669710 self .target .name .has_name = true
@@ -673,11 +714,15 @@ function Rename:set_first_name(word_idx)
673714 if type (sync_name ) == ' function' then
674715 sync_name ()
675716 else
717+ sync_name .has_name = true
676718 sync_name .first_name = self .target .name .first_name
677719 end
678720 end
679721end
680722
723+ --- @param component df.language_name_component
724+ --- @param word_idx integer
725+ --- @param part_of_speech df.part_of_speech
681726function Rename :set_component_word_by_data (component , word_idx , part_of_speech )
682727 self .target .name .words [component ] = word_idx
683728 self .target .name .parts_of_speech [component ] = part_of_speech
@@ -700,6 +745,8 @@ function Rename:set_component_word(_, choice)
700745 self :set_component_word_by_data (comp_choice .data .val , choice .data .idx , choice .data .part_of_speech )
701746end
702747
748+ --- @param val integer index into df.language_translation : get_vector ()
749+ --- @param prev_val integer index into df.language_translation : get_vector ()
703750function Rename :set_language (val , prev_val )
704751 self .target .name .language = val
705752 -- translate current first name into target language
@@ -721,6 +768,7 @@ function Rename:randomize_first_name()
721768 self :set_first_name (choices [math.random (# choices )].data .idx )
722769end
723770
771+ --- @param comp df.language_name_component
724772function Rename :randomize_component_word (comp )
725773 local choices = self :get_word_choices (df .language_name_component .TheX )
726774 local choice = choices [math.random (# choices )]
@@ -756,6 +804,10 @@ local part_of_speech_to_display = {
756804 [df .part_of_speech .VerbGerund ] = ' Present Participle' ,
757805}
758806
807+ --- @param comp df.language_name_component
808+ --- @param idx integer
809+ --- @param word df.language_word
810+ --- @param part_of_speech df.part_of_speech
759811function Rename :add_word_choice (choices , comp , idx , word , part_of_speech )
760812 local english = word .forms [part_of_speech ]
761813 if # english == 0 then return end
@@ -784,12 +836,13 @@ function Rename:add_word_choice(choices, comp, idx, word, part_of_speech)
784836 })
785837end
786838
839+ --- @param comp df.language_name_component
787840function Rename :get_word_choices (comp )
788841 if self .cache [comp ] then
789842 return self .cache [comp ]
790843 end
791844
792- local choices = {}
845+ local choices = {} --- @type { text : ( string | table ) [] | string , search_key : ( fun (): string ), data :{ idx : integer , english : string , native_fn :( fun (): string ), part_of_speech : df.part_of_speech }}
793846 for idx , word in ipairs (language .words ) do
794847 local flags = word .flags
795848 if comp == df .language_name_component .FrontCompound then
@@ -831,9 +884,11 @@ function Rename:get_word_choices(comp)
831884 end
832885
833886 self .cache [comp ] = choices
834- return choices
887+ return self . cache [ comp ]
835888end
836889
890+ --- @param sort_widget widgets.CycleHotkeyLabel ?
891+ --- @param sort_fn function ?
837892function Rename :refresh_list (sort_widget , sort_fn )
838893 local clist = self .subviews .component_list
839894 if not clist then return end
@@ -870,6 +925,7 @@ RenameScreen.ATTRS {
870925 focus_path = ' rename' ,
871926}
872927
928+ --- @param info { target : target_type , show_selector : boolean ? }
873929function RenameScreen :init (info )
874930 self :addviews {
875931 Rename {
@@ -924,7 +980,7 @@ UnitEmbarkRenameOverlay.ATTRS {
924980}
925981
926982local function get_selected_embark_unit ()
927- local scr = dfhack .gui .getDFViewscreen (true )
983+ local scr = dfhack .gui .getDFViewscreen (true ) --- @cast scr df.viewscreen_setupdwarfgamest
928984 return scr .s_unit [scr .selected_u ]
929985end
930986
@@ -984,6 +1040,7 @@ if not dfhack.isWorldLoaded() then
9841040 qerror (' This script requires a world to be loaded' )
9851041end
9861042
1043+ --- @return target_type ?
9871044local function get_target (opts )
9881045 local target
9891046 if opts .histfig_id then
@@ -1009,6 +1066,7 @@ local function get_target(opts)
10091066 if not squad then qerror (' Squad not found' ) end
10101067 target = squad .name
10111068 elseif opts .unit_id then
1069+ --- @diagnostic disable-next-line : param-type-mismatch
10121070 target = get_unit_target (df .unit .find (opts .unit_id ))
10131071 if not target then qerror (' Unit not found' ) end
10141072 elseif opts .world then
@@ -1048,6 +1106,7 @@ local function main(args)
10481106 return
10491107 end
10501108
1109+ --- @param target target_type
10511110 local function launch (target )
10521111 view = view and view :raise () or RenameScreen {
10531112 target = target ,
0 commit comments